diff options
Diffstat (limited to 'RecolectarDatos.activity/pippy_app.py')
-rw-r--r-- | RecolectarDatos.activity/pippy_app.py | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/RecolectarDatos.activity/pippy_app.py b/RecolectarDatos.activity/pippy_app.py new file mode 100644 index 0000000..950992f --- /dev/null +++ b/RecolectarDatos.activity/pippy_app.py @@ -0,0 +1,540 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2010 - Sebastian Silva <sebastian@somosazucar.org> +# Equipo de desarrollo de Sugar Labs Global. + +from sugar.datastore import datastore +from sugar import profile +import sys,csv,os,gtk,shutil +from time import localtime, strftime, time, ctime, altzone +from subprocess import Popen,PIPE,STDOUT +try: + from sqlite3 import dbapi2 as sqlite +except: + try: + from pysqlite2 import dbapi2 as sqlite + except: + print "Advertencia: Imposible encontrar bilbiotecas pysqlite." + +version = "13" + +###################################################### +# Aquí intentamos determinar la versión de Sugar +# el atributo "config.version" contendrá la versión +try: + # Ubicación estándar (Sugar 0.84+) + from jarabe import config + got_config = True +except: + got_config = False + +if not got_config: + try: + # OLPC XO build 802 + sys.path.insert(0, '/usr/share/sugar/shell') + import config + except: + # Builds viejos no tienen este dato (703) + class dummy_configuration: + def __init__(self): + self.version = "n/d" + config = dummy_configuration() +####################################################### + +def wait(time_lapse): + """ Implementa un "sleep timer" compatible con GTK """ + time_start = time() + time_end = (time_start + time_lapse) + + while time_end > time(): + while gtk.events_pending(): + gtk.main_iteration() + +def execute_cmd(cmd): + """ Ejecuta un comando de la terminal y retorna el + output del comando. + """ + p = Popen(cmd, shell=True, bufsize=0, + stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) + (child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) + + return child_stdout_and_stderr.read() + +def responseToDialog(entry, dialog, response): + dialog.response(response) +def getText(markup, markup2=""): + """ + Presenta un diálogo que pregunta algo al usuario. + Retorna lo que el usuario ingresó. + """ + dialog = gtk.MessageDialog( + None, + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + gtk.MESSAGE_QUESTION, + gtk.BUTTONS_OK, + None) + dialog.set_markup(markup) + entry = gtk.Entry() + #allow the user to press enter to do ok + entry.connect("activate", responseToDialog, dialog, gtk.RESPONSE_OK) + #create a horizontal box to pack the entry and a label + hbox = gtk.HBox() + hbox.pack_start(gtk.Label("Name:"), False, 5, 5) + hbox.pack_end(entry) + #some secondary text + dialog.format_secondary_markup(markup2) + #add it and show it + dialog.vbox.pack_end(hbox, True, True, 0) + dialog.show_all() + #go go go + dialog.run() + text = entry.get_text() + dialog.destroy() + return text + +def load_journal_table(): + """ + Carga la metadata del Diario de Sugar + Retorna una lista de objetos de datastore + """ + ds_mounts = datastore.mounts() + mountpoint_id = None + if len(ds_mounts) == 1 and ds_mounts[0]['id'] == 1: + query = { 'sorting':'timestamp' } + else: + # we're in sugar 0.82 + query = { 'order_by':'-timestamp' } + for mountpoint in ds_mounts: + id = mountpoint['id'] + uri = mountpoint['uri'] + if uri.startswith('/home'): + mountpoint_id = id + + if mountpoint_id is not None: + query['mountpoints'] = [ mountpoint_id ] + + ds_objects, num_objects = datastore.find( + query, properties=['title_set_by_user','activity', + 'title', 'mime_type', 'mtime', 'share-scope','uid', + 'keep', 'tags', 'description']) + + print "Listo! Ingrese los datos que se piden a continuación." + return ds_objects + +def get_media_mountpoint(): + """ + Retorna la URI del primer + dispositivo USB que encontremos + """ + ds_mounts = datastore.mounts() + mountpoint_id = None + if len(ds_mounts) == 1 and ds_mounts[0]['id'] == 1: + from gio import VolumeMonitor + vm = VolumeMonitor() + mounts = vm.get_mounts() + for mountpoint in mounts: + uri = mountpoint.get_root().get_path() + if uri.startswith('/media'): + return(uri+"/"), False + return "", False + else: + # we're in sugar 0.82 + for mountpoint in ds_mounts: + id = mountpoint['id'] + uri = mountpoint['uri'] + if uri.startswith('/media'): + return(uri+"/"), id + return "", True + +def _read_file(path): + """ + Esta función lee y retorna el contenido + de archivos de texto + """ + if os.access(path, os.R_OK) == 0: + return "n/d" + + fd = open(path, 'r') + value = fd.read() + fd.close() + if value: + value = value.strip('\n') + return value + else: + print "Error leyendo "+path + return None + +class Data_general: + """ + Obtenemos y almacenamos datos para registrar + """ + def __init__(self): + self.retrieved = False + self.events = {} + + def abrir_csv(self, file_suffix = ""): + """ + Crea un archivo CSV en el USB (si se encuentra) + retorna el objeto CSV para continuar la escritura. + Guarda en atributos del objeto los parametros + para no consultarlos nuevamente. + """ + if not self.retrieved: + self.retrieved = True + # Preparamos el encabezado + self.nick = profile.get_nick_name() + self.serial_no = _read_file('/ofw/serial-number') + self.build_no = str(_read_file('/boot/olpc_build')) + self.firmware_no = _read_file('/ofw/openprom/model') + self.sugar_no = config.version + self.escuela = str(getText("Porfavor indique el código modular de la IE")) + while len(self.escuela) != 7: + self.escuela = str(getText("Porfavor indique el <b>código modular</b> de IE", + "El código debe ser de 7 dígitos. Intente nuevamente.")) + self.alumno = str(getText("Porfavor ingrese el código de alumno")) + while len(self.alumno) != 3: + self.alumno = str(getText("Porfavor indique el <b>código de alumno</b>", + "El código debe ser de 3 dígitos. Intente nuevamente.")) + self.fecha = strftime("%Y-%m-%d %H:%M:%S", localtime()) + self.fecha_encuestador = str(getText("Porfavor indique la fecha de HOY (en formato DD/MM/AAAA)")) + self.hora_encuestador= str(getText("Porfavor indique la hora ACTUAL (en formato HH:MM AM/PM)")) + + self.media, self.mount_id = get_media_mountpoint() + if self.media=="": + self.usb_media = False + print "ADVERTENCIA: No se encontró una unidad USB!" + try: + self.media=os.environ['SUGAR_ACTIVITY_ROOT']+"/data/" + except: + pass + else: + self.usb_media = True + + filename = os.path.join(self.media, self.escuela+"_"+self.alumno+file_suffix+".csv") + if os.path.exists(filename): + filename = os.path.join(self.media, self.escuela+"_"+self.alumno+file_suffix+"-re.csv") + fd = open(filename,"wb") + writer = csv.writer(fd, dialect='excel') + + writer.writerow(['Usuario:', self.nick]) + writer.writerow(['Azucar:', self.sugar_no]) + writer.writerow(['Serie:', self.serial_no]) + writer.writerow(['Ensamble:', self.build_no]) + writer.writerow(['Firmware:', self.firmware_no]) + writer.writerow(['Escuela:', self.escuela]) + writer.writerow(['Alumno:', self.alumno]) + writer.writerow(['Fecha interna:', self.fecha]) + writer.writerow(['Fecha medicion:', self.fecha_encuestador]) + writer.writerow(['Hora medicion:', self.hora_encuestador]) + writer.writerow([]) + + return writer, filename, fd + + def write_csv_diario(self, objetos): + """ + Interroga al usuario + carga datos de la máquina XO / Sugar + guarda en un archivo CSV la info del diario. + """ + writer, filename, fd = self.abrir_csv("-diario") + + # Ordenamos los atributos + propiedades = {'activity': 'ACTIVIDAD', + 'title_set_by_user': 'TITULADO POR USUARIO', + 'title': 'TITULO', + 'mime_type': 'TIPO', + 'mtime': 'FECHA', + 'share-scope': 'COMPARTIDO', + 'keep': 'FAVORITO', + 'tags': 'ETIQUETAS', + 'description': 'DESCRIPCION'} + props = propiedades.keys() + props.sort() + row=[] + for p in props: + row.append(propiedades[p]) + writer.writerow(row) + + for obj in objetos: + metadata = obj.metadata.get_dictionary() + row = [] + for p in props: + try: + row.append(metadata[p]) + except: + row.append("") + writer.writerow(row) + obj.destroy() + + fd.close() + print "Archivo creado en "+filename + + def try_to_unmount(self): + if self.usb_media: + activity_bundle_file = self.media+"RecolectarDatos-"+version+".xo" + if os.path.exists(activity_bundle_file): + execute_cmd('touch '+activity_bundle_file) + print "Se actualizó la fecha de " + activity_bundle_file + execute_cmd('sync') + try: + if self.mount_id!=False: + datastore.unmount(self.mount_id) + output = execute_cmd('umount ' + self.media[:-1]) + if len(output)>1: + datastore.mount(self.media, "") + raise NameError('Imposible desmontar') + else: + from gio import VolumeMonitor + vm = VolumeMonitor() + mounts = vm.get_mounts() + for mountpoint in mounts: + uri = mountpoint.get_root().get_path() + if uri.startswith('/media'): + result = mountpoint.unmount(lambda x,y: None) + wait(3) + print "Se ha desmontado automaticamente la memoria USB." + except: + print "No olvide desmontar la memoria USB desde el Diario!" + + def event(self, hora, texto): + try: + if self.events[hora]: + self.events[hora+1] = [ctime(hora), texto ] + except KeyError: + self.events[hora] = [ctime(hora), texto ] + + def collect_logs(self): + """ + guarda en un archivo CSV la info de los logs de de los historiales. + """ + log_dir = os.environ['HOME']+"/.sugar/default/logs" + if not os.path.exists(log_dir): + log_dir = "/home/olpc/.sugar/default/logs" + if not os.path.exists(log_dir): + return None + + # Navegar recursivamente los directorios de logs + stack = [log_dir] + mtime = 0 + while stack: + directory = stack.pop() + for base in os.listdir(directory): + name = os.path.join(directory, base) + if os.path.isdir(name): + if not os.path.islink(name): + stack.append(name) + else: + mtime = os.path.getmtime(name) + # creation_time = os.path.getctime(name) + # nota: creation time no es correcto pq los logs han sido movidos a subcarpetas + this_dir = os.path.split(os.path.dirname(name))[1] + if this_dir == "logs": + this_dir = "" + continue + if base == "shellservice.log": + self.event(mtime, "LOG: *** Se inició nueva sesión de Sugar." ) + elif base == "shell.log": + pass + elif base == "presenceservice.log": + pass + elif base == "datastore.log": + pass + else: + self.event(mtime, "LOG: Ultima actividad en log de " + base[:-4]) + + # Ahora vamos a iterar por todos los historiales de Navegador / Wikipedia + isolation_dir = "/home/olpc/isolation/1/gid_to_data_dir" + if not os.path.exists(isolation_dir): + print "Imposible encontrar directorio isolation." + return None + stack = [isolation_dir] + while stack: + directory = stack.pop() + try: + subdirs = os.listdir(directory) + except: + subdirs = [] + for base in subdirs: + name = os.path.join(directory, base) + if os.path.isdir(name): + if not os.path.islink(name): + stack.append(name) + else: + if base == "places.sqlite": + shutil.copy (name, "/tmp") + tmpname = os.path.join("/tmp", base) + con = sqlite.connect(tmpname) + cur = con.cursor() + results = cur.execute ("select visit_date, url from moz_historyvisits,moz_places where place_id == moz_places.id") + for r in results: + item_timestamp = float(str(r[0])[:10]) + if r[1][:27] == 'http://localhost:8000/wiki/': + self.event(item_timestamp, "WEB: Articulo Wikipedia: " + r[1][27:]) + elif r[1][:31] == 'http://localhost:8000/search?q=': + self.event(item_timestamp, "WEB: Búsqueda en Wikipedia: " + r[1][31:]) + elif r[1][:29] == 'http://localhost:8000/static/': + self.event(item_timestamp, "WEB: Índice de Wikipedia.") + else: + self.event(item_timestamp, "WEB: Navega a: " + r[1]) + con.close() + os.unlink(tmpname) + + # Vamos a mirar por redes conocidas + nm_file = '/home/olpc/.sugar/default/nm/networks.cfg' + if not os.path.exists(nm_file): + print "Imposible encontrar info de Network Manager." + else: + try: + contents = _read_file(nm_file) + for line in contents.splitlines(): + if line[:1]=='[': + net = line[1:-1] + elif line[:9]=='timestamp': + timestamp = float(line[12:]) + self.event(timestamp, "RED: Se ha asociado a la red WIFI " + net) + except: + print "Advertencia: Imposible leer info de Network Manager." + + + return True + + def write_csv_logs(self): + if self.collect_logs(): + # Finalmente escupimos todo + writer, filename, fd = self.abrir_csv("-historial") + for ev in sorted(self.events): + writer.writerow(self.events[ev]) + + fd.close() + print "Archivo creado en "+filename + return True + else: + print "Ejecutando como actividad." + return False + + def install_logger(self): + #copiamos el ejecutable + logdir = "/home/olpc/.logger" + + if not os.path.exists(logdir): + execute_cmd("mkdir -p "+logdir) + + logprog = os.path.join(logdir, "logger.py") + shutil.copy (sys.argv[0], logprog) + + #lo hacemos autoiniciar + already_installed = False + xsession = "/home/olpc/.xsession" + if os.path.exists(xsession): + contents = _read_file(xsession) + for line in contents.splitlines(): + if line == "python /home/olpc/.logger/logger.py &": + already_installed = True + + if not already_installed: + execute_cmd("echo 'python /home/olpc/.logger/logger.py &' >> " + xsession) + + def update_logger(self): + if self.collect_logs(): + logdir = "/home/olpc/.logger" + logfile = "historial.csv" + + logpath = os.path.join(logdir, logfile) + + if not os.path.exists(logpath): + execute_cmd("mkdir -p "+logdir) + mtime = 0 + else: + lastlog = execute_cmd("tail -n1 "+logpath) + lastlog = lastlog.strip('\n') + try: + mtime = float(lastlog[-11:-1]) + except: + mtime = 0 + + size = os.path.getsize(logpath) + # poor mans logrotate + if size>262144: #limit 256k + if os.path.exists(logpath+"-3"): + execute_cmd("mv "+logpath+"-3 "+logpath+"-4") + if os.path.exists(logpath+"-2"): + execute_cmd("mv "+logpath+"-2 "+logpath+"-3") + if os.path.exists(logpath+"-1"): + execute_cmd("mv "+logpath+"-1 "+logpath+"-2") + execute_cmd("mv "+logpath+" "+logpath+"-1") + + s = os.statvfs(logdir) + free_disk_in_mb = (s.f_bsize * s.f_bavail)/(1024*1024) + + if free_disk_in_mb > 30: + fd = open(logpath,"a") + writer = csv.writer(fd, dialect='excel') + for ev in sorted(self.events): + if ev > mtime: + writer.writerow(self.events[ev]) + #print self.events[ev] + writer.writerow([ctime(time()), "LOGGER: Se actualizó registro de seguimiento *** " + str(int(time()))]) + #print ctime() + " updated " + str(int(time())) + #print ctime(mtime) + " :last log " + str(int(mtime)) + fd.close() + else: + print "Imposible cargar logs." + + +# Aquí comienza la ejecución +if os.path.split(sys.argv[0])[-1:][0]=='logger.py': + # Somos el sistema de seguimiento + dg = Data_general() + wait(30) + dg.update_logger() +elif os.path.split(sys.argv[0])[-1:][0]=='instalar.py': + print "Instalador." + dg = Data_general() + dg.install_logger() + dg.update_logger() + print "Se ha instalado el programa de seguimiento." +elif os.path.split(sys.argv[0])[-1:][0]=='monitor.py': + # Somos el instalador-recolector + print """Progama Recolectar Datos. +Copyright (c) 2010 - Sebastian Silva <sebastian@somosazucar.org> +Miembro de Equipo Sugar Labs Global. +-- +Inicio: Cargando los datos de los registros, la navegación y diario ahora! Porfavor espere... +""" + diario = load_journal_table() + dg = Data_general() + dg.write_csv_diario(diario) + message = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_NONE, u"¿Desea instalar Ud. la aplicacion de seguimiento de uso de la Laptop XO?") + message.add_button('SI', gtk.RESPONSE_YES) + message.add_button('No gracias', gtk.RESPONSE_NO) + resp = message.run() + if resp == gtk.RESPONSE_YES: + dg.install_logger() + dg.update_logger() + else: + print "Advertencia: NO se ha instalado el programa de seguimiento!" + if dg.write_csv_logs(): + dg.try_to_unmount() + md = gtk.MessageDialog(None, + gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, + gtk.BUTTONS_CLOSE, 'Ha ejecutado correctamente el programa python.\nQue tenga un buen día. :-)') + md.run() + md.destroy() + print 'Ha ejecutado correctamente el programa python.\nQue tenga un buen día. :-)' +else: + # Vamos a recolectar datos pero somos la actividad + print """Actividad Recolectar Datos. +Copyright (c) 2010 - Sebastian Silva <sebastian@somosazucar.org> +Miembro de Equipo Sugar Labs Global. +-- +Inicio: Cargando los datos del Diario ahora! Porfavor espere... +""" + diario = load_journal_table() + dg = Data_general() + dg.write_csv_diario(diario) + md = gtk.MessageDialog(None, + gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, + gtk.BUTTONS_CLOSE, 'Ha ejecutado correctamente Recolectar Datos.\nNo olvide ejecutar ahora el comando de python.') + md.run() + md.destroy() + print 'Ha ejecutado correctamente Recolectar Datos.\nNo olvide ejecutar ahora el comando de python.' |