Example #1
0
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,
        }
Example #2
0
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,
            }
Example #3
0
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())
Example #4
0
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,
        )
Example #5
0
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,
        )