class TransmisionApp(object): # Constantes de Estados DESCONECTADO = 0 CONECTADO = 1 AUTENTICADO = 2 MARGEN_LECTURAS_CON_COLISION = 2 MARGEN_LECTURAS_ERRONEAS = 2 COLOR_OK = '#006600' COLOR_ERR = '#FF0000' COLOR_ALERTA = '#FF0000' def __init__(self, hw_init=True): self.config = Configuracion() if self.config.USAR_LOCK == 'true': self.lock = thread.allocate_lock() else: self.lock = None logger.debug("__init__: lock = " + str(self.lock)) self._widget_tree = gtk.glade.XML(self.config.GLADE_FILE) self._wndPrincipal = self._widget_tree.get_widget('wndPrincipal') self._lblMensajePantalla = self._widget_tree.get_widget( 'lblMensajePantalla') self._vbox_acta = self._widget_tree.get_widget('vbox_acta') self._widget_vport = self._widget_tree.get_widget('vp_acta') self._status = self._widget_tree.get_widget('status') self._lbl_mesas = self._widget_tree.get_widget('lbl_mesas') self.__set_estado_conexion(self.DESCONECTADO) self.__modulo_lector = ModuloLector(self._lector_callback, False) self._conexion = None self._timeout_id = None eventos = { "on_wndPrincipal_delete": self.salir, "on_tlbSalir_clicked": self.salir, "on_mnuArchivoSalir_activate": self.salir, "on_mnuAyudaAcercaDe_activate": self.mostrar_acerca_de, "on_mnuAccionesRed_activate": self.configurar_red, "on_mnuAccionesImportar_activate": self.mostrar_importar_claves, "on_tblPreferencias_clicked": self.mostrar_preferencias, "on_tlbConectar_clicked": self.conectar, "on_tblConfigurarRed_clicked": self.configurar_red, "on_tblImportarCert_clicked": self.mostrar_importar_claves, "on_tlbBajarCert_clicked": self.mostrar_autenticacion, } self._widget_tree.signal_autoconnect(eventos) self.borradores = [] self.valid_tags = None self._actas = {} def mostrar_autenticacion(self, menuitem): autenticacion = Autenticacion(self.config, self) autenticacion.mostrar() def mostrar(self): if not DEBUG: self._wndPrincipal.maximize() self._wndPrincipal.show() def mostrar_acerca_de(self, menuitem): acerca_de = AcercaDe(self.config) acerca_de.mostrar() def mostrar_preferencias(self, menuitem): preferencias = Preferencias(self.config) preferencias.mostrar() def mostrar_importar_claves(self, menuitem): wnd_importar_claves = ImportarClaves(self.config) wnd_importar_claves.mostrar() def configurar_red(self, *args): try: subprocess.Popen(PATH_NETWORK_CONF_APP, shell=False) except OSError as e: logger.error(MSG_ERROR_CONF_APP % str(e.message)) @bloqueante def conectar(self, toolbutton): if self.__estado_conexion != self.DESCONECTADO: self.set_mensaje(MSG_CONECTADO) self.set_status(("conexion", ST_CONECTADO)) elif self._conexion is not None: self.set_mensaje(MSG_CONECTANDO) elif self.__modulo_lector is None: self.set_mensaje(MSG_LECTOR_DESCONECTADO % self.config.PUERTO_LECTOR) self.set_status(("lector", ST_NO_LECTOR)) else: self.set_mensaje(MSG_CONECTANDO_ESPERE) self._conexion = Conexion( '%s://%s/' % (PROTOCOLO, self.config.HOST), DEBUG, self.config.TIMEOUT) self._conexion.set_https_keys(self.config.KEY_FILE, self.config.CERT_FILE) self._conexion.set_ca_certificate(self.config.CA_FILE) thread.start_new_thread(self.__conectar, ()) self.__conectar_lector() return if self.lock: logger.debug("_conectar: libero lock manual!") self.lock.release() @desbloqueante def __conectar_lector(self): self.set_mensaje(MSG_AHORA_PASE_TARJETA, idle=True, color=self.COLOR_OK) self.__modulo_lector.conectar_lector() return @mostrar_espera @desbloqueante def __conectar(self): """Conectar al servidor web usando un thread en segundo plano""" logger.info("llamando a conectar") result = self._conexion.test_conexion() if result is not False and result.status_ok(): if self.config.VERIFICAR_DATOS: self._verificar_datos() self.valid_tags = [ b64decode(tag).upper() for tag in result._dict['tags'] ] self.__set_estado_conexion(self.CONECTADO) self.set_status(("conexion", ST_CONECTADO)) self.set_mensaje(MSG_AHORA_PASE_TARJETA, idle=True, color=self.COLOR_OK) gobject.idle_add(self._set_timeout) else: error = self._conexion.diagnosticar() if error is self._conexion.UNKNOW_ERROR: ayuda = "" elif error is self._conexion.CONNECTION_ERROR: ayuda = MSG_COMPRUEBE_CONEXION elif error is self._conexion.SSL_ERROR: ayuda = MSG_VERIFIQUE_CERTIFICADOS else: ayuda = "" self.set_mensaje(MSG_INTENTE_NUEVAMENTE % ayuda, idle=True, color=self.COLOR_ERR) self._conexion = None self.set_status(("conexion", ST_DESCONECTADO)) def _set_timeout(self): if self._timeout_id is not None: gobject.source_remove(self._timeout_id) self._timeout_id = gobject.timeout_add_seconds( PING_EVERY, self._conexion.test_conexion) return False @desbloqueante def __desconectar(self): """Cierra la conexión al servidor web usando un thread en segundo plano""" if self._timeout_id is not None: gobject.source_remove(self._timeout_id) logger.info("llamando a conectar") self._conexion.desconectar() self.set_status(("conexion", ST_DESCONECTADO)) def _verificar_datos(self): # @TODO: Verificar si los datos que posee el cliente son válidos # encontrar un método para verificar esto msg = '' destino = '/tmp/datos_actualizados' destino_tmp = '/tmp' version_datos = ConfiguracionSistema.one(codigo='db_version').valor logger.debug('Verificando datos locales: {}'.format(version_datos)) respuesta = self._conexion.obtener_datos_servidor(version_datos) logger.debug('Verificando datos remotos: {}'.format( respuesta['version'])) if len(respuesta['archivo']) > 0: actualizar_datos(respuesta['ubicacion'], respuesta['archivo'], destino, destino_tmp) # Borro la cache del objeto configuracion cache_name = ConfiguracionSistema.get_cache_name() if cache_name is not None and hasattr(ConfiguracionSistema.cache, cache_name): ConfiguracionSistema.cache.clear(cache_name) # Defino un nuevo origen de datos set_data_source(destino) else: msg = 'no ' logger.debug("Funcion: obtener datos del servidor, " "Los datos para la ubicación {} {}han sido " "actualizados".format(respuesta['ubicacion'], msg)) def esperando_evento(self, ocupado, idle=True): """ Setea el puntero de la ventana principal en ocupado (True) o flecha estándar (False). Es idle por defecto porque se lo llama casi exclusivamente desde el segundo hilo. """ if ocupado: cursor = gtk.gdk.Cursor(gtk.gdk.WATCH) else: cursor = None if idle: gobject.idle_add(self._wndPrincipal.window.set_cursor, cursor) else: self._wndPrincipal.window.set_cursor(cursor) def set_status(self, status): pass def set_mensaje(self, text, idle=False, color=None, alerta=''): """ Modifica el string de la pantalla. Acepta pango markup, ser llamado cuando gtk está idle y un color. Ver http://www.pygtk.org/docs/pygtk/pango-markup-language.html """ if color: text = MSG_BIG_SPAN_COLOR % (color, text) else: text = MSG_BIG_SPAN % text if alerta: text = MSG_BIG_SPAN_ALERT % (self.COLOR_ALERTA, alerta) + text if idle: gobject.idle_add(self._lblMensajePantalla.set_label, text) else: self._lblMensajePantalla.set_label(text) if multi_test: p = re.compile(r'<.*?>') logger.debug(">>> " + p.sub('', text)) def get_mensaje(self): """ Obtiene el string de la pantalla, sin el pango markup """ return self._lblMensajePantalla.get_text() def agregar_mensaje(self, text, idle=False): mensaje = self.get_mensaje() + '\n' + text if idle: gobject.idle_add(self._lblMensajePantalla.set_label, mensaje) else: self._lblMensajePantalla.set_label(mensaje) @mostrar_espera @bloqueante def _lector_callback(self, evento_tag, tag=None, datos_extra=None): """ Esta función es llamada cada vez que se detecta un cambio en el lector. """ token = tag.get("token") if token is not None: token = token.upper() if (tag is not None and tag != {} and len(self.valid_tags) > 0 and token not in self.valid_tags): self.set_mensaje(MSG_CHIP_NO_AUTORIZADO) else: if evento_tag == NO_TAG: logger.debug(">>> " + evento_tag) elif evento_tag == TAG_ERROR: logger.debug(evento_tag) self.set_mensaje(MSG_ERROR_LEER_CHIP) elif evento_tag == TAG_VACIO: tipo = tag['tipo'] if tipo: msg = MSG_CHIP_DE_VACIO % tipo else: msg = MSG_CHIP_VACIO self.set_mensaje(msg) elif evento_tag == TAG_DATOS or evento_tag == TAG_ADMIN: if (self.__estado_conexion == self.CONECTADO and tag['tipo'] == TAG_USUARIO_MSA): self.set_mensaje(MSG_AUTENTICANDO_USUARIO) thread.start_new_thread(self.__autenticar, (tag['datos'], )) # salgo para no desbloquear al final: return elif (self.__estado_conexion == self.AUTENTICADO and tag['clase'] == CLASE_ICODE2): if tag['tipo'] == TAG_RECUENTO: if tag['datos'] is None: self.set_mensaje(MSG_CONTENIDO_CHIP) else: self.set_mensaje(MSG_ENVIANDO_DATOS) if multi_test: # a veces esto se ejecutaba despues del # thread de enviar gobject.idle_add(self._elimimar_vista_acta) if not ACTA_DESGLOSADA: self.set_mensaje(MSG_ENVIANDO_DATOS) thread.start_new_thread( self.__enviar_recuento, (tag['datos'], )) else: self.set_mensaje(MSG_RESTO_ACTAS) thread.start_new_thread( self.agregar_acta, (tag['datos'], )) # salgo para no desbloquear al final: return elif tag['tipo'] in (TAG_USUARIO_MSA, TAG_PRESIDENTE_MESA): pass # Debería informalo? else: self.set_mensaje(MSG_CHIP_NO_TIPO_RECUENTO) else: if tag['clase'] == CLASE_ICODE2: self.set_mensaje(MSG_CHIP_NO_ICODE2 % tag['tipo']) elif tag['clase'] == CLASE_MIFARE: self.set_mensaje(MSG_CHIP_NO_RECUENTO) if self.lock: logger.debug("%s: libero lock manual!" % "_lector_callback") self.lock.release() @desbloqueante def agregar_acta(self, data): recuento = Recuento.desde_tag(data) data_mesa = None for mesa in self._estados_mesas: if mesa[0][2] == recuento.mesa.numero and mesa[0][1] \ not in (ESTADO_OK, ESTADO_PUBLICADA): data_mesa = mesa break if data_mesa is not None: dict_actas = self._actas.get(recuento.mesa.codigo) if dict_actas is None: self._actas[recuento.mesa.codigo] = {} dict_actas = self._actas.get(recuento.mesa.codigo) dict_actas[recuento.cod_categoria] = recuento recopiladas = [] for categoria in data_mesa[1:]: if categoria[0] == recuento.cod_categoria: categoria[3] = True gobject.idle_add(self._update_estados_mesas) recopiladas.append(categoria[3]) self._elimimar_vista_acta() self.set_mensaje(MSG_GENERANDO_IMG) imagen = recuento.a_imagen(svg=True, de_muestra=True, tipo=(CIERRE_TRANSMISION, recuento.cod_categoria)) path_destino = join( get_desktop_path(), "%s_%s.svg" % (recuento.mesa.numero, recuento.cod_categoria)) file_destino = open(path_destino, 'w') file_destino.write(imagen) file_destino.close() self._mostrar_acta(path_destino) self.set_mensaje(MSG_RESTO_ACTAS) if all(recopiladas): actas = dict_actas.values() recuento_ = Recuento(actas[0].mesa) campos_especiales = [ "votos_emitidos", "votos_impugnados", "votos_nulos", "votos_recurridos", "votos_observados", "cantidad_ciudadanos", "certificados_impresos" ] primer_acta = actas[0] for campo in campos_especiales: if hasattr(primer_acta, campo): setattr(recuento_, campo, getattr(primer_acta, campo)) for acta in actas: for key, value in acta._resultados.items(): if key[0] == acta.cod_categoria: recuento_._resultados[key] = value datos_tag = recuento_.a_tag() thread.start_new_thread(self.__enviar_recuento, (datos_tag, )) if multi_test: self._ultimo_recuento = datos_tag if len(self._vbox_acta.children()) == 1: botsi = BotonColor(MSG_BIG_SI, "#00cc00", "#ffffff") botno = BotonColor(MSG_BIG_NO, "#ff0000", "#000000") botsi.set_size_request(80, 70) botno.set_size_request(80, 70) botsi.connect("button-release-event", self._confirmar_transmision, datos_tag) botno.connect("button-release-event", self._cancelar_confirmacion) _hbox = gtk.HBox(False) _hbox.set_border_width(10) _hbox.pack_start(botno, padding=100) sep = gtk.VSeparator() sep.set_size_request(80, -1) _hbox.pack_start(sep, True, True) _hbox.pack_start(botsi, padding=100) self._vbox_acta.pack_end(_hbox, False, True) # Descargo y muestro el borrador nuevamente self._vbox_acta.show_all() #self.set_mensaje(MSG_BOLD % respuesta['mensaje'], # idle=True, color=self.COLOR_OK, # alerta=alerta) else: # la mesa no esta para transm self.set_mensaje(MSG_MESA_NO_HABILITADA) @mostrar_espera @desbloqueante def __enviar_recuento(self, datos_tag): """Envía el resultado al servidor web dentro de un thread en segundo plano.""" try: respuesta = self._conexion.enviar_recuento(datos_tag) if respuesta.status_ok(): alerta = '' if 'alerta' in respuesta: alerta = respuesta['alerta'] if 'acta_borrador' in respuesta: # Descargo y muestro el borrador if not ACTA_DESGLOSADA: #self._descargar_y_mostrar_acta(respuesta) self._generar_y_mostrar_acta(datos_tag) self.set_mensaje(MSG_BOLD % respuesta['mensaje'], idle=True, color=self.COLOR_OK, alerta=alerta) self.borradores.append(datos_tag) #if ACTA_DESGLOSADA: # self.__enviar_recuento(datos_tag) elif 'confirma_definitiva' in respuesta: self._generar_y_mostrar_acta(datos_tag) if len(self._vbox_acta.children()) < 2: # Muestro la botonera de confirmacion botsi = BotonColor(MSG_BIG_SI, "#00cc00", "#ffffff") botno = BotonColor(MSG_BIG_NO, "#ff0000", "#000000") botsi.set_size_request(80, 70) botno.set_size_request(80, 70) botsi.connect("button-release-event", self._confirmar_transmision, datos_tag) botno.connect("button-release-event", self._cancelar_confirmacion) _hbox = gtk.HBox(False) _hbox.set_border_width(10) _hbox.pack_start(botno, padding=100) sep = gtk.VSeparator() sep.set_size_request(80, -1) _hbox.pack_start(sep, True, True) _hbox.pack_start(botsi, padding=100) self._vbox_acta.pack_end(_hbox, False, True) # Descargo y muestro el borrador nuevamente #self._descargar_y_mostrar_acta(respuesta) self._vbox_acta.show_all() self.set_mensaje(MSG_BOLD % respuesta['mensaje'], idle=True, color=self.COLOR_OK, alerta=alerta) else: self.set_mensaje(MSG_BOLD % respuesta['mensaje'], idle=True, color=self.COLOR_OK, alerta=alerta) else: self.set_mensaje(MSG_ERROR_BOLD % respuesta['mensaje'], idle=True, color=self.COLOR_ERR) except Exception as e: logger.debug(str(e)) self.set_mensaje(MSG_ERROR_COMUNICACION, idle=True, color=self.COLOR_ERR) def _confirmar_transmision(self, w=None, ev=None, datos_tag=None): respuesta_conf = self._conexion.confirmar_acta(datos_tag) if respuesta_conf.status_ok() and 'acta_definitiva' in respuesta_conf: # Descargo y muestro el definitivo #self._descargar_y_guardar_acta(respuesta_conf) self.set_mensaje(MSG_BOLD % respuesta_conf['mensaje'], idle=True, color=self.COLOR_OK) self._quitar_mesa(respuesta_conf['mesa']) self._update_estados_mesas() if datos_tag in self.borradores: self.borradores.remove(datos_tag) else: self.set_mensaje(MSG_ERROR_BOLD % respuesta_conf['mensaje'], idle=True, color=self.COLOR_ERR) self._elimimar_vista_acta() def _cancelar_confirmacion(self, w=None, ev=None): self.set_mensaje(MSG_CONFIRMACION_CIERRE_CANCELADA, idle=True, color=self.COLOR_OK) gobject.idle_add(self._elimimar_vista_acta) @mostrar_espera @desbloqueante def __autenticar(self, datos): """ Autentica el usuario contra el servidor web dentro de un thread en segundo plano """ try: (usuario, clave) = datos.split(',') except ValueError: self.set_mensaje(MSG_TARJETA_INVALIDA, idle=True, color=self.COLOR_ERR) else: try: respuesta = self._conexion.autenticar(usuario, clave) if respuesta.status_ok(): self.__set_estado_conexion(self.AUTENTICADO) self.set_mensaje('%s' % respuesta['mensaje'], idle=True, color=self.COLOR_OK) # @TODO: Modificar la lista de estados desde el servidor # para capturar los estados de las mesas y mostrar en # distintos colores como la web de consulta estados = respuesta['estado_mesas'] for mesa in estados: for categoria in mesa[1:]: categoria.append(False) self._estados_mesas = estados self._update_estados_mesas() else: self.set_mensaje(MSG_ERROR_GENERICO % respuesta['mensaje'], idle=True, color=self.COLOR_ERR) except Exception as e: logger.debug(str(e)) self.set_mensaje(MSG_ERROR_COMUNICACION, idle=True, color=self.COLOR_ERR) def _quitar_mesa(self, id_mesa): nuevos_estados = [] for estado in self._estados_mesas: data_mesa = estado[0] if data_mesa[2].strip() != id_mesa[5:].strip(): nuevos_estados.append([data_mesa] + estado[1:]) self._estados_mesas = nuevos_estados def _update_estados_mesas(self): lbls_mesas = [] for mesa in self._estados_mesas: data_mesa = mesa[0] if data_mesa[1] not in (ESTADO_OK, ESTADO_PUBLICADA): actas = " ".join([ ("<b>%s</b>" % cargo[0]) if cargo[3] else cargo[0] for cargo in mesa[1:] ]) lbl = "Mesa %s%s%s%s" % \ (data_mesa[2], (data_mesa[3] if data_mesa[3] is not None else "X"), " - " if len(actas) else "", actas) lbls_mesas.append(lbl) lbl_pendientes = self._widget_tree.get_widget('label1') gobject.idle_add(lbl_pendientes.set_text, "Pendientes(%s):" % len(lbls_mesas)) gobject.idle_add(self._lbl_mesas.set_markup, "\n".join(lbls_mesas)) def _elimimar_vista_acta(self): for child in self._vbox_acta.get_children(): self._vbox_acta.remove(child) def _generar_y_mostrar_acta(self, datos_tag): self._elimimar_vista_acta() recuento = Recuento.desde_tag(datos_tag) self.set_mensaje(MSG_GENERANDO_IMG) imagen = recuento.a_imagen(svg=True, de_muestra=True, tipo=(CIERRE_TRANSMISION, recuento.cod_categoria)) path_destino = join( get_desktop_path(), "%s_%s.svg" % (recuento.mesa.numero, recuento.cod_categoria)) file_destino = open(path_destino, 'w') file_destino.write(imagen) file_destino.close() self._mostrar_acta(path_destino) self.set_mensaje(MSG_RESTO_ACTAS) def _descargar_y_mostrar_acta(self, respuesta): """ Descarga el acta del servidor y lo muestra al usuario """ if not ACTA_DESGLOSADA: path_destino = join(get_desktop_path(), respuesta['file_name']) url_acta = '%s://%s/%s' % (PROTOCOLO, self.config.HOST, respuesta['url']) self._conexion.descargar(url_acta, path_destino) self._mostrar_acta(path_destino) def _mostrar_acta(self, path_destino): imgviewer = image_viewer.SimpleImageViewer(path_destino) self._vbox_acta.pack_start(imgviewer, True, True) self._vbox_acta.show_all() self._vbox_acta.show() def _descargar_y_guardar_acta(self, respuesta): """ Descarga el acta del servidor y lo guarda al usuario """ path_destino = join(os.getenv('HOME'), respuesta['file_name']) self._conexion.descargar( '%s://%s/%s' % (PROTOCOLO, self.config.HOST, respuesta['url']), path_destino) self._elimimar_vista_acta() def __set_estado_conexion(self, estado_conexion): """ """ self.__estado_conexion = estado_conexion self.__tiempo_conexion = time.time() def salir(self, *args): if self.borradores: msg = MSG_ATENCION_NO_CONFIRMADAS % len(self.borradores) dialog = gtk.MessageDialog(self._wndPrincipal, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK_CANCEL, msg) dialog.connect('response', self._respuesta_salir) dialog.run() dialog.destroy() else: self.salir_definitivo() def _respuesta_salir(self, dialog, response, data=None): if response == gtk.RESPONSE_OK: self.salir_definitivo() def salir_definitivo(self): if self.__modulo_lector: self.__modulo_lector.desconectar_lector() gtk.main_quit()
class TransmisionApp(object): # Constantes de Estados DESCONECTADO = 0 CONECTADO = 1 AUTENTICADO = 2 MARGEN_LECTURAS_CON_COLISION = 2 MARGEN_LECTURAS_ERRONEAS = 2 COLOR_OK = "#006600" COLOR_ERR = "#FF0000" COLOR_ALERTA = "#FF0000" def __init__(self, hw_init=True): self.config = Configuracion() if self.config.USAR_LOCK == "true": self.lock = thread.allocate_lock() else: self.lock = None logger.debug("__init__: lock = " + str(self.lock)) self._widget_tree = gtk.glade.XML(self.config.GLADE_FILE) self._wndPrincipal = self._widget_tree.get_widget("wndPrincipal") self._lblMensajePantalla = self._widget_tree.get_widget("lblMensajePantalla") self._vbox_acta = self._widget_tree.get_widget("vbox_acta") self._widget_vport = self._widget_tree.get_widget("vp_acta") self._status = self._widget_tree.get_widget("status") self._lbl_mesas = self._widget_tree.get_widget("lbl_mesas") self.__set_estado_conexion(self.DESCONECTADO) self.__modulo_lector = ModuloLector(self._lector_callback, False) self._conexion = None self._timeout_id = None eventos = { "on_wndPrincipal_delete": self.salir, "on_tlbSalir_clicked": self.salir, "on_mnuArchivoSalir_activate": self.salir, "on_mnuAyudaAcercaDe_activate": self.mostrar_acerca_de, "on_mnuAccionesRed_activate": self.configurar_red, "on_mnuAccionesImportar_activate": self.mostrar_importar_claves, "on_tblPreferencias_clicked": self.mostrar_preferencias, "on_tlbConectar_clicked": self.conectar, "on_tblConfigurarRed_clicked": self.configurar_red, "on_tblImportarCert_clicked": self.mostrar_importar_claves, "on_tlbBajarCert_clicked": self.mostrar_autenticacion, } self._widget_tree.signal_autoconnect(eventos) self.borradores = [] self.valid_tags = None self._actas = {} def mostrar_autenticacion(self, menuitem): autenticacion = Autenticacion(self.config, self) autenticacion.mostrar() def mostrar(self): if not DEBUG: self._wndPrincipal.maximize() self._wndPrincipal.show() def mostrar_acerca_de(self, menuitem): acerca_de = AcercaDe(self.config) acerca_de.mostrar() def mostrar_preferencias(self, menuitem): preferencias = Preferencias(self.config) preferencias.mostrar() def mostrar_importar_claves(self, menuitem): wnd_importar_claves = ImportarClaves(self.config) wnd_importar_claves.mostrar() def configurar_red(self, *args): try: subprocess.Popen(PATH_NETWORK_CONF_APP, shell=False) except OSError as e: logger.error(MSG_ERROR_CONF_APP % str(e.message)) @bloqueante def conectar(self, toolbutton): if self.__estado_conexion != self.DESCONECTADO: self.set_mensaje(MSG_CONECTADO) self.set_status(("conexion", ST_CONECTADO)) elif self._conexion is not None: self.set_mensaje(MSG_CONECTANDO) elif self.__modulo_lector is None: self.set_mensaje(MSG_LECTOR_DESCONECTADO % self.config.PUERTO_LECTOR) self.set_status(("lector", ST_NO_LECTOR)) else: self.set_mensaje(MSG_CONECTANDO_ESPERE) self._conexion = Conexion("%s://%s/" % (PROTOCOLO, self.config.HOST), DEBUG, self.config.TIMEOUT) self._conexion.set_https_keys(self.config.KEY_FILE, self.config.CERT_FILE) self._conexion.set_ca_certificate(self.config.CA_FILE) thread.start_new_thread(self.__conectar, ()) self.__conectar_lector() return if self.lock: logger.debug("_conectar: libero lock manual!") self.lock.release() @desbloqueante def __conectar_lector(self): self.set_mensaje(MSG_AHORA_PASE_TARJETA, idle=True, color=self.COLOR_OK) self.__modulo_lector.conectar_lector() return @mostrar_espera @desbloqueante def __conectar(self): """Conectar al servidor web usando un thread en segundo plano""" logger.info("llamando a conectar") result = self._conexion.test_conexion() if result is not False and result.status_ok(): if self.config.VERIFICAR_DATOS: self._verificar_datos() self.valid_tags = [b64decode(tag).upper() for tag in result._dict["tags"]] self.__set_estado_conexion(self.CONECTADO) self.set_status(("conexion", ST_CONECTADO)) self.set_mensaje(MSG_AHORA_PASE_TARJETA, idle=True, color=self.COLOR_OK) gobject.idle_add(self._set_timeout) else: error = self._conexion.diagnosticar() if error is self._conexion.UNKNOW_ERROR: ayuda = "" elif error is self._conexion.CONNECTION_ERROR: ayuda = MSG_COMPRUEBE_CONEXION elif error is self._conexion.SSL_ERROR: ayuda = MSG_VERIFIQUE_CERTIFICADOS else: ayuda = "" self.set_mensaje(MSG_INTENTE_NUEVAMENTE % ayuda, idle=True, color=self.COLOR_ERR) self._conexion = None self.set_status(("conexion", ST_DESCONECTADO)) def _set_timeout(self): if self._timeout_id is not None: gobject.source_remove(self._timeout_id) self._timeout_id = gobject.timeout_add_seconds(PING_EVERY, self._conexion.test_conexion) return False @desbloqueante def __desconectar(self): """Cierra la conexión al servidor web usando un thread en segundo plano""" if self._timeout_id is not None: gobject.source_remove(self._timeout_id) logger.info("llamando a conectar") self._conexion.desconectar() self.set_status(("conexion", ST_DESCONECTADO)) def _verificar_datos(self): # @TODO: Verificar si los datos que posee el cliente son válidos # encontrar un método para verificar esto msg = "" destino = "/tmp/datos_actualizados" destino_tmp = "/tmp" version_datos = ConfiguracionSistema.one(codigo="db_version").valor logger.debug("Verificando datos locales: {}".format(version_datos)) respuesta = self._conexion.obtener_datos_servidor(version_datos) logger.debug("Verificando datos remotos: {}".format(respuesta["version"])) if len(respuesta["archivo"]) > 0: actualizar_datos(respuesta["ubicacion"], respuesta["archivo"], destino, destino_tmp) # Borro la cache del objeto configuracion cache_name = ConfiguracionSistema.get_cache_name() if cache_name is not None and hasattr(ConfiguracionSistema.cache, cache_name): ConfiguracionSistema.cache.clear(cache_name) # Defino un nuevo origen de datos set_data_source(destino) else: msg = "no " logger.debug( "Funcion: obtener datos del servidor, " "Los datos para la ubicación {} {}han sido " "actualizados".format(respuesta["ubicacion"], msg) ) def esperando_evento(self, ocupado, idle=True): """ Setea el puntero de la ventana principal en ocupado (True) o flecha estándar (False). Es idle por defecto porque se lo llama casi exclusivamente desde el segundo hilo. """ if ocupado: cursor = gtk.gdk.Cursor(gtk.gdk.WATCH) else: cursor = None if idle: gobject.idle_add(self._wndPrincipal.window.set_cursor, cursor) else: self._wndPrincipal.window.set_cursor(cursor) def set_status(self, status): pass def set_mensaje(self, text, idle=False, color=None, alerta=""): """ Modifica el string de la pantalla. Acepta pango markup, ser llamado cuando gtk está idle y un color. Ver http://www.pygtk.org/docs/pygtk/pango-markup-language.html """ if color: text = MSG_BIG_SPAN_COLOR % (color, text) else: text = MSG_BIG_SPAN % text if alerta: text = MSG_BIG_SPAN_ALERT % (self.COLOR_ALERTA, alerta) + text if idle: gobject.idle_add(self._lblMensajePantalla.set_label, text) else: self._lblMensajePantalla.set_label(text) if multi_test: p = re.compile(r"<.*?>") logger.debug(">>> " + p.sub("", text)) def get_mensaje(self): """ Obtiene el string de la pantalla, sin el pango markup """ return self._lblMensajePantalla.get_text() def agregar_mensaje(self, text, idle=False): mensaje = self.get_mensaje() + "\n" + text if idle: gobject.idle_add(self._lblMensajePantalla.set_label, mensaje) else: self._lblMensajePantalla.set_label(mensaje) @mostrar_espera @bloqueante def _lector_callback(self, evento_tag, tag=None, datos_extra=None): """ Esta función es llamada cada vez que se detecta un cambio en el lector. """ token = tag.get("token") if token is not None: token = token.upper() if tag is not None and tag != {} and len(self.valid_tags) > 0 and token not in self.valid_tags: self.set_mensaje(MSG_CHIP_NO_AUTORIZADO) else: if evento_tag == NO_TAG: logger.debug(">>> " + evento_tag) elif evento_tag == TAG_ERROR: logger.debug(evento_tag) self.set_mensaje(MSG_ERROR_LEER_CHIP) elif evento_tag == TAG_VACIO: tipo = tag["tipo"] if tipo: msg = MSG_CHIP_DE_VACIO % tipo else: msg = MSG_CHIP_VACIO self.set_mensaje(msg) elif evento_tag == TAG_DATOS or evento_tag == TAG_ADMIN: if self.__estado_conexion == self.CONECTADO and tag["tipo"] == TAG_USUARIO_MSA: self.set_mensaje(MSG_AUTENTICANDO_USUARIO) thread.start_new_thread(self.__autenticar, (tag["datos"],)) # salgo para no desbloquear al final: return elif self.__estado_conexion == self.AUTENTICADO and tag["clase"] == CLASE_ICODE2: if tag["tipo"] == TAG_RECUENTO: if tag["datos"] is None: self.set_mensaje(MSG_CONTENIDO_CHIP) else: self.set_mensaje(MSG_ENVIANDO_DATOS) if multi_test: # a veces esto se ejecutaba despues del # thread de enviar gobject.idle_add(self._elimimar_vista_acta) if not ACTA_DESGLOSADA: self.set_mensaje(MSG_ENVIANDO_DATOS) thread.start_new_thread(self.__enviar_recuento, (tag["datos"],)) else: self.set_mensaje(MSG_RESTO_ACTAS) thread.start_new_thread(self.agregar_acta, (tag["datos"],)) # salgo para no desbloquear al final: return elif tag["tipo"] in (TAG_USUARIO_MSA, TAG_PRESIDENTE_MESA): pass # Debería informalo? else: self.set_mensaje(MSG_CHIP_NO_TIPO_RECUENTO) else: if tag["clase"] == CLASE_ICODE2: self.set_mensaje(MSG_CHIP_NO_ICODE2 % tag["tipo"]) elif tag["clase"] == CLASE_MIFARE: self.set_mensaje(MSG_CHIP_NO_RECUENTO) if self.lock: logger.debug("%s: libero lock manual!" % "_lector_callback") self.lock.release() @desbloqueante def agregar_acta(self, data): recuento = Recuento.desde_tag(data) data_mesa = None for mesa in self._estados_mesas: if mesa[0][2] == recuento.mesa.numero and mesa[0][1] not in (ESTADO_OK, ESTADO_PUBLICADA): data_mesa = mesa break if data_mesa is not None: dict_actas = self._actas.get(recuento.mesa.codigo) if dict_actas is None: self._actas[recuento.mesa.codigo] = {} dict_actas = self._actas.get(recuento.mesa.codigo) dict_actas[recuento.cod_categoria] = recuento recopiladas = [] for categoria in data_mesa[1:]: if categoria[0] == recuento.cod_categoria: categoria[3] = True gobject.idle_add(self._update_estados_mesas) recopiladas.append(categoria[3]) self._elimimar_vista_acta() self.set_mensaje(MSG_GENERANDO_IMG) imagen = recuento.a_imagen(svg=True, de_muestra=True, tipo=(CIERRE_TRANSMISION, recuento.cod_categoria)) path_destino = join(get_desktop_path(), "%s_%s.svg" % (recuento.mesa.numero, recuento.cod_categoria)) file_destino = open(path_destino, "w") file_destino.write(imagen) file_destino.close() self._mostrar_acta(path_destino) self.set_mensaje(MSG_RESTO_ACTAS) if all(recopiladas): actas = dict_actas.values() recuento_ = Recuento(actas[0].mesa) campos_especiales = [ "votos_emitidos", "votos_impugnados", "votos_nulos", "votos_recurridos", "votos_observados", "cantidad_ciudadanos", "certificados_impresos", ] primer_acta = actas[0] for campo in campos_especiales: if hasattr(primer_acta, campo): setattr(recuento_, campo, getattr(primer_acta, campo)) for acta in actas: for key, value in acta._resultados.items(): if key[0] == acta.cod_categoria: recuento_._resultados[key] = value datos_tag = recuento_.a_tag() thread.start_new_thread(self.__enviar_recuento, (datos_tag,)) if multi_test: self._ultimo_recuento = datos_tag if len(self._vbox_acta.children()) == 1: botsi = BotonColor(MSG_BIG_SI, "#00cc00", "#ffffff") botno = BotonColor(MSG_BIG_NO, "#ff0000", "#000000") botsi.set_size_request(80, 70) botno.set_size_request(80, 70) botsi.connect("button-release-event", self._confirmar_transmision, datos_tag) botno.connect("button-release-event", self._cancelar_confirmacion) _hbox = gtk.HBox(False) _hbox.set_border_width(10) _hbox.pack_start(botno, padding=100) sep = gtk.VSeparator() sep.set_size_request(80, -1) _hbox.pack_start(sep, True, True) _hbox.pack_start(botsi, padding=100) self._vbox_acta.pack_end(_hbox, False, True) # Descargo y muestro el borrador nuevamente self._vbox_acta.show_all() # self.set_mensaje(MSG_BOLD % respuesta['mensaje'], # idle=True, color=self.COLOR_OK, # alerta=alerta) else: # la mesa no esta para transm self.set_mensaje(MSG_MESA_NO_HABILITADA) @mostrar_espera @desbloqueante def __enviar_recuento(self, datos_tag): """Envía el resultado al servidor web dentro de un thread en segundo plano.""" try: respuesta = self._conexion.enviar_recuento(datos_tag) if respuesta.status_ok(): alerta = "" if "alerta" in respuesta: alerta = respuesta["alerta"] if "acta_borrador" in respuesta: # Descargo y muestro el borrador if not ACTA_DESGLOSADA: # self._descargar_y_mostrar_acta(respuesta) self._generar_y_mostrar_acta(datos_tag) self.set_mensaje(MSG_BOLD % respuesta["mensaje"], idle=True, color=self.COLOR_OK, alerta=alerta) self.borradores.append(datos_tag) # if ACTA_DESGLOSADA: # self.__enviar_recuento(datos_tag) elif "confirma_definitiva" in respuesta: self._generar_y_mostrar_acta(datos_tag) if len(self._vbox_acta.children()) < 2: # Muestro la botonera de confirmacion botsi = BotonColor(MSG_BIG_SI, "#00cc00", "#ffffff") botno = BotonColor(MSG_BIG_NO, "#ff0000", "#000000") botsi.set_size_request(80, 70) botno.set_size_request(80, 70) botsi.connect("button-release-event", self._confirmar_transmision, datos_tag) botno.connect("button-release-event", self._cancelar_confirmacion) _hbox = gtk.HBox(False) _hbox.set_border_width(10) _hbox.pack_start(botno, padding=100) sep = gtk.VSeparator() sep.set_size_request(80, -1) _hbox.pack_start(sep, True, True) _hbox.pack_start(botsi, padding=100) self._vbox_acta.pack_end(_hbox, False, True) # Descargo y muestro el borrador nuevamente # self._descargar_y_mostrar_acta(respuesta) self._vbox_acta.show_all() self.set_mensaje(MSG_BOLD % respuesta["mensaje"], idle=True, color=self.COLOR_OK, alerta=alerta) else: self.set_mensaje(MSG_BOLD % respuesta["mensaje"], idle=True, color=self.COLOR_OK, alerta=alerta) else: self.set_mensaje(MSG_ERROR_BOLD % respuesta["mensaje"], idle=True, color=self.COLOR_ERR) except Exception as e: logger.debug(str(e)) self.set_mensaje(MSG_ERROR_COMUNICACION, idle=True, color=self.COLOR_ERR) def _confirmar_transmision(self, w=None, ev=None, datos_tag=None): respuesta_conf = self._conexion.confirmar_acta(datos_tag) if respuesta_conf.status_ok() and "acta_definitiva" in respuesta_conf: # Descargo y muestro el definitivo # self._descargar_y_guardar_acta(respuesta_conf) self.set_mensaje(MSG_BOLD % respuesta_conf["mensaje"], idle=True, color=self.COLOR_OK) self._quitar_mesa(respuesta_conf["mesa"]) self._update_estados_mesas() if datos_tag in self.borradores: self.borradores.remove(datos_tag) else: self.set_mensaje(MSG_ERROR_BOLD % respuesta_conf["mensaje"], idle=True, color=self.COLOR_ERR) self._elimimar_vista_acta() def _cancelar_confirmacion(self, w=None, ev=None): self.set_mensaje(MSG_CONFIRMACION_CIERRE_CANCELADA, idle=True, color=self.COLOR_OK) gobject.idle_add(self._elimimar_vista_acta) @mostrar_espera @desbloqueante def __autenticar(self, datos): """ Autentica el usuario contra el servidor web dentro de un thread en segundo plano """ try: (usuario, clave) = datos.split(",") except ValueError: self.set_mensaje(MSG_TARJETA_INVALIDA, idle=True, color=self.COLOR_ERR) else: try: respuesta = self._conexion.autenticar(usuario, clave) if respuesta.status_ok(): self.__set_estado_conexion(self.AUTENTICADO) self.set_mensaje("%s" % respuesta["mensaje"], idle=True, color=self.COLOR_OK) # @TODO: Modificar la lista de estados desde el servidor # para capturar los estados de las mesas y mostrar en # distintos colores como la web de consulta estados = respuesta["estado_mesas"] for mesa in estados: for categoria in mesa[1:]: categoria.append(False) self._estados_mesas = estados self._update_estados_mesas() else: self.set_mensaje(MSG_ERROR_GENERICO % respuesta["mensaje"], idle=True, color=self.COLOR_ERR) except Exception as e: logger.debug(str(e)) self.set_mensaje(MSG_ERROR_COMUNICACION, idle=True, color=self.COLOR_ERR) def _quitar_mesa(self, id_mesa): nuevos_estados = [] for estado in self._estados_mesas: data_mesa = estado[0] if data_mesa[2].strip() != id_mesa[5:].strip(): nuevos_estados.append([data_mesa] + estado[1:]) self._estados_mesas = nuevos_estados def _update_estados_mesas(self): lbls_mesas = [] for mesa in self._estados_mesas: data_mesa = mesa[0] if data_mesa[1] not in (ESTADO_OK, ESTADO_PUBLICADA): actas = " ".join([("<b>%s</b>" % cargo[0]) if cargo[3] else cargo[0] for cargo in mesa[1:]]) lbl = "Mesa %s%s%s%s" % ( data_mesa[2], (data_mesa[3] if data_mesa[3] is not None else "X"), " - " if len(actas) else "", actas, ) lbls_mesas.append(lbl) lbl_pendientes = self._widget_tree.get_widget("label1") gobject.idle_add(lbl_pendientes.set_text, "Pendientes(%s):" % len(lbls_mesas)) gobject.idle_add(self._lbl_mesas.set_markup, "\n".join(lbls_mesas)) def _elimimar_vista_acta(self): for child in self._vbox_acta.get_children(): self._vbox_acta.remove(child) def _generar_y_mostrar_acta(self, datos_tag): self._elimimar_vista_acta() recuento = Recuento.desde_tag(datos_tag) self.set_mensaje(MSG_GENERANDO_IMG) imagen = recuento.a_imagen(svg=True, de_muestra=True, tipo=(CIERRE_TRANSMISION, recuento.cod_categoria)) path_destino = join(get_desktop_path(), "%s_%s.svg" % (recuento.mesa.numero, recuento.cod_categoria)) file_destino = open(path_destino, "w") file_destino.write(imagen) file_destino.close() self._mostrar_acta(path_destino) self.set_mensaje(MSG_RESTO_ACTAS) def _descargar_y_mostrar_acta(self, respuesta): """ Descarga el acta del servidor y lo muestra al usuario """ if not ACTA_DESGLOSADA: path_destino = join(get_desktop_path(), respuesta["file_name"]) url_acta = "%s://%s/%s" % (PROTOCOLO, self.config.HOST, respuesta["url"]) self._conexion.descargar(url_acta, path_destino) self._mostrar_acta(path_destino) def _mostrar_acta(self, path_destino): imgviewer = image_viewer.SimpleImageViewer(path_destino) self._vbox_acta.pack_start(imgviewer, True, True) self._vbox_acta.show_all() self._vbox_acta.show() def _descargar_y_guardar_acta(self, respuesta): """ Descarga el acta del servidor y lo guarda al usuario """ path_destino = join(os.getenv("HOME"), respuesta["file_name"]) self._conexion.descargar("%s://%s/%s" % (PROTOCOLO, self.config.HOST, respuesta["url"]), path_destino) self._elimimar_vista_acta() def __set_estado_conexion(self, estado_conexion): """ """ self.__estado_conexion = estado_conexion self.__tiempo_conexion = time.time() def salir(self, *args): if self.borradores: msg = MSG_ATENCION_NO_CONFIRMADAS % len(self.borradores) dialog = gtk.MessageDialog( self._wndPrincipal, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK_CANCEL, msg ) dialog.connect("response", self._respuesta_salir) dialog.run() dialog.destroy() else: self.salir_definitivo() def _respuesta_salir(self, dialog, response, data=None): if response == gtk.RESPONSE_OK: self.salir_definitivo() def salir_definitivo(self): if self.__modulo_lector: self.__modulo_lector.desconectar_lector() gtk.main_quit()