Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/RecolectarDatos.activity/pippy_app.py
diff options
context:
space:
mode:
Diffstat (limited to 'RecolectarDatos.activity/pippy_app.py')
-rw-r--r--RecolectarDatos.activity/pippy_app.py540
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.'