141 lines
4.9 KiB
Python
141 lines
4.9 KiB
Python
import copy
|
|
from citext import CIText
|
|
from flask import g
|
|
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
from ereuse_devicehub.db import db
|
|
from ereuse_devicehub.resources.user.models import User
|
|
from sortedcontainers import SortedSet
|
|
from ereuse_devicehub.resources.models import Thing
|
|
|
|
from sqlalchemy import BigInteger, Column, Sequence
|
|
from sqlalchemy.orm import backref
|
|
from teal.db import CASCADE_OWN, URL
|
|
|
|
from ereuse_devicehub.resources.enums import Severity
|
|
|
|
|
|
_sorted_documents = {
|
|
'order_by': lambda: TradeDocument.created,
|
|
'collection_class': SortedSet
|
|
}
|
|
|
|
class TradeDocument(Thing):
|
|
"""This represent a document involved in a trade action.
|
|
Every document is added to a lot.
|
|
When this lot is converted in one trade, the action trade is added to the document
|
|
and the action trade need to be confirmed for the both users of the trade.
|
|
This confirmation can be revoked and this revoked need to be ConfirmRevoke for have
|
|
some efect.
|
|
|
|
This documents can be invoices or list of devices or certificates of erasure of
|
|
one disk.
|
|
|
|
Like a Devices one document have actions and is possible add or delete of one lot
|
|
if this lot don't have a trade
|
|
|
|
The document is saved in the database
|
|
|
|
"""
|
|
|
|
id = Column(BigInteger, Sequence('device_seq'), primary_key=True)
|
|
id.comment = """The identifier of the device for this database. Used only
|
|
internally for software; users should not use this.
|
|
"""
|
|
# type = Column(Unicode(STR_SM_SIZE), nullable=False)
|
|
date = Column(db.DateTime)
|
|
date.comment = """The date of document, some documents need to have one date
|
|
"""
|
|
id_document = Column(CIText())
|
|
id_document.comment = """The id of one document like invoice so they can be linked."""
|
|
description = Column(db.CIText())
|
|
description.comment = """A description of document."""
|
|
owner_id = db.Column(UUID(as_uuid=True),
|
|
db.ForeignKey(User.id),
|
|
nullable=False,
|
|
default=lambda: g.user.id)
|
|
owner = db.relationship(User, primaryjoin=owner_id == User.id)
|
|
lot_id = db.Column(UUID(as_uuid=True),
|
|
db.ForeignKey('lot.id'),
|
|
nullable=False)
|
|
lot = db.relationship('Lot',
|
|
backref=backref('documents',
|
|
lazy=True,
|
|
cascade=CASCADE_OWN,
|
|
**_sorted_documents),
|
|
primaryjoin='TradeDocument.lot_id == Lot.id')
|
|
lot.comment = """Lot to which the document is associated"""
|
|
file_name = Column(db.CIText())
|
|
file_name.comment = """This is the name of the file when user up the document."""
|
|
file_hash = Column(db.CIText())
|
|
file_hash.comment = """This is the hash of the file produced from frontend."""
|
|
url = db.Column(URL())
|
|
url.comment = """This is the url where resides the document."""
|
|
|
|
__table_args__ = (
|
|
db.Index('document_id', id, postgresql_using='hash'),
|
|
# db.Index('type_doc', type, postgresql_using='hash')
|
|
)
|
|
|
|
@property
|
|
def actions(self) -> list:
|
|
"""All the actions where the device participated, including:
|
|
|
|
1. Actions performed directly to the device.
|
|
2. Actions performed to a component.
|
|
3. Actions performed to a parent device.
|
|
|
|
Actions are returned by descending ``created`` time.
|
|
"""
|
|
return sorted(self.actions_multiple_docs, key=lambda x: x.created)
|
|
|
|
@property
|
|
def trading(self):
|
|
"""The trading state, or None if no Trade action has
|
|
ever been performed to this device. This extract the posibilities for to do"""
|
|
|
|
confirm = 'Confirm'
|
|
to_confirm = 'ToConfirm'
|
|
revoke = 'Revoke'
|
|
|
|
if not self.actions:
|
|
return
|
|
|
|
actions = copy.copy(self.actions)
|
|
actions = list(reversed(actions))
|
|
ac = actions[0]
|
|
|
|
if ac.type == confirm:
|
|
return confirm
|
|
|
|
if ac.type == revoke:
|
|
return revoke
|
|
|
|
if ac.type == confirm:
|
|
if ac.user == self.owner:
|
|
return confirm
|
|
return to_confirm
|
|
|
|
def last_action_of(self, *types):
|
|
"""Gets the last action of the given types.
|
|
|
|
:raise LookupError: Device has not an action of the given type.
|
|
"""
|
|
try:
|
|
# noinspection PyTypeHints
|
|
actions = self.actions
|
|
actions.sort(key=lambda x: x.created)
|
|
return next(e for e in reversed(actions) if isinstance(e, types))
|
|
except StopIteration:
|
|
raise LookupError('{!r} does not contain actions of types {}.'.format(self, types))
|
|
|
|
def _warning_actions(self, actions):
|
|
return sorted(ev for ev in actions if ev.severity >= Severity.Warning)
|
|
|
|
|
|
def __lt__(self, other):
|
|
return self.id < other.id
|
|
|
|
def __str__(self) -> str:
|
|
return '{0.file_name}'.format(self)
|