def test_creacion_dias_no_laborales(): # Creación de un conjunto de días laborables sin días cd = DiasNoLaborables(usuario='@JJ', insertar=True) assert cd.usuario == '@JJ' assert cd.dias_antelacion == None assert cd.conjunto == [] # Comprobar que los datos se han insertado en la base de datos data = cd.storage.get_usuario('@JJ') assert data['diasantelacion'] == None assert data['conjunto'] == [] # No se puede insertar ese día no laborable ya que ahí existe un acontecimiento with patch.object(DiasNoLaborables, '_obtener_dias_con_acontecimiento', return_value=[datetime(2020, 2, 3)]): cd = DiasNoLaborables(usuario='@albertosml', dias_antelacion=2, conjunto=[datetime(2020, 1, 2), datetime(2020, 2, 3)], insertar=True) assert cd.conjunto == [datetime(2020, 1, 2)] # Al especificar un día en el sexto día de la semana (sábado) el día de la semana no se guarda cd = DiasNoLaborables(usuario='@a', dias_antelacion=2, conjunto=[6, datetime(2019, 11, 9)], insertar=True) assert cd.conjunto == [6] # Creación de un conjunto de días laborables con días cd = DiasNoLaborables(usuario='@pepe', dias_antelacion=2, conjunto=[6, datetime(2020, 1, 2)], insertar=True) assert cd.conjunto == [6, datetime(2020, 1, 2)]
def test_cancelar_recordatorio(): cd = DiasNoLaborables(usuario='@albertosml', dias_antelacion=2, insertar=True) assert cd.dias_antelacion == 2 cd.cancelar_recordatorio() assert cd.dias_antelacion == None
def test_cancelar_recordatorio(app_client): DiasNoLaborables(usuario='albertosml', conjunto=[6, datetime(2020, 1, 2)], dias_antelacion=5, insertar=True) # Cancelo el recordatorio response = app_client.post('/diasnolaborables/cancelar_recordatorio', data=json.dumps({'usuario': 'albertosml'}), content_type='application/json') assert response.status_code == 200 assert json.loads(response.get_data()) == {'msg': 'Recordatorio cancelado'} # Chequear recordatorio cd = DiasNoLaborables(usuario='albertosml') assert cd.dias_antelacion == None
def cancelar_recordatorio(): data = request.get_json() usuario = data['usuario'] try: # Obtener el conjunto para ese usuario cd = DiasNoLaborables(usuario=usuario) except DiasNoLaborablesException: return jsonify({ 'msg': 'No existe un conjunto de días no laborables para ese usuario' }), 200 # Cancelar recordatorio de un día no laboral de un usuario cd.cancelar_recordatorio() return jsonify({'msg': 'Recordatorio cancelado'}), 200
def modificar_recordatorio(): data = request.get_json() usuario = data['usuario'] dias_antelacion = data['diasantelacion'] try: # Obtener el conjunto para ese usuario cd = DiasNoLaborables(usuario=usuario) except DiasNoLaborablesException: return jsonify({ 'msg': 'No existe un conjunto de días no laborables para ese usuario' }), 200 # Modificar días de antelación de recuerdo cd.modificar_dias_antelacion_recordatorio(dias_antelacion) return jsonify({'msg': 'Recordatorio modificado'}), 200
def __init__(self): super(Table, self).__init__() self.setupUi(self) self.table_logs.setColumnCount(10) self.table_rango.setColumnCount(10) self.__row = 0 self.diasnolaborables = DiasNoLaborables() self.tablesContent = {'logs': [], 'rango': []}
def eliminar_dia_no_laboral(): data = request.get_json() usuario = data['usuario'] dia_a_eliminar = data['diaaeliminar'] try: # Obtener el conjunto para ese usuario cd = DiasNoLaborables(usuario=usuario) except DiasNoLaborablesException: return jsonify({ 'msg': 'No existe un conjunto de días no laborables para ese usuario' }), 200 # Transformar fecha (si se ha introducido) if isinstance(dia_a_eliminar, str): dia_a_eliminar = datetime.strptime(dia_a_eliminar, '%d/%m/%Y') # Se elimina el día no laboral del conjunto cd.eliminar_dia_no_laboral(dia_a_eliminar) return jsonify({'msg': 'Se ha eliminado el día del conjunto'}), 200
def obtener_dias(): data = request.get_json() usuario = data['usuario'] fecha_inicio = data['fechainicio'] fecha_fin = data['fechafin'] try: # Obtener el conjunto para ese usuario cd = DiasNoLaborables(usuario=usuario) except DiasNoLaborablesException: return jsonify({ 'msg': 'No existe un conjunto de días no laborables para ese usuario' }), 200 # Transformar fechas fecha_inicio = datetime.strptime(fecha_inicio, '%d/%m/%Y') fecha_fin = datetime.strptime(fecha_fin, '%d/%m/%Y') # Devolver conjunto días no laborables dias_no_laborables = cd.devolver_dias_no_laborables_anio( fecha_inicio, fecha_fin) return jsonify({'diasnolaborables': dias_no_laborables}), 200
def aniadir_dias_no_laborables(): data = request.get_json() usuario = data['usuario'] dias_a_aniadir = data['diasaaniadir'] try: # Obtener el conjunto para ese usuario cd = DiasNoLaborables(usuario=usuario) except DiasNoLaborablesException: return jsonify({ 'msg': 'No existe un conjunto de días no laborables para ese usuario' }), 200 # Procesar conjunto de días no laborables dias_a_aniadir = transformar_conjunto(dias_a_aniadir) # Añadir los días no laborables al conjunto cd.aniadir_dias_no_laborables(dias_a_aniadir) return jsonify({ 'msg': 'Se han añadido los días no laborables correspondientes al conjunto' }), 200
def __init__(self): database = SQL() database.loadChekInOutTable() self.matriz = database.data_matrix self.personal = sorted(jornada_personal.keys()) dates = database.dates self.dates = [d for d in dates if d > datetime.date(2014, 1, 1)] self.start_date = self.dates[0] self.stop_date = self.dates[-1] self.exceptions = None self.workers_not_found = None self.days_of_work = {} # contendrá los días de trabajo del archivo empaquetados en la clase Workday self.validateCheck() self.content = () self.thisday = DiasNoLaborables() self.organizar_por_fecha()
def test_eliminar_dia(app_client): crear_conjunto_dias_no_laborables() # Elimino el jueves del conjunto response = app_client.post('/diasnolaborables/eliminar_dia', data=json.dumps({ 'usuario': 'albertosml', 'diaaeliminar': 6 }), content_type='application/json') assert response.status_code == 200 assert json.loads(response.get_data()) == { 'msg': 'Se ha eliminado el día del conjunto' } # Comprobar conjunto cd = DiasNoLaborables(usuario='albertosml') assert cd.conjunto == [datetime(2020, 1, 2)]
def test_modificar_recordatorio(app_client): crear_conjunto_dias_no_laborables() # Modifico los días de antelación de recordatorio response = app_client.post('/diasnolaborables/modificar_recordatorio', data=json.dumps({ 'usuario': 'albertosml', 'diasantelacion': 5 }), content_type='application/json') assert response.status_code == 200 assert json.loads(response.get_data()) == { 'msg': 'Recordatorio modificado' } # Chequear recordatorio cd = DiasNoLaborables(usuario='albertosml') assert cd.dias_antelacion == 5
def test_aniadir_dias(app_client): crear_conjunto_dias_no_laborables() # Añado el jueves al conjunto response = app_client.post('/diasnolaborables/aniadir_dias', data=json.dumps({ 'usuario': 'albertosml', 'diasaaniadir': [4] }), content_type='application/json') assert response.status_code == 200 assert json.loads(response.get_data()) == { 'msg': 'Se han añadido los días no laborables correspondientes al conjunto' } # Comprobar conjunto cd = DiasNoLaborables(usuario='albertosml') assert cd.conjunto == [4, 6]
def nuevo_conjunto_dias_no_laborables(): data = request.get_json() usuario = data['usuario'] dias_antelacion = data.get('diasantelacion', None) conjunto = data.get('conjunto', []) # Procesar conjunto de días no laborables conjunto = transformar_conjunto(conjunto) try: # Crear el conjunto de días no laborables para ese usuario DiasNoLaborables(usuario=usuario, dias_antelacion=dias_antelacion, conjunto=conjunto, insertar=True) except DiasNoLaborablesException: return jsonify({'msg': 'Problemas al crear el conjunto'}), 200 return jsonify({ 'msg': 'El conjunto de días laborables para ese usuario se ha creado' }), 200
class Reorganizar: def __init__(self): database = SQL() database.loadChekInOutTable() self.matriz = database.data_matrix self.personal = sorted(jornada_personal.keys()) dates = database.dates self.dates = [d for d in dates if d > datetime.date(2014, 1, 1)] self.start_date = self.dates[0] self.stop_date = self.dates[-1] self.exceptions = None self.workers_not_found = None self.days_of_work = {} # contendrá los días de trabajo del archivo empaquetados en la clase Workday self.validateCheck() self.content = () self.thisday = DiasNoLaborables() self.organizar_por_fecha() def validateCheck(self): matrix = [] exceptions = [] workers_not_found = [] for row in self.matriz: # determiar si el check in es valido worker = row[0] check = row[1] in_or_out = row[2] # Los checks mal hechos o fuera de los parametros aceptados # pasan a una lista para ser verificados try: jornada = jornada_personal[worker] except KeyError: if worker not in workers_not_found: workers_not_found.append(worker) continue if jornada == "diurno": if not HorarioDiurno.validateCheck(check, in_or_out): exceptions.append(row) continue if jornada == "nocturno": if not HorarioNocturno.validateCheck(check, in_or_out): exceptions.append(row) continue matrix.append(row) self.exceptions = exceptions self.workers_not_found = workers_not_found self.matriz = matrix def __str__(self): texto = '' for line in self.matriz: line = line[:8] line = '\t'.join(line) texto += line + '\n' return texto def organizar_por_fecha(self): """ return: dict """ # ordena las self.fechas por lista para luego generar el documento por fecha self.__matriz = self.matriz.copy() self.content = () # Rango de fechas start_date = min(self.dates) stop_date = max(self.dates) ciclos.DaybyDay(start_date, stop_date, daysCycle=self.loadContent) def loadContent(self, date): """ Función sobre la cual se iterará para llenar los work_days. Por cada día en el intervalo se generará un Workday que será almacenado en work_days :param date: recibe la fecha de iteración :return: work_days (lista) """ # organiza la matriz que generara el documento de salida por fecha workday = Workday(date, self.personal) temp = [] # matriz temporal for line in self.__matriz: if date == line[1].date(): # line [worker, checktime, in/out] temp.append(line) # agrega la matriz correspondiente a la fecha for worker in self.personal: # transformar a [worker, time_segment, entry_time, exit_time] worker_schedule = jornada_personal[worker] if worker_schedule == "diurno": w = {"worker": worker, "matutino": {"entrada": None, "salida": None}, "vespertino": {"entrada": None, "salida": None}} for line in temp: if worker in line: # checktime, in/out -> time_segment, entry_time, exit_time checktime = line[1] in_out = line[2] if HorarioDiurno.is_matutino(checktime, in_out): if in_out == "I": w["matutino"]["entrada"] = checktime elif in_out == "O": w["matutino"]["salida"] = checktime elif HorarioDiurno.is_vespertino(checktime, in_out): if in_out == "I": w["vespertino"]["entrada"] = checktime elif in_out == "O": w["vespertino"]["salida"] = checktime workday.load_horary( w["worker"], "matutino", w["matutino"]["entrada"], w["matutino"]["salida"]) workday.load_horary( w["worker"], "vespertino", w["vespertino"]["entrada"], w["vespertino"]["salida"]) elif worker_schedule == "nocturno": w = {"worker": worker, "nocturno": {"entrada": None, "salida": None}} for line in temp: if worker in line: # checktime, in/out -> time_segment, entry_time, exit_time checktime = line[1] in_out = line[2] if in_out == "I": w["nocturno"]["entrada"] = checktime elif in_out == "O": w["nocturno"]["salida"] = checktime workday.load_horary( w["worker"], "nocturno", w["nocturno"]["entrada"], w["nocturno"]["salida"]) else: print("Este tipo de horario no existe aun") raise TypeError # end for worker in self.personal # end for date in self.dates # TODO mecanismo para detectar las fechas especiales y modificar las horas laborables if not self.thisday.isWorkable(date): workday.isworkable = False workday.changeWorkableHours(diurno=0, nocturno=0) self.content += (workday,) def calculateWorkedTime(self): """ Agrega los workday del contenido los calculos del tiempo trabajado, tiempo ausente y tiempo extra """ content = list(self.content) content_length = len(self.content) assert content_length > 0, "No hay contenido que analizar." for i in range(content_length): workday = self.content[i] date = workday.date try: next_workday = self.content[i + 1] except IndexError: next_workday = None # cargar el resumen del mes for w in self.personal: w_info = workday.workers[w] h = w_info['horario'].content zero_time = datetime.timedelta(hours=0) worked_time = zero_time for k, v in h.items(): if k == 'matutino' or k == 'vespertino': v_in = v['entrada'] v_out = v['salida'] elif k == 'nocturno': # worked_time += datetime.timedelta(hours=24) + (v_out - v_in) v_in = v['entrada'] if next_workday is None: v_out = None else: v_out = next_workday.workers[w]['horario'].content['nocturno']['salida'] if v_out != None and v_in != None: worked_time += v_out - v_in #Dependiendo de la jornada del trabajador, las horas laborables del dia pueden variar work_time_reference = workday.workableHours[jornada_personal[w]] # si no hay tiempo extra, el resultado será zero. Es decir, no habrán resultados negativos if worked_time > work_time_reference: extra_time = worked_time - work_time_reference else: extra_time = datetime.timedelta(hours=0) # si no hay tiempo ausente, el resultado será zero. Igual que para el tiempo extra if worked_time < work_time_reference: absent_time = work_time_reference - worked_time else: absent_time = datetime.timedelta(hours=0) workday.addPerformance(w, worked_time, extra_time, absent_time) content[i] = workday def row(self, n): row = self.matriz[n] # print(row) return row def column(self, n): column = [] for line in self.matriz: try: column.append(line[n]) except IndexError: raise Exception(IndexError, str(line[n])) return column def filter(self, from_date, to_date, personal=None): # TODO filtrar personal if personal is None: personal = self.personal self.start_date = from_date self.stop_date = to_date filtered_content = () for wd in self.content: d = wd.date if from_date > d: continue if to_date < d: continue filtered_content += (wd.filterWorkers(personal),) self.content = filtered_content
def crear_conjunto_dias_no_laborables(): cd = DiasNoLaborables(usuario='@albertosml', conjunto=[6, datetime(2020, 1, 2)], insertar=True) return cd
class Table(QWidget, Ui_Form): def __init__(self): super(Table, self).__init__() self.setupUi(self) self.table_logs.setColumnCount(10) self.table_rango.setColumnCount(10) self.__row = 0 self.diasnolaborables = DiasNoLaborables() self.tablesContent = {'logs': [], 'rango': []} def append(self, item, column=0, color=None): if type(item) == list: for i in item: newItem = QTableWidgetItem(i) if color is not None: newItem.setBackground(QColor(color)) self.activeTable.setItem(self.__row, column, newItem) column += 1 index_increment = 1 else: newItem = QTableWidgetItem(item) if color is not None: newItem.setBackground(color) index_increment = 1 self.activeTable.setItem(self.__row, column, newItem) item = [item] column += 1 self.__row += index_increment if self.activeTable is self.table_logs: tableName = 'logs' elif self.activeTable is self.table_rango: tableName = 'rango' else: tableName = 'otra' save = item + ['' for i in range(10 - column)] self.tablesContent[tableName] += [save, ] def paintHorary(self, row, horary): column = 1 for h in (horary['matutino']['entrada'], horary['matutino']['salida'], horary['vespertino']['entrada'], horary['vespertino']['salida'], horary['nocturno']['entrada'], horary['nocturno']['salida'],): if not h == '--': self.activeTable.item(row, column).setBackground(QColor(150, 255, 150)) else: self.activeTable.item(row, column).setBackground(QColor(255, 100, 100)) column += 1 def paintNotWorkable(self): pass def loadTables(self, reporte): self.reporte = reporte self.totals = AnvizReader.TotalizeByRange(reporte) self.reset() #Cargar tabla de dias self.activeTable = self.table_logs self.__workdays = list(self.reporte.content) day_table_length = len(self.__workdays) * (2 + len(header) + len(self.reporte.personal)) self.table_logs.setRowCount(day_table_length) while self.__workdays: self.workDays() self.table_logs.setRowCount(self.__row + 1) #Cargar tabla de rango self.activeTable = self.table_rango self.__row = 0 self.__workrange = list(self.totals.byRange) month_table_length = len(self.__workrange) * (5 + len(self.reporte.personal)) self.table_rango.setRowCount(month_table_length) while self.__workrange: self.workRange() self.table_rango.setRowCount(self.__row + 1) def workDays(self): date = self.__workdays[0].date year, month, day = date.year, date.month, date.day fecha_invertida = "Fecha: {dia} {d} de {m} del {year}".format(dia=MyDates.dayName(year, month, day), d=day, m=MyDates.monthName(month), year=year) #TODO mostrar los dias no laborables como una fecha coloreada en azul claro if not self.diasnolaborables.isWorkable(date): self.activeTable.setSpan(self.__row, 0, 1, 10) self.append(fecha_invertida, color=QColor(200,200,255)) if self.diasnolaborables.significado(date) is not None: self.activeTable.setSpan(self.__row, 0, 1, 10) self.append(self.diasnolaborables.significado(date), color=QColor(200,200,255)) self.append('') del self.__workdays[0] return #implementando una lista FIFO try: workday = self.__workdays[0] except IndexError: print('--- END ---') return else: # self..append(header) # Cargar en excel el desempeño de los trabajadores en el día workday_info = workday.get_workers_info() self.activeTable.setSpan(self.__row, 0, 1, 10) self.append(fecha_invertida) for horario in horarios: for i in heads[horario]: self.append(i) for worker in self.reporte.personal: if personalShift[worker] != horario: continue schedule = workday_info[worker] # print(worker, schedule) h = schedule['horario'] if horario == 'diurno': line_to_QTable = [schedule['nombre'], h['matutino']['entrada'], h['matutino']['salida'], h['vespertino']['entrada'], h['vespertino']['salida'], schedule['tiempo trabajado'], schedule['tiempo extra'], schedule['tiempo ausente']] else: line_to_QTable = [schedule['nombre'], h['nocturno']['entrada'], h['nocturno']['salida'], schedule['tiempo trabajado'], schedule['tiempo extra'], schedule['tiempo ausente']] self.append(line_to_QTable) #self.paintHorary(self.__row - 1, h) self.append('') # salto de linea self.append('') del self.__workdays[0] def workRange(self): if not self.__workrange: return self.activeTable = self.table_rango _wm = self.__workrange year, month, day = _wm.from_date.year, _wm.from_date.month, _wm.from_date.day fecha_invertida1 = "Desde el {dia} {d} de {m} del {year}".format(dia=MyDates.dayName(year, month, day), d=day, m=MyDates.monthName(month), year=year) year, month, day = _wm.to_date.year, _wm.to_date.month, _wm.to_date.day fecha_invertida2 = "Hasta el {dia} {d} de {m} del {year}".format(dia=MyDates.dayName(year, month, day), d=day, m=MyDates.monthName(month), year=year) self.activeTable.setSpan(self.__row, 0, 1, 4) self.append(fecha_invertida1) self.activeTable.setSpan(self.__row, 0, 1, 4) self.append(fecha_invertida2) self.activeTable.setSpan(self.__row, 0, 1, 4) self.append("Horas diurnas laborables: {}".format(_wm.workableHours['diurno'])) self.activeTable.setSpan(self.__row, 0, 1, 4) self.append("Horas nocturnas laborables: {}".format(_wm.workableHours['nocturno'])) self.append(header2) for worker in self.reporte.personal: schedule = _wm.getWorkerInfo(worker) # print(worker, schedule) line_to_QTable = [worker, str(schedule['tiempo trabajado']), str(schedule['tiempo extra']), str(schedule['tiempo ausente'])] self.append(line_to_QTable) # salto de linea self.append('') def loadRange(self, reporte): self.reporte = reporte self.totals = AnvizReader.TotalizeByRange(reporte) self.reset() # Cargar tabla de dias self.activeTable = self.table_logs self.__workdays = list(self.reporte.content) day_table_length = len(self.__workdays) * (2 + len(header) + len(self.reporte.personal)) self.table_logs.setRowCount(day_table_length) while self.__workdays: self.workDays() # Cargar tabla de meses self.__row = 0 self.__workrange = self.totals.byRange month_table_length = 5 + len(self.reporte.personal) self.table_rango.setRowCount(month_table_length) self.workRange() def reset(self): self.__row = 0 self.table_rango.clearContents() self.table_logs.clearContents()