class PrinterTransaction(db.Model): """ The `PrinterTransaction` model code representing the messages sent to a :class:`despinassy.Printer.Printer`. The transaction of a printer can either be control messages or print query to output content like parts from the printer. """ __tablename__ = "printer_transaction" id = db.Column(db.Integer, primary_key=True, autoincrement=True) printer_id = db.Column(db.Integer, db.ForeignKey("printer.id")) printer = relationship("Printer") """:class:`despinassy.Printer.Printer` where the transaction happened""" # part_id = db.Column(db.Integer, db.ForeignKey('part.id'), unique=True) # part = relationship('Part') destination = db.Column(db.String(50)) origin = db.Column(db.Enum(IpcOrigin), nullable=False) """ Device that created this transaction. See :class:`despinassy.ipc.IpcOrigin` for more information. """ device = db.Column(db.String(50)) """ String precising the origin of the originator of the transaction. """ msg_type = db.Column(db.Integer, default=IpcMessageType.PRINT) """ Type of the message received by the printer. See :class:`despinassy.ipc.IpcOrigin` for more information. """ barcode = db.Column(db.String(50), nullable=False) """Barcode of the part the message refer to""" name = db.Column(db.String(120), nullable=False) """Name of the part the message refer to""" number = db.Column(db.Integer, default=1) """Number of output required by the printer""" created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) def to_dict(self): return { "id": self.id, "barcode": self.barcode, "name": self.name, "number": self.number, "origin": self.origin, "device": self.device, "created_at": self.created_at, }
class ScannerTransaction(db.Model): """ The `ScannerTransaction` model code representing the messages sent by the :class:`despinassy.Scanner.Scanner`. """ __tablename__ = "scanner_transaction" id = db.Column(db.Integer, primary_key=True, autoincrement=True) scanner_id = db.Column(db.Integer, db.ForeignKey("scanner.id")) scanner = relationship("Scanner") """:class:`despinassy.Scanner.Scanner` origin of the transaction""" mode = db.Column(db.Enum(ScannerModeEnum), nullable=False) """ Mode of the :class:`despinassy.Scanner.Scanner` at the moment of the transaction """ quantity = db.Column(db.Float, default=1) """Quantity attached to the value logged by this transaction""" value = db.Column(db.String(50), nullable=False) """Input or scanned value""" created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) def to_dict(self, full=False): if full: return { "id": self.id, "scanner": self.scanner_id, "mode": int(self.mode), "quantity": self.quantity, "value": self.value, "created_at": self.created_at, } else: return { "id": self.id, "mode": int(self.mode), "quantity": self.quantity, "value": self.value, "created_at": self.created_at, }
class Inventory(db.Model): """ The Inventory model code associate a number and a unit to an existing :class:`despinassy.Part`. Inventory represent the current stock of a :class:`despinassy.Part` at a given time. """ __tablename__ = "inventory" __table_args__ = (db.UniqueConstraint("part_id", "session_id"), ) id = db.Column(db.Integer, primary_key=True, autoincrement=True) quantity = db.Column(db.Float, default=0) """Current quantity of a :class:`despinassy.Part` in stock""" unit = db.Column(db.Enum(InventoryUnitEnum), default=InventoryUnitEnum.PIECES) """The unit of quantity""" part_id = db.Column(db.Integer, db.ForeignKey("part.id", ondelete="CASCADE")) part = relationship("Part") """Part associated with this inventory entry""" session_id = db.Column( db.Integer, db.ForeignKey("inventory_session.id", ondelete="CASCADE")) session = relationship("InventorySession") """Session associated with this inventory entry""" created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) def __init__(self, **kwargs): kwargs["session"] = InventorySession.query.order_by( InventorySession.created_at.desc()).first() super().__init__(**kwargs) def __repr__(self): return "<Inventory id=%i count=%i barcode='%s'>" % ( self.id, self.counter, self.part.barcode, ) def add(self, number=1): self.quantity += int(number) def to_dict(self): return { "id": self.id, "session": self.session_id, "part": self.part.to_dict(), "quantity": self.quantity, "unit": str(self.unit), } @staticmethod def last_session_entries(): """ Return the inventory entries from the last session. """ last_session = InventorySession.last() return Inventory.query.filter(Inventory.session == last_session) @staticmethod def archive(): """ Archive the current inventory by creating a new :class:`despinassy.inventory.InventorySession`. """ db.session.add(InventorySession()) db.session.commit() @staticmethod def retrieve_inventory_from_barcode(barcode): return (db.session.query(Inventory).join(Part).filter( Part.barcode == barcode).first()) @staticmethod def _export_csv(delimiter=","): strio = io.StringIO(newline=None) columns = [ "id", "part_name", "part_barcode", "quantity", "unit", "created_at", "updated_at", ] writer = csv.DictWriter(strio, fieldnames=columns, delimiter=delimiter, lineterminator="\n") writer.writeheader() for i in Inventory.last_session_entries().all(): row = { "id": i.id, "part_name": i.part.name, "part_barcode": i.part.barcode, "quantity": i.quantity, "unit": i.unit, "created_at": str(i.created_at), "updated_at": str(i.updated_at), } writer.writerow(row) return strio @staticmethod def export_csv(path): """Export the inventory entries to a '.csv' file :param path: The location of the '.csv' file to save """ with open(path, "w") as csvfile: csvfile.write(Inventory._export_csv().getvalue())
class Printer(db.Model): """ The `Printer` model code. Printers entry are devices that can output parts in a defined dialect. This model holds the information about this output device. A `Printer` can either be something virtual that will just output the result to a console or a physical device like a Zebra sticker printer. """ __tablename__ = "printer" id = db.Column(db.Integer, primary_key=True, autoincrement=True) type = db.Column(db.Enum(PrinterTypeEnum), nullable=False) """ Type of printer device. See :class:`despinassy.Printer.PrinterTypeEnum` for more information. """ available = db.Column(db.Boolean) """ Whether or not the `Printer` is currently available to print something. For instance if a printer of type `PrinterTypeEnum.STATIC` is not connected this boolean will be listed as false. """ width = db.Column(db.Integer) """Width of the output""" height = db.Column(db.Integer) """Height of the output""" dialect = db.Column(db.Enum(PrinterDialectEnum), nullable=False) """ Print form of the output of the printer. See :class:`despinassy.Printer.PrinterDialectEnum` for more information. """ name = db.Column(db.String(50), nullable=False) """User defined common name for this printer""" redis_id = db.Column(db.Integer, db.ForeignKey("channel.id")) redis = relationship("Channel") """Channel the printer listen for incoming message""" settings = db.Column(db.JSON) """Settings dependant on printer type""" transactions = relationship( "PrinterTransaction", order_by="desc(PrinterTransaction.created_at)", back_populates="printer", ) """List of transaction sent to this printer""" hidden = db.Column(db.Boolean, default=False) """Is the printer hidden to the user.""" created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) updated_at = db.Column(db.DateTime, onupdate=datetime.datetime.utcnow) @validates("redis") def validate_redis(self, key, value): c = Channel.query.filter(Channel.name == value) if c.count(): c = c.first() else: try: c = Channel(name=value) db.session.add(c) db.session.commit() except IntegrityError: db.session.rollback() c = Channel.query.filter(Channel.name == value).first() return c def to_dict(self, full=False): if full: return { "id": self.id, "type": self.type, "available": self.available, "width": self.width, "height": self.height, "dialect": self.dialect, "name": self.name, "redis": str(self.redis), "settings": json.loads(self.settings), "transactions": [t.to_dict() for t in self.transactions], "created_at": self.created_at, "updated_at": self.updated_at, "hidden": self.hidden, } else: return { "id": self.id, "type": self.type, "available": self.available, "width": self.width, "height": self.height, "dialect": self.dialect, "name": self.name, "redis": str(self.redis), "settings": json.loads(self.settings), "created_at": self.created_at, "updated_at": self.updated_at, "hidden": self.hidden, } def add_transaction(self, **kwargs): """Helper to create a new :class:`despinassy.Printer.PrinterTransaction` Someone should always use this helper function to create a new :class:`despinassy.Printer.PrinterTransaction` instead of creating one by hand. """ self.updated_at = datetime.datetime.utcnow() pt = PrinterTransaction(printer=self, **kwargs) return pt def __repr__(self): return "<Printer id=%i type=%i name='%s' redis='%s' settings='%s'>" % ( self.id, self.type, self.name, str(self.redis), self.settings, )
class Scanner(db.Model): """ The `Scanner` model code. Scanners are devices that can input/scan barcodes. Scanners can be physical device (USB, bluetooth) or virtual like the input from STDIN or the input from a webapp. """ __tablename__ = "scanner" id = db.Column(db.Integer, primary_key=True, autoincrement=True) type = db.Column(db.Enum(ScannerTypeEnum), nullable=False) """ Type of scanner device. See :class:`despinassy.Scanner.ScannerTypeEnum` for more information. """ mode = db.Column(db.Enum(ScannerModeEnum), default=ScannerModeEnum.PRINTMODE, nullable=False) """ Current mode of the scanner device. See :class:`despinassy.Scanner.ScannerModeEnum` for more information. """ available = db.Column(db.Boolean) """ Whether or not the `Scanner` is currently available to scan. For instance if a usb scanner is disconnected this boolean will be set to false. """ name = db.Column(db.String(50), unique=True) """User defined common name for this scanner""" redis_id = db.Column(db.Integer, db.ForeignKey("channel.id")) redis = relationship("Channel") """Channel the scanner send message to""" settings = db.Column(db.JSON) """Settings dependant on scanner type""" transactions = relationship( "ScannerTransaction", order_by="desc(ScannerTransaction.created_at)", back_populates="scanner", ) """List of transaction made by this scanner""" hidden = db.Column(db.Boolean, default=False) """Is the scanner hidden to the user.""" created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) updated_at = db.Column(db.DateTime, onupdate=datetime.datetime.utcnow) @validates("redis") def validate_redis(self, key, value): if isinstance(value, str): c = Channel.query.filter_by(name=value).first() if c is None: try: c = Channel(name=value) db.session.add(c) db.session.commit() except IntegrityError: db.session.rollback() c = Channel.query.filter(Channel.name == value).first() elif isinstance(value, Channel): c = value else: raise Exception("Not valid redis") return c def to_dict(self, full=False): if full: return { "id": self.id, "type": self.type, "name": self.name, "redis": str(self.redis), "settings": json.loads(self.settings), "mode": self.mode, "available": self.available, "created_at": self.created_at, "updated_at": self.updated_at, "transactions": [t.to_dict() for t in self.transactions], "hidden": self.hidden, } else: return { "id": self.id, "type": self.type, "name": self.name, "redis": str(self.redis), "settings": json.loads(self.settings), "mode": self.mode, "available": self.available, "created_at": self.created_at, "updated_at": self.updated_at, "hidden": self.hidden, } def add_transaction(self, **kwargs): """Helper to create a new :class:`despinassy.Scanner.ScannerTransaction` Always use this helper function to create a new :class:`despinassy.Scanner.ScannerTransaction` instead of creating one by hand. """ self.updated_at = datetime.datetime.utcnow() st = ScannerTransaction(scanner=self, **kwargs) return st def __repr__(self): return "<Scanner id=%i type=%i name='%s' redis='%s' settings='%s'>" % ( self.id, self.type, self.name, str(self.redis), self.settings, )