def _analyse_operation_non_imputable(self, operation_id): c = session().query(Operation.operation_id, OperationDefinition.imputable, OperationDefinition.short_id.label("operation_definition_short_id"), OperationDefinition.on_operation, Order.accounting_label.label("order_accounting_label"), OrderPart.label.label("order_part_label"), OrderPart.state.label("order_part_state")).\ join(ProductionFile).\ join(OrderPart).\ join(Order).\ join(OperationDefinition, OperationDefinition.operation_definition_id == Operation.operation_definition_id).\ filter(Operation.operation_id == operation_id).first() if not c: raise ServerException(ServerErrors.operation_unknown, operation_id) elif not c.imputable: raise ServerException( ServerErrors.operation_definition_not_imputable, c.operation_definition_short_id) elif c.order_part_state != OrderStatusType.order_ready_for_production: raise ServerException( ServerErrors.order_part_not_in_production_unknown, "{}{}".format(c.order_accounting_label, c.order_part_label or "")) else: raise ServerException( ServerErrors.general_failure, "This operation is imputable but I expectd it's not. Contact support !" )
def get_operation_information(self, employee_id: int, operation_id: int): """ Retrieve information about an operation. The operation is meant to be imputable. Returns a tuple : * operation * machines : machines available for the operation * next_action (if it was computable) * colleagues : a list of person working on the operation FIXME That is a problem : if the operation is not imputable, one should still be able to report an "end of task" (but no "start task") """ try: o, m, next_action_kind, colleagues = dao.operation_dao.find_by_operation_id_frozen( employee_id, operation_id) except Exception as ex: mainlog.exception(ex) raise ServerException(ServerErrors.operation_unknown, operation_id) if o.imputable: # Ouch! I have to convert the colleagues to an array of tuple # because the colleagues are represented in a dict with keys which # are integers. Unfortunately, json converts those key to strings... return o, m, next_action_kind, colleagues else: self._analyse_operation_non_imputable(operation_id) raise ServerException(ServerErrors.operation_non_imputable, operation_id, o.order_part_identifier)
def _recordTaskOnOperation(self, operation_id, machine_id): """ Create a task for reporting time on a "task on operation" with machine_id and operation_id """ mainlog.debug("*** " * 100) mainlog.debug( "_recordTaskOnOperation operation_id={}, machine_id={}".format( operation_id, machine_id)) # First we locate the operation and check if one can record time on it. q = session().query(Operation.operation_id, Order.accounting_label.label("order_accounting_label"), OrderPart.label.label("order_part_label"), OrderPart.description.label('order_part_description'), Customer.fullname.label('customer_name'), OperationDefinition.short_id.label("operation_definition_short_id"), TaskOnOperation.task_id, and_(OperationDefinition.imputable == True, OperationDefinition.on_operation == True, Order.state == OrderStatusType.order_ready_for_production).label("imputable"), TaskOnOperation.active.label("task_active"), Operation.description.label("operation_description"), Operation.position.label("operation_position"))\ .join(ProductionFile).join(OrderPart).join(Order).outerjoin(TaskOnOperation,TaskOnOperation.operation_id == Operation.operation_id).join(OperationDefinition, OperationDefinition.operation_definition_id == Operation.operation_definition_id).join(Customer,Order.customer_id == Customer.customer_id).options(lazyload('*')).filter(Operation.operation_id == operation_id) row = q.first() if row is None: self._analyse_operation_non_imputable(operation_id) raise ServerException(ServerErrors.operation_non_imputable, operation_id) h = row._asdict() # SQLA KeyedTupe h['task_type'] = 'TaskOnOperation' h['order_part_label'] = str(row[1]) + (row[2] or "-") if row.imputable: task_id = row.task_id if task_id is None: task_id = dao.task_dao.create_task_on_operation( operation_id, machine_id).task_id elif not row.task_active: raise ServerException(ServerErrors.task_not_imputable, task_id) return task_id, h else: raise ServerException( ServerErrors.operation_non_imputable, u"'{} {}'".format((row.operation_definition_short_id or ""), (row.operation_description or "")), str(row.order_accounting_label) + (row.order_part_label or '-'))
def _figure_task_for_operation_and_machine(self, operation_id, machine_id): # FIXME Same as TaskDAO.get_task_for_operation_and_machine operation = self._load_operation_and_task_data(operation_id, machine_id) task_id = operation.task_id # Check if the pointage has to be recorded on a machine too. machine_ids = [ m.machine_id for m in machine_service.find_machines_for_operation_definition( operation.operation_definition_id) ] mainlog.debug("Authorized machine id's are : {}; you gave {}".format( machine_ids, machine_id)) if not machine_id and machine_ids: raise ServerException( ServerErrors.machine_not_compatible_with_operation, machine_id, str(machine_ids)) elif machine_id and machine_id not in machine_ids: raise ServerException( ServerErrors.machine_not_compatible_with_operation, machine_id, str(machine_ids)) if operation.imputable: # Make sure a taks is tied to the operation/machine if operation.task_id is None: mainlog.debug( "_make_task_for_operation_and_machine : create_task_on_operation" ) task_id = dao.task_dao.create_task_on_operation( operation_id, machine_id).task_id elif not operation.task_active: raise ServerException(ServerErrors.task_not_imputable, task_id) else: raise ServerException( ServerErrors.operation_non_imputable, u"'{} {}'".format( (operation.operation_definition_short_id or ""), (operation.operation_description or "")), str(operation.order_accounting_label) + (operation.order_part_label or '-')) return task_id
def update_name_and_description(self, document_id: int, name: str, description: str): if not name: raise ServerException(ServerErrors.file_name_cannot_be_empty) # file_name_cannot_be_empty # raise Exception("Name cannot be empty") mainlog.debug('Renaming doc:{} to:{} with description:{}'.format( document_id, name, description)) try: self._rename_file_in_storage(document_id, name) except Exception as ex: mainlog.error("Could not rename document {}".format(document_id)) mainlog.exception(ex) doc = session().query(Document).filter( Document.document_id == document_id).one() doc.description = description or "" doc.filename = name audit_trail_service.record("DOCUMENT_RENAMED", "", document_id, commit=False) session().commit()
def find_machine_by_id(self, machine_id): if self._cache_by_id == None: self._reset_cache() if machine_id in self._cache_by_id: return self._cache_by_id[machine_id] else: raise ServerException(ServerErrors.unknown_machine, machine_id)
def set_event_on_days(self, day_event: DayEvent, days_duration: list): """ Set an event on several days each time with a specific duration. :param day_event: :param days_duration: An array of pairs. Each pair is (date, duration). Each date must be unique. :return: """ day_max = date(1980, 1, 1) day_min = date(2050, 12, 31) mainlog.debug("set_event_on_days") mainlog.debug(days_duration) for day, duration in days_duration: day_min = min(day_min, day) day_max = max(day_max, day) db_events = session().query(DayEvent).filter( and_(DayEvent.employee_id == day_event.employee_id, DayEvent.event_type == day_event.event_type, DayEvent.date.between(day_min, day_max))).all() db_events_dates = dict(zip([e.date for e in db_events], db_events)) other_db_events = session().query(DayEvent.date, func.sum(DayEvent.duration).label("duration_sum")).\ filter( and_( DayEvent.employee_id == day_event.employee_id, DayEvent.event_type != day_event.event_type, DayEvent.date.between(day_min, day_max))).\ group_by(DayEvent.date).all() other_db_events_dates = dict([(e.date, e.duration_sum) for e in other_db_events]) for day, duration in days_duration: if day in other_db_events_dates and other_db_events_dates[ day] + duration > 1: raise ServerException(ServerErrors.too_much_off_time_on_a_day, date_to_dmy(day)) if day in db_events_dates: # Replace the old duration db_event = db_events_dates[day] db_event.duration = duration else: nu_event = DayEvent() nu_event.date = day nu_event.duration = duration nu_event.event_type = day_event.event_type nu_event.employee_id = day_event.employee_id session().add(nu_event) session().commit()
def record_pointage_on_unbillable(self, operation_definition_id: int, employee_id: int, action_time: datetime, location: str, action_kind: TaskActionReportType): mainlog.debug("record_pointage_on_unbillable ") opdef = dao.operation_definition_dao.find_by_id_frozen( operation_definition_id, commit=False, resilient=True) if not opdef: raise ServerException(ServerErrors.operation_definition_unknown, operation_definition_id) elif not opdef.imputable: raise ServerException( ServerErrors.operation_definition_not_imputable, opdef.short_id) task_id = dao.task_dao._get_task_for_non_billable( operation_definition_id) self._recordActionOnWorkTask(task_id, employee_id, action_time, location, action_kind)
def _barcodeToTask(self, barcode, additional_data): data = None try: data = BarCodeIdentifier.barcode_to_id(barcode) except Exception as ex: raise ServerException(ServerErrors.barcode_syntax_invalid, barcode) if data[0] == Operation: operation_id = data[1] machine_id = additional_data task_id, h = self._recordTaskOnOperation(operation_id, machine_id) return task_id, h elif data[0] == OperationDefinition: operation_definition_id = data[1] task_id, h = self._recordTaskOnNonBillable(operation_definition_id) return task_id, h elif data[0] == Order and data[1] == OperationDefinition: order_id = data[2] operation_definition_id = data[3] task_id, h = self._recordTaskOnOrder(order_id, operation_definition_id) # self.dao.task_dao.task_for_order(order_id,operation_definition_id) return task_id, h elif data[0] in (TaskActionReportType.day_in, TaskActionReportType.day_out): task_id = dao.task_action_report_dao.presence_task().task_id h = dict() h['task_type'] = 'TaskForPresence' h['action_kind'] = data[0] return task_id, h else: raise ServerException(ServerErrors.barcode_invalid, barcode)
def get_operation_definition_information(self, employee_id: int, operation_definition_id: int): """ Retrieve information about an operation. The operation is meant to be imputable. FIXME That is a problem : if the operation is not imputable, one should still be able to report an "end of task" (bu not "start task") """ opdef = dao.operation_definition_dao.find_by_id_frozen( operation_definition_id, commit=False, resilient=True) if not opdef: raise ServerException(ServerErrors.operation_definition_unknown, operation_definition_id) next_action_kind = dao.operation_definition_dao.find_next_action_for_employee_operation_definition( employee_id, operation_definition_id, at_time=datetime.now()) session().commit() return opdef, next_action_kind
def get_person_data(self, employee_id: int, start_date: date): """ Get a lot of information about a person : * person data (totally independent of the start_date) * presence hours per day (on a period of 4 days, sorted from the oldest to th emost recent) The data are given on a specific date. The data are collected over a period of 3 days before the given start date. WARNING : To make sure the presence information is correct, a presence TAR may have to be recorded before calling this. """ # FIXME I think about adding the activity hours instead of the presence hours if not start_date: raise ServerException(ServerErrors.invalid_parameter, 'start_date') mainlog.debug("get_person_data {}".format(employee_id)) # 120 calls # query+json 6.70s => 0.055s per call # start_date = date.today() + timedelta(-360) end_date = start_date start_date = end_date - timedelta(days=3) from koi.junkyard.services import services try: chrono_start() person = dao.employee_dao.find_by_id_frozen2(employee_id) # person = services.employees.find_by_id(employee_id) d = DictAsObj(person._asdict()) d.password = None # Don't give the password away ! chrono_click("clock_service : get_person_data - 1") activity_hours = dao.employee_dao.find_activity( employee_id, start_date, end_date) presence_hours = dao.employee_dao.find_person_presence_for_period( employee_id, start_date, end_date) # activity_hours = services.employees.find_activity(employee_id, start_date, end_date) # presence_hours = services.employees.find_person_presence_for_period(employee_id, start_date, end_date) chrono_click("clock_service : get_person_data - 2") mainlog.debug("get_person_data Done") d.activity_hours = activity_hours d.presence_hours = presence_hours return d except OperationalError as e: mainlog.debug("Raising Server exception") raise ServerException(ServerErrors.db_error, str(e)) except Exception as e: mainlog.debug("Raising Server exception-2") mainlog.exception(e) raise ServerException(ServerErrors.unknown_employee_id, employee_id)
def _load_operation_and_task_data(self, operation_id, machine_id): """ Load an operation and all its associated task_data The task data may not be there. """ mainlog.debug("_load_operation_and_task_data") # We outer join on the tasks to majke sure to read the operation # even if there's no task. q = session().query(Operation.operation_id, Order.accounting_label.label("order_accounting_label"), OrderPart.label.label("order_part_label"), OrderPart.description.label('order_part_description'), Customer.fullname.label('customer_name'), OperationDefinition.operation_definition_id, OperationDefinition.short_id.label("operation_definition_short_id"), Operation.description.label("operation_description"), Operation.position.label("operation_position"), and_(OperationDefinition.imputable == True, OperationDefinition.on_operation == True, Order.state == OrderStatusType.order_ready_for_production).label("imputable"), TaskOnOperation.task_id, TaskOnOperation.active.label("task_active")).\ join(ProductionFile).\ join(OrderPart).join(Order).\ join(OperationDefinition, OperationDefinition.operation_definition_id == Operation.operation_definition_id).\ join(Customer, Order.customer_id == Customer.customer_id).\ outerjoin(TaskOnOperation, and_( TaskOnOperation.operation_id == Operation.operation_id, TaskOnOperation.machine_id == machine_id)).\ options(lazyload('*')).\ filter( Operation.operation_id == operation_id) # None is for the outer join; "or" works, in [None, machine] doesn not. row = q.all() # We do a bit of result analysis to improve error reporting if len(row) > 1: raise Exception("_load_operation_and_task_data : Too many results") elif not row: c = session().query(Operation.operation_id, OperationDefinition.imputable, OperationDefinition.short_id.label("operation_definition_short_id"), OperationDefinition.on_operation, Order.accounting_label.label("order_accounting_label"), OrderPart.label.label("order_part_label"), OrderPart.state.label("order_part_state")).\ join(ProductionFile).\ join(OrderPart).\ join(Order).\ join(OperationDefinition, OperationDefinition.operation_definition_id == Operation.operation_definition_id).\ filter(Operation.operation_id == operation_id).first() if not c: raise ServerException(ServerErrors.operation_unknown, operation_id) elif not c.imputable: raise ServerException( ServerErrors.operation_definition_not_imputable, c.operation_definition_short_id) elif c.order_part_state != OrderStatusType.order_ready_for_production: raise ServerException( ServerErrors.order_part_not_in_production_unknown, "{}{}".format(c.order_accounting_label, c.order_part_label)) return row[0]
def print_order_report(dao, order_id, test_mode=False): global header_text,sub_header_text mainlog.debug('print_order_report order_id={}'.format(order_id)) o = dao.order_dao.find_by_id(order_id) header_text = u"{} [{}]".format(o.customer.fullname, o.customer_order_name) sub_header_text = u"Mode op\u00E9ratoire" big_order_nr = ParagraphStyle(name = "subtitle", fontName = 'Helvetica-Bold', fontSize=24, leading=26) topstyle = ParagraphStyle(name = "zou", fontName = 'Helvetica', fontSize=16) p = platypus.Paragraph(u"<b>Client {}, commande #{}</b>".format(o.customer.customer_id,o.accounting_label),topstyle) spacer = platypus.Spacer(1,50) centered = ParagraphStyle(name = "zou", fontName = 'Helvetica-bold', alignment=TA_CENTER, fontSize=14) #, borderWidth=1, borderColor=colors.black) s = ParagraphStyle(name = "zou", fontName = 'Helvetica', fontSize=14, leading=16) #, borderWidth=1, borderColor=colors.black) page_number_style = ParagraphStyle(name = "page_number_style", fontName = 'Helvetica', alignment=TA_RIGHT, fontSize=12, leading=12) #, borderWidth=1, borderColor=colors.black) opNumberStyle = ParagraphStyle(name = "zou", fontName = 'Helvetica-bold', alignment=TA_CENTER, fontSize=28,leading=28) #, borderWidth=1, borderColor=colors.black) complete_document = [] global satisfied satisfied = False sections.clear() headings_frame = HeadingsFrame(*basic_frame_dimensions) # complete_document.append(DocAssign("i",0)) for part in o.parts: # Build up a table with accompanying style # data_ops = [ ['Poste',None,u'Op\u00E9ration',None] ] # We build up a data model and the appropriate styling information titles_row = [] if part.production_file and len(part.production_file[0].operations) > 0: data_ops = [[Paragraph(part.human_identifier,big_order_nr), Paragraph(escape_html(part.description),subtitle_style)], [SubSectionNumber(part.human_identifier, page_number_style), Paragraph(_("Quantity : {} - Deadline: {}").format(part.qty, date_to_dmy(part.deadline)),subtitle_style)]] ts = platypus.TableStyle() ts.add('GRID',(0,0),(-1,-1),0.5,colors.black) ts.add('VALIGN', (0, 0), (-1, -1), 'TOP') ts.add('ALIGN', (0, 1), (-1, 1), 'RIGHT') start_mark = PageMarker() complete_document.append( start_mark) header_maker = HeaderMaker(data_ops, ts, col_widths=compute_strut([3.8*cm, None], A4[0]-1*cm)) complete_document.append(HeaderSetter( header_maker)) operation_ndx = 1 for op in part.production_file[0].operations: complete_document.append( KeepTogether( [ make_boxes(operation_ndx, op, test_mode), platypus.Spacer(1,0.1*cm) ] )) operation_ndx += 1 end_mark = PageMarker() complete_document.append( end_mark) sections[part.human_identifier] = (start_mark, end_mark) complete_document.append(HeaderSetter(None)) complete_document.append(PageBreak()) # complete_document.append(platypus.Spacer(1,30)) session().close() # FIXME Dirty if len(complete_document) > 0: filename = make_pdf_filename("OrderAndParts_{}_".format(order_id)) ladderDoc = basic_PDF(filename,body_frame=headings_frame) ladderDoc.subject = u"Mode op\u00E9ratoire" ladderDoc.title = u"Client {}".format(o.customer.customer_id) ladderDoc.multiBuild(complete_document,canvasmaker=NumberedCanvas) open_pdf(filename) return True else: raise ServerException( ServerErrors.printing_an_empty_report)