def save_template(self, file_handle, file_name): """ A new template document is added to our database. We ask for a file handle because we expect the file to come from the web server (that's the way cherrypy does it). :returns the doc id of the file """ mainlog.debug(u"save_template(): fh:{} fn:{}".format( file_handle, file_name)) document = TemplateDocument() document.filename = file_name document.upload_date = date.today() document.server_location = "DUMMY" document.file_size = 666 session().add(document) session().flush() # get an id document.server_location, document.file_size = self._copy_file_to_storage( file_handle, document.document_id, file_name) doc_id = document.document_id audit_trail_service.record("TEMPLATE_CREATED", "", document.document_id, commit=False) session().commit() mainlog.debug("Saved to template doc_id={}, bytes={}".format( doc_id, document.file_size)) return doc_id
def replace_template(self, template_id, file_handle, file_name): """ A new template document replaces an old one in our database. We ask for a file handle because we expect the file to come from the web server (that's the way cherrypy does it). """ mainlog.debug(u"replace_template(): doc_id:{}, fh:{} fn:{}".format( template_id, file_handle, file_name)) document = session().query(TemplateDocument).filter( TemplateDocument.template_document_id == template_id).one() document.filename = file_name document.upload_date = date.today() document.server_location = "DUMMY" document.file_size = 666 document.server_location, document.file_size = self._copy_file_to_storage( file_handle, document.document_id, file_name) doc_id = document.document_id # save for use after session's flushed audit_trail_service.record("TEMPLATE_REPLACED", "", document.document_id, commit=False) session().commit() mainlog.debug("Replaced template {}".format(doc_id)) return doc_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 copy_template_to_document(self, template_id): tpl = session().query(TemplateDocument).filter( TemplateDocument.template_document_id == template_id).one() doc_id = None with open(tpl.server_location, 'rb') as f: doc_id = self.save(None, f, tpl.filename, tpl.description) audit_trail_service.record("TEMPLATE_INSTANCIATED", "", template_id, commit=False) session().commit() return doc_id
def _set_order_state(self, order: Order, state): if state not in OrderStatusType: # This will catch None state as well raise Exception("The state {} is unknown".format(state)) if state == OrderStatusType.preorder_sent and order.sent_as_preorder is None: # Arriving to OrderStatusType.preorder_sent state order.sent_as_preorder = central_clock.today() elif order.state == OrderStatusType.preorder_sent and state == OrderStatusType.preorder_definition: # The user probably wants to clear an error order.sent_as_preorder = None if state in (OrderStatusType.order_completed, OrderStatusType.order_aborted): order.completed_date = central_clock.today() else: order.completed_date = None # Pay attention, the following assignments are not as simple # as they seem. That is, they'll trigger an update (of course). # Since there are constraints tying preorder_label, accounting_label # and state, one must be careful that they are updated all at once # in a single update statement. The following assignment on # accounting label is constructed so that SQLAlchemy produces # one sql update statement. See the doc at file:///C:/PORT-STC/opt/python/Doc/sqlalchemy/orm/session.html#embedding-sql-insert-update-expressions-into-a-flush # order.preorder_label = None if (state in (OrderStatusType.preorder_definition, OrderStatusType.preorder_sent)) and order.preorder_label is None: order.preorder_label = gaplessseq('preorder_id') mainlog.debug( "_set_order_state() setting order.preorder_label to {} 'cos sate is {}" .format(order.preorder_label, order.state)) elif (state not in (OrderStatusType.preorder_definition, OrderStatusType.preorder_sent, OrderStatusType. order_aborted)) and order.accounting_label is None: order.accounting_label = gaplessseq('order_id') mainlog.debug( "_set_labels_for_state() setting order.accouting_label {} 'cos sate is {}" .format(order.accounting_label, state)) order.state = state audit_trail_service.record("ORDER_STATE_CHANGED", "State transition to {}".format(state), order.order_id, commit=False)
def save(self, document_id, file_handle, file_name, description): """ If document id is None (or 0), a new document is added to our database. Else an old one is overwritten. We ask for a file handle because we expect the file to come from the web server (that's the way cherrypy does it). """ mainlog.debug(u"save(): id:{} fh:{} fn:{} d:{}".format( document_id, file_handle, file_name, description)) c = all_non_relation_columns(Document) document = None if document_id: document = session().query(*c).filter( Document.document_id == document_id).one() session().commit() else: document = Document() session().add(document) document.filename = file_name document.upload_date = date.today() document.description = description or "" # I need the id to store on the file system. # But to get the server_location, I need to store on the filesystem # => catch 22 document.server_location = "DUMMY" document.file_size = 666 if not document.document_id: session().flush() # get an id document.server_location, document.file_size = self._copy_file_to_storage( file_handle, document.document_id, file_name) doc_id = document.document_id audit_trail_service.record("DOCUMENT_CREATED", "", document.document_id, commit=False) session().commit() mainlog.debug("Saved to document {}".format(doc_id)) return doc_id
def transition_part_state(self, order_part: OrderPart, state): """ Transition an order part state, and apply all business decisions accordingly. :param order_part: An OrderPart (will usually be part of a sqla session) :param state: The new state. :return: """ assert isinstance(order_part, OrderPart) from koi.datalayer.DeclEnumP3 import EnumSymbol assert isinstance(state, EnumSymbol) if state != order_part.state: mainlog.debug("transition_part_state : {} --> {}".format( order_part, state)) if order_part.state and state not in self.order_part_possible_next_states( order_part.state): raise DataException( "Improper transition from state '{}' to state '{}'".format( order_part.state, state), DataException.IMPROPER_STATE_TRANSITION) mainlog.debug(u"Order part {} : transiion from {} -> {}".format( order_part.order_part_id, order_part.state, state)) order_part.state = state # So that means that if the user changes a past part (say january) # from completed to aborted, then the changed part "moves" to # today (and thus may move from on GUI view to another) if order_part.state in (OrderPartStateType.completed, OrderPartStateType.aborted): order_part.completed_date = central_clock.today() else: order_part.completed_date = None audit_trail_service.record("UPDATE_ORDER_PART", "Transition to {} for {}:{}".format( state.description, order_part.label, order_part.description), order_part.order_part_id, commit=False) else: mainlog.debug("transition_part_state : nothing to do")
def delete(self, document_id: int): # This is expected to work polymorphically on Documents' children. # So you can delete anything that inherits from Document. mainlog.debug("delete document id {}".format(document_id)) doc = session().query(Document).filter( Document.document_id == document_id).one() self._remove_file_from_storage(doc.document_id) session().delete( doc ) # Cascade to order_part / order <-> document association table audit_trail_service.record("DOCUMENT_DELETED", "", document_id, commit=False) session().commit()
def mark_as_non_conform(self, quality_event_dto, commit=True): part = self.dao.order_part_dao.find_by_id( quality_event_dto.order_part_id) # Create a non conformity event # self.dao.quality_dao.make(non_confomity_type, part.order_part_id, commit = False) self.dao.quality_dao.save_or_update(quality_event_dto) # Change the state of the order part self.transition_part_state(part, OrderPartStateType.non_conform) order_part = self.dao.order_part_dao.find_by_id( quality_event_dto.order_part_id) # will commit audit_trail_service.record( "UPDATE_ORDER_PART", "Non conformity : {} for {}:{}".format( quality_event_dto.kind.description, order_part.label, order_part.description), quality_event_dto.order_part_id)
def _update_supply_order_parts(self, actions, supply_order): """ Pay attention ! This will update the action array to reflect the order part that were merged The object in the actions don't necessarily have to be SQLA mapped object, they can be frozen as well. """ # FIXME replace supply_order by supply_order_id and merge # save supply order and update supply order parts # supply_order = session().query(SupplyOrder).filter(SupplyOrderPart.supply_order_id == supply_order_id).one() new_pos = 10000 mainlog.debug("Reloading order parts into SQLA's session") for i in range(len(actions)): action_type, op, op_ndx = actions[i] if action_type == DBObjectActionTypes.TO_UPDATE: op = defrost_to_session(op, SupplyOrderPart) elif action_type == DBObjectActionTypes.UNCHANGED: # I don't use merge because merge will trigger # a copy of the attributes. Which will in # turn result in an UPDATE being performed. # And that is useless (since it is unchanged). # Moreover it makes testing a bit harder because # I have to think about the session to make # test cases. op = self._find_by_id(op.supply_order_part_id) elif action_type == DBObjectActionTypes.TO_CREATE: op = defrost_to_session(op, SupplyOrderPart) mainlog.debug("supply_order.supply_order_id = {}".format( supply_order.supply_order_id)) op.supply_order_id = supply_order.supply_order_id op.position = new_pos new_pos += 1 elif action_type == DBObjectActionTypes.TO_DELETE: # Yes, we do need to merge the "TO_DELETE" ones. Because # SQLA will check against what it has in session. # SO if the part we want to destroy is already # in session and op is the same part but is not in # session, then SQLA will complain # op = session().merge(op) op = self._find_by_id(op.supply_order_part_id) supply_order.parts.remove( op) # We need the delete to cascade ! # session().delete(op) actions[i] = (action_type, op, op_ndx) mainlog.debug("At this point, there are {} parts in the order".format( len(supply_order.parts))) # Remember that when doing flush, nothing guarantees # that the DB statements will be sent to the DB # in the order there were done in Python. SQLA # may reorder them at will. # Handles positions : order far away pos = 100000 for action_type, op, op_ndx in actions: if action_type != DBObjectActionTypes.TO_DELETE: op.position = pos pos += 1 session().flush() # Handles positions : bring back in place pos = 1 for action_type, op, op_ndx in actions: if action_type != DBObjectActionTypes.TO_DELETE: # mainlog.debug(u"reposition {} {}".format(pos,op.description)) op.position = pos pos += 1 session().flush() self._recompute_position_labels(supply_order) session().commit() for action_type, op, op_ndx in actions: if action_type == DBObjectActionTypes.TO_CREATE: audit_trail_service.record("CREATE_SUPPLY_ORDER_PART", str(op), op.supply_order_part_id) if action_type == DBObjectActionTypes.TO_UPDATE: audit_trail_service.record("UPDATE_SUPPLY_ORDER_PART", str(op), op.supply_order_part_id) elif action_type == DBObjectActionTypes.TO_DELETE: # !!! Order id ! because order part id doesn't make sense after a delete :-) audit_trail_service.record("DELETE_SUPPLY_ORDER_PART", str(op), supply_order.supply_order_id) mainlog.debug(u"update_supply_order_parts -- done") return actions
def test_add(self): audit_trail_service.record("CUSTOMER_EDIT", "name=Google", 123, self.employee_id)