base for documents

This commit is contained in:
Cayo Puigdefabregas 2021-05-13 13:35:46 +02:00
parent 5222f2ceb7
commit c21de78982
7 changed files with 179 additions and 16 deletions

View File

@ -296,9 +296,10 @@ class ActionDevice(db.Model):
primary_key=True) primary_key=True)
class ActionWithMultipleDocuments(Action): class ActionWithMultipleDocuments(ActionWithMultipleDevices):
# pass
documents = relationship(Document, documents = relationship(Document,
backref=backref('actions_multiple', lazy=True, **_sorted_actions), backref=backref('actions_multiple_docs', lazy=True, **_sorted_actions),
secondary=lambda: ActionDocument.__table__, secondary=lambda: ActionDocument.__table__,
order_by=lambda: Document.id, order_by=lambda: Document.id,
collection_class=OrderedSet) collection_class=OrderedSet)
@ -1448,7 +1449,7 @@ class CancelReservation(Organize):
"""The act of cancelling a reservation.""" """The act of cancelling a reservation."""
class Confirm(JoinedTableMixin, ActionWithMultipleDevices): class Confirm(JoinedTableMixin, ActionWithMultipleDocuments):
"""Users confirm the one action trade this confirmation it's link to trade """Users confirm the one action trade this confirmation it's link to trade
and the devices that confirm and the devices that confirm
""" """
@ -1488,7 +1489,7 @@ class ConfirmRevoke(Confirm):
return '<{0.t} {0.id} accepted by {0.user}>'.format(self) return '<{0.t} {0.id} accepted by {0.user}>'.format(self)
class Trade(JoinedTableMixin, ActionWithMultipleDevices, ActionWithMultipleDocuments): class Trade(JoinedTableMixin, ActionWithMultipleDocuments):
"""Trade actions log the political exchange of devices between users. """Trade actions log the political exchange of devices between users.
Every time a trade action is performed, the old user looses its Every time a trade action is performed, the old user looses its
political possession, for example ownership, in favor of another political possession, for example ownership, in favor of another

View File

@ -499,7 +499,6 @@ class ConfirmRevoke(ActionWithMultipleDevices):
class Trade(ActionWithMultipleDevices): class Trade(ActionWithMultipleDevices):
__doc__ = m.Trade.__doc__ __doc__ = m.Trade.__doc__
document_id = SanitizedStr(validate=Length(max=STR_SIZE), data_key='documentID', required=False)
date = DateTime(data_key='date', required=False) date = DateTime(data_key='date', required=False)
price = Float(required=False, data_key='price') price = Float(required=False, data_key='price')
user_to_id = SanitizedStr(validate=Length(max=STR_SIZE), data_key='userTo', missing='', user_to_id = SanitizedStr(validate=Length(max=STR_SIZE), data_key='userTo', missing='',

View File

@ -0,0 +1,10 @@
from teal.resource import Converters, Resource
from ereuse_devicehub.resources.tradedocument import schemas
from ereuse_devicehub.resources.tradedocument.views import DocumentView
class TradeDocumentDef(Resource):
SCHEMA = schemas.Document
VIEW = DocumentView
AUTH = True
ID_CONVERTER = Converters.string

View File

@ -0,0 +1,115 @@
import os
from itertools import chain
from citext import CIText
from flask import current_app as app, g
from sqlalchemy.dialects.postgresql import UUID
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.user.models import User
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing, listener_reset_field_updated_in_actual_time
from sqlalchemy import BigInteger, Boolean, Column, Float, ForeignKey, Integer, \
Sequence, SmallInteger, Unicode, inspect, text
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import ColumnProperty, backref, relationship, validates
from sqlalchemy.util import OrderedSet
from sqlalchemy_utils import ColorType
from teal.db import CASCADE_DEL, POLYMORPHIC_ID, POLYMORPHIC_ON, \
check_lower, check_range
from teal.resource import url_for_resource
from ereuse_devicehub.resources.utils import hashcode
from ereuse_devicehub.resources.enums import BatteryTechnology, CameraFacing, ComputerChassis, \
DataStorageInterface, DisplayTech, PrinterTechnology, RamFormat, RamInterface, Severity, TransferState
class Document(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)
file_name = Column(db.CIText())
file_name.comment = """This is the name of the file when user up the document."""
file_name_disk = Column(db.CIText())
file_name_disk.comment = """This is the name of the file as devicehub save in server."""
__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 path_to_file(self) -> str:
"""The path of one file is defined by the owner, file_name and created time.
"""
base = app.config['PATH_DOCUMENTS_STORAGE']
file_name = "{0.date}-{0.filename}".format(self)
base = os.path.join(base, g.user.email, file_name)
return sorted(self.actions_multiple_docs, key=lambda x: x.created)
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)

View File

@ -0,0 +1,14 @@
from marshmallow.fields import DateTime, Integer
from teal.marshmallow import SanitizedStr
from ereuse_devicehub.resources.schemas import Thing
from ereuse_devicehub.resources.tradedocument import models as m
class Document(Thing):
__doc__ = m.Document.__doc__
id = Integer(description=m.Document.id.comment, dump_only=True)
date = DateTime(required=False, description=m.Document.date.comment)
id_document = SanitizedStr(default='', description=m.Document.id_document.comment)
description = SanitizedStr(default='', description=m.Document.description.comment)
file_name = SanitizedStr(default='', description=m.Document.file_name.comment)

View File

@ -0,0 +1,35 @@
import marshmallow
from flask import g, current_app as app, render_template, request, Response
from flask.json import jsonify
from flask_sqlalchemy import Pagination
from marshmallow import fields, fields as f, validate as v, Schema as MarshmallowSchema
from teal.resource import View
from ereuse_devicehub import auth
from ereuse_devicehub.db import db
from ereuse_devicehub.query import SearchQueryParser, things_response
from ereuse_devicehub.resources.tradedocument.models import Document
class DocumentView(View):
# @auth.Auth.requires_auth
def one(self, id: str):
document = Document.query.filter_by(id=id).first()
return self.schema.jsonify(document)
# @auth.Auth.requires_auth
def post(self):
"""Posts an action."""
json = request.get_json(validate=False)
resource_def = app.resources[json['type']]
a = resource_def.schema.load(json)
Model = db.Model._decl_class_registry.data[json['type']]()
action = Model(**a)
db.session.add(action)
db.session().final_flush()
ret = self.schema.jsonify(action)
ret.status_code = 201
db.session.commit()
return ret

View File

@ -56,7 +56,6 @@ def test_offer_without_to(user: UserClient):
'userFrom': user.email, 'userFrom': user.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot['id'], 'lot': lot['id'],
'confirm': False, 'confirm': False,
'code': 'MAX' 'code': 'MAX'
@ -84,7 +83,6 @@ def test_offer_without_to(user: UserClient):
'userFrom': user.email, 'userFrom': user.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot['id'], 'lot': lot['id'],
'confirm': False, 'confirm': False,
'code': 'MAX' 'code': 'MAX'
@ -107,7 +105,6 @@ def test_offer_without_to(user: UserClient):
'userFrom': user.email, 'userFrom': user.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot2.id, 'lot': lot2.id,
'confirm': False, 'confirm': False,
'code': 'MAX' 'code': 'MAX'
@ -138,7 +135,6 @@ def test_offer_without_from(user: UserClient, user2: UserClient):
'userTo': user2.email, 'userTo': user2.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot.id, 'lot': lot.id,
'confirm': False, 'confirm': False,
'code': 'MAX' 'code': 'MAX'
@ -183,7 +179,6 @@ def test_offer_without_users(user: UserClient):
'devices': [device.id], 'devices': [device.id],
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot.id, 'lot': lot.id,
'confirm': False, 'confirm': False,
'code': 'MAX' 'code': 'MAX'
@ -217,7 +212,6 @@ def test_offer(user: UserClient):
'userTo': user2.email, 'userTo': user2.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot.id, 'lot': lot.id,
'confirm': True, 'confirm': True,
} }
@ -244,7 +238,6 @@ def test_offer_without_devices(user: UserClient):
'userTo': user2.email, 'userTo': user2.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot['id'], 'lot': lot['id'],
'confirm': True, 'confirm': True,
} }
@ -272,7 +265,6 @@ def test_endpoint_confirm(user: UserClient, user2: UserClient):
'userTo': user2.email, 'userTo': user2.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot['id'], 'lot': lot['id'],
'confirm': True, 'confirm': True,
} }
@ -313,7 +305,6 @@ def test_confirm_revoke(user: UserClient, user2: UserClient):
'userTo': user2.email, 'userTo': user2.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot['id'], 'lot': lot['id'],
'confirm': True, 'confirm': True,
} }
@ -392,7 +383,6 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient):
'userTo': user.email, 'userTo': user.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot['id'], 'lot': lot['id'],
'confirm': True, 'confirm': True,
} }
@ -580,7 +570,6 @@ def test_confirmRevoke(user: UserClient, user2: UserClient):
'userTo': user.email, 'userTo': user.email,
'price': 10, 'price': 10,
'date': "2020-12-01T02:00:00+00:00", 'date': "2020-12-01T02:00:00+00:00",
'documentID': '1',
'lot': lot['id'], 'lot': lot['id'],
'confirm': True, 'confirm': True,
} }