def graficar_por_producto(self, partes): """ Construye un gráfico de rendimiento por producto. Agrupa los partes de un mismo producto y calcula su productividad conjunta. Esos datos los envía a un gráfico para ver los productos que mejor le van a la línea. """ vpro = VentanaProgreso(padre = self.wids['ventana']) vpro.mostrar() i = 0.0 tot = len(partes) productos = defaultdict(lambda: []) for pdp in partes: i += 1 vpro.set_valor(i / tot, "Analizando rendimiento por producto...") producto = pdp.productoVenta if not producto: txterr = "consulta_productividad.py::graficar_por_producto ->"\ " %s:%s sin producto de venta." % (pdp.puid, pdp.get_info()) myprint(txterr) self.logger.warning(txterr) else: productos[producto].append(pdp) data = OrderedDict() tot += len(productos) productos_ordenados = productos.keys() productos_ordenados.sort(key = lambda prod: prod.descripcion) for producto in productos_ordenados: i += 1 vpro.set_valor(i / tot, "Analizando rendimiento por producto...") pdps = productos[producto] data[producto.descripcion] = calcular_productividad_conjunta( tuple(pdps)) try: plot = GtkCairoPlot(HORIZONTAL, data, self.wids['ventana']) except ValueError: plot = gtk.Label("<big>No hay datos suficientes.</big>") plot.set_use_markup(True) parent = self.wids['grafico'].parent parent.add(plot) parent.remove(self.wids['grafico']) self.wids['grafico'] = plot parent.show_all() vpro.ocultar()
def rellenar_tabla(self, partes, solobalas): """ Rellena el model con los partes rescatados de la BD en `buscar`. PRERREQUISITO: Los partes vienen en una lista y deben estar ordenados por fecha y hora de inicio. """ # Lo primero de todo. Si está activado el DEBUG, muestro las columnas. tv = self.wids['tv_datos'] for ncol in range(7, len(tv.get_columns())): tv.get_column(ncol).set_property("visible", pclases.DEBUG) self.huecos = [] # Lista de los últimos partes tratados por línea. self.incidencias_horaini = [] self.incidencias_horafin = [] self.tiempo_faltante = mx.DateTime.TimeDeltaFrom(0) vpro = VentanaProgreso(padre = self.wids['ventana']) model = self.wids['tv_datos'].get_model() model.clear() total = 0 kilosproducidos = 0 kilosgranza = 0 vector = [] tot = len(partes) i = 0.0 vpro.mostrar() e_personasturno = 0 tiempototal = 0 tiempo_sin_incidencias = 0 # Por si inicia una consulta sin resultados metrosproducidos = 0 kilosconsumidos = kilosconsumidos_partidas_completas = 0 tiempototaltotal = mx.DateTime.DateTimeDelta(0) #selffin = utils.parse_fecha('/'.join(self.fin.split('/')[::-1])) selffin = self.fin # ... WTF? Esto está hecho como el culo. ¡REFACTORIZAR! # XXX Primer intento de acelerar los treeview self.wids['tv_datos'].freeze_child_notify() self.wids['tv_datos'].set_model(None) # XXX personashora = [] dia_actual = "" # Estos tres parámetros son para medir la # producción y productividad por día. produccion_actual = 0.0 #productividad_actual = 0.0 t_teorico_por_dia = 0 t_real_por_dia = 0 pdps_dia = [] # Ojo. Los partes deben venir ordenados para que # esta lista solo contenga los partes del día tratado en la # iteración por la que vaya. Se limpia al cambiar de un día al # siguiente mientras relleno los datos. #nodos_hijos_por_dia = 0 balas_consumidas = [] # Lista con las balas consumidas por # todos los partes, así evito contar la misma dos veces si en dos # partes se ha usado la misma partida o dos partidas de la misma # partida de carga. padre = None # En una de las iteraciones se espera que padre se haya # instanciado en el final de la anterior. Inicializo para evitar # errores y errores en análisis sintáctico de algunos IDE en la # línea 342. for pdp in partes: if pdp.se_solapa(): texto_warning = "%sconsulta_productividad::rellenar_tabla"\ " -> El parte ID %d se solapa con otros de la misma línea"\ ". Si estaba verificado, lo desbloqueo para que se vuelva"\ " a revisar y lo ignoro para el cálculo actual." % ( self.usuario and self.usuario.usuario + ": " or "", pdp.id) self.logger.warning(texto_warning) myprint(texto_warning) pdp.bloqueado = False continue vpro.set_valor(i/tot, 'Añadiendo parte %s...' % ( utils.str_fecha(pdp.fecha))) delta_entre_partes, parte_anterior = detectar_hueco(pdp, self.huecos) if delta_entre_partes: self.marcar_hueco(parte_anterior, pdp) self.tiempo_faltante += delta_entre_partes kilosgranza_del_parte = 0.0 (tiempototal, tiempo_sin_incidencias) = self.calcular_tiempo_trabajado(pdp) tiempototaltotal += tiempototal #productividad = pdp.calcular_productividad() # CWT: Ahora no se calcula así, sino como el antiguo rendimiento. productividad = pdp.calcular_rendimiento() # Tratamiento especial para partes de balas try: str_producto = pdp.productoVenta.descripcion except AttributeError: str_producto = "Sin producción" if (self.wids['r_balas'].get_active() or self.wids['r_ambos'].get_active()): # Sólo balas o todos. if pdp.es_de_fibra(): balas = [a.bala for a in pdp.articulos if a.balaID != None] bigbags = [a.bigbag for a in pdp.articulos if a.bigbagID != None] for b in balas: kilosproducidos += b.pesobala for b in bigbags: kilosproducidos += b.pesobigbag kilosgranza_del_parte = pdp.get_granza_consumida() kilosgranza += kilosgranza_del_parte if pdp.get_produccion()[0] - kilosgranza_del_parte > 1000: # XXX DEBUG self.logger.warning("El parte ID %d (%s) ha consumido " "(%s) mucha menos granza que fibra ha " "producido (%s) !!!" % (pdp.id, utils.str_fecha(pdp.fecha), utils.float2str(kilosgranza_del_parte), utils.float2str(pdp.get_produccion()[0]))) # XXX DEBUG if (self.wids['r_cemento'].get_active() or self.wids['r_ambos'].get_active()): kilosproducidos += pdp.get_produccion()[0] kilosconsumidos += sum([bb.pesobigbag for bb in pdp.bigbags]) if (self.wids['r_rollos'].get_active() or self.wids['r_ambos'].get_active()): # Sólo rollos o todos. if pdp.es_de_geotextiles(): superficies_defectuosos = [a.superficie for a in pdp.articulos if a.es_rollo_defectuoso()] metrosproducidos += sum(superficies_defectuosos) rollos = [a.rollo for a in pdp.articulos if a.rollo != None] if len(rollos) > 0: # Para que no intente hacer el # cálculo con partes que tengan balas y/o no rollos. pv = pdp.articulos[0].productoVenta cer = pv.camposEspecificosRollo metrosproducidos += (len(rollos) * cer.metros_cuadrados) if pdp.articulos[0].es_rollo(): partida = pdp.articulos[0].rollo.partida elif pdp.articulos[0].es_rollo_defectuoso(): partida = pdp.articulos[0].rolloDefectuoso.partida else: partida = None if partida != None: contar_kilosconsums_partidas_completas \ = partida.entra_en_cota_superior(selffin) for b in partida.balas: if b not in balas_consumidas: # Evito contar # la misma bala dos veces. bpesobala = b.pesobala kilosconsumidos += bpesobala if contar_kilosconsums_partidas_completas: kilosconsumidos_partidas_completas \ += bpesobala balas_consumidas.append(b) empleados = len(pdp.horasTrabajadas) e_personasturno += empleados tiempoparte = pdp.get_duracion().hours if tiempoparte > 0: personashora.append(empleados/tiempoparte) else: personashora.append(0.0) vector.append(productividad) produccion = pdp.get_produccion() # Resúmenes por día como nodos padre del TreeView. if not dia_actual: pdps_dia.append(pdp) if dia_actual == utils.str_fecha(pdp.fecha): pdps_dia.append(pdp) else: if dia_actual != "": # Actualizo el padre if not self.wids['r_ambos'].get_active(): model[padre][4] = "%s %s" % ( utils.float2str(produccion_actual), produccion[1]) else: # Evito mezclar kilos con metros model[padre][4] = "-" productividad_actual = calcular_productividad_conjunta( tuple(pdps_dia)) model[padre][5] = "%s %%" % ( utils.float2str(productividad_actual)) model[padre][6] = min(productividad_actual, 100) model[padre][7] = t_teorico_por_dia model[padre][8] = t_real_por_dia pdps_dia = [pdp] # He cambiado de día. Limpio. t_teorico_por_dia = 0 t_real_por_dia = 0 dia_actual = utils.str_fecha(pdp.fecha) produccion_actual = 0.0 productividad_actual = 0.0 #nodos_hijos_por_dia = 0 padre = model.append(None, (dia_actual, "", "", "", "", "%s %%" % (utils.float2str(productividad_actual)), min(productividad_actual, 100), "", # t. teórico "", # t. real 0)) t_teorico_pdp = pdp.calcular_tiempo_teorico() t_teorico_por_dia += t_teorico_pdp t_real_pdp = pdp.calcular_tiempo_real() t_real_por_dia += t_real_pdp model.append(padre, (utils.str_fecha(pdp.fecha), str(pdp.horainicio)[:5], str(pdp.horafin)[:5], str_producto, "%s %s" % (utils.float2str(produccion[0]), produccion[1]), "%s %%" % (utils.float2str(productividad)), min(productividad, 100), t_teorico_pdp, t_real_pdp, pdp.id)) produccion_actual += produccion[0] i+=1 # Actualizo el padre de los últimos nodos: if dia_actual != "": # Actualizo el padre if not self.wids['r_ambos'].get_active(): model[padre][4] = "%s %s" % ( utils.float2str(produccion_actual), produccion[1]) else: # Evito mezclar kilos con metros model[padre][4] = "-" productividad_actual = calcular_productividad_conjunta( tuple(pdps_dia)) model[padre][5] = "%s %%" % (utils.float2str(productividad_actual)) model[padre][6] = min(productividad_actual, 100) # Campos de depuración model[padre][7] = t_teorico_por_dia model[padre][8] = t_real_por_dia vpro.ocultar() # XXX Primer intento de acelerar los treeview self.wids['tv_datos'].set_model(model) self.wids['tv_datos'].thaw_child_notify() # XXX if partes != []: total = calcular_productividad_conjunta(tuple(partes)) # Campos especiales de "Sólo balas" if self.wids['r_balas'].get_active(): self.wids['label7'].set_text('Kilos producidos:') self.wids['label8'].set_text('Kilos granza consumidos:') self.wids['label9'].set_text('Kilos / Hora producción:') self.wids['e_kilosproducidos'].set_text( "%s kg" % (utils.float2str(kilosproducidos))) self.wids['e_kilosgranza'].set_text( "%s kg" % (utils.float2str(kilosgranza))) try: e_kiloshora = (kilosproducidos /float(tiempototaltotal.hours)) except ZeroDivisionError: self.logger.warning( "consulta_productividad.py::rellenar_tabla" " -> tiempototaltotal = 0") e_kiloshora = 0 # Cero por poner algo. Porque un parte # de tiempo transcurrido=0... se las trae. self.wids['e_kiloshora'].set_text("%s kg/h" % ( utils.float2str(e_kiloshora))) elif self.wids['r_rollos'].get_active(): tips = gtk.Tooltips() tips.set_tip(self.wids['e_kilosgranza'], "Kilogramos consumidos contando partidas " "completas por defecto y por exceso.\n(En un caso" " se cuentan solo partidas de carga cuyas " "partidas de geotextiles se hayan completado " "estrictamente antes o en la cota superior.\n" "En el otro, por exceso, se contabilizan " "partidas completas aunque parte de su " "producción se salga del rango de fechas.\n" "En ambos casos el límite inferior es flexible " "-por compensación-.)") tips.enable() self.wids['label7'].set_text('m² producidos:') self.wids['label8'].set_text('Kg fibra consumidos:') self.wids['label9'].set_text('m²/h producción:') try: e_kiloshora = (metrosproducidos/float(tiempototaltotal.hours)) except ZeroDivisionError: self.logger.warning( "consulta_productividad.py::rellenar_tabla -> " "tiempototaltotal = 0") e_kiloshora = 0 self.wids['e_kilosproducidos'].set_text("%s m²" % ( utils.float2str(metrosproducidos))) if kilosconsumidos_partidas_completas != kilosconsumidos: self.wids['e_kilosgranza'].set_text("%s kg ~ %s kg" % ( utils.float2str(kilosconsumidos_partidas_completas), utils.float2str(kilosconsumidos))) else: self.wids['e_kilosgranza'].set_text("%s kg" % ( utils.float2str(kilosconsumidos))) self.wids['e_kiloshora'].set_text("%s m²/h" % ( utils.float2str(e_kiloshora))) else: self.wids['label7'].set_text('Producido:') self.wids['label8'].set_text('Consumido:') self.wids['label9'].set_text('Producción combinada a la hora:') try: e_kiloshora = (kilosproducidos/float(tiempototaltotal.hours)) metros_hora = (metrosproducidos/float(tiempototaltotal.hours)) except ZeroDivisionError: self.logger.warning("consulta_productividad.py::" "rellenar_tabla -> tiempototaltotal = 0") e_kiloshora = 0 metros_hora = 0 self.wids['e_kilosproducidos'].set_text("%s m²; %s kg" % ( utils.float2str(metrosproducidos), utils.float2str(kilosproducidos))) self.wids['e_kilosgranza'].set_text( "%s kg fibra; %s kg granza" % (utils.float2str( kilosconsumidos), utils.float2str(kilosgranza))) self.wids['e_kiloshora'].set_text("%s m²/h; %s kg/h" % ( utils.float2str(metros_hora), utils.float2str(e_kiloshora))) # Fin campos especiales de "Sólo balas" self.wids['curva'].set_range(0, len(vector), 0, 100) self.wids['curva'].set_vector(vector) self.wids['curva'].set_curve_type(gtk.CURVE_TYPE_SPLINE) # self.wids['curva'].set_curve_type(gtk.CURVE_TYPE_LINEAR) self.wids['pb'].set_fraction(max(min(total, 100), 0) / 100.0) else: self.wids['curva'].set_range(0, 2, 0, 100) self.wids['curva'].set_vector((0, 0)) self.wids['pb'].set_fraction(1 / 100.0) self.wids['e_kilosproducidos'].set_text('') self.wids['e_kilosgranza'].set_text('') self.wids['e_kiloshora'].set_text('') self.wids['e_total'].set_text("%s %%" % (utils.float2str(total))) try: personashora = sum(personashora)/len(personashora) except ZeroDivisionError: personashora = 0 try: e_personasturno = float(e_personasturno) / len(partes) except ZeroDivisionError: e_personasturno = 0 self.wids['e_personasturno'].set_text("%s (%s personas/h)" % ( utils.float2str(e_personasturno), utils.float2str(personashora))) self.wids['e_tiempo_faltante'].set_text( self.tiempo_faltante and (str_horas(self.tiempo_faltante) + " h") or "") self.wids['e_tiempo_partes'].set_text( str_horas(tiempototaltotal) + " h")