From 740783b9fd36e1b8fe0a81276bfa4cee1ab137fe Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 21 Jul 2021 08:35:18 +0200 Subject: [PATCH] adding generic documents model --- .../versions/7ecb8ff7abad_documents.py | 73 +++++++++++++++++++ ereuse_devicehub/resources/action/models.py | 17 +++++ ereuse_devicehub/resources/action/schemas.py | 4 + .../resources/action/views/documents.py | 63 ++++++++++++++++ .../resources/action/views/views.py | 5 ++ .../resources/documents/models.py | 47 ++++++++++++ .../resources/documents/schemas.py | 26 +++++++ 7 files changed, 235 insertions(+) create mode 100644 ereuse_devicehub/migrations/versions/7ecb8ff7abad_documents.py create mode 100644 ereuse_devicehub/resources/action/views/documents.py create mode 100644 ereuse_devicehub/resources/documents/models.py create mode 100644 ereuse_devicehub/resources/documents/schemas.py diff --git a/ereuse_devicehub/migrations/versions/7ecb8ff7abad_documents.py b/ereuse_devicehub/migrations/versions/7ecb8ff7abad_documents.py new file mode 100644 index 00000000..163da39b --- /dev/null +++ b/ereuse_devicehub/migrations/versions/7ecb8ff7abad_documents.py @@ -0,0 +1,73 @@ +"""documents + +Revision ID: 7ecb8ff7abad +Revises: 3a3601ac8224 +Create Date: 2021-07-19 14:46:42.375331 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utils +import citext +import teal + +from alembic import op +from alembic import context +from sqlalchemy.dialects import postgresql + + +# revision identifiers, used by Alembic. +revision = '7ecb8ff7abad' +down_revision = '3a3601ac8224' +branch_labels = None +depends_on = None + + +def get_inv(): + INV = context.get_x_argument(as_dictionary=True).get('inventory') + if not INV: + raise ValueError("Inventory value is not specified") + return INV + +def upgrade(): + # Document table + op.create_table('document', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Document recorded a change for \n this thing.\n '), + sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, comment='When Document created this.'), + sa.Column('type', sa.Unicode(), nullable=False), + sa.Column('date', sa.TIMESTAMP(timezone=True), nullable=True), + sa.Column('id_document', sa.Unicode(), nullable=True), + sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('file_name', sa.Unicode(), nullable=False), + sa.Column('file_hash', sa.Unicode(), nullable=False), + sa.Column('url', sa.Unicode(), nullable=False), + + sa.ForeignKeyConstraint(['owner_id'], ['common.user.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + op.create_index('generic_document_id', 'document', ['id'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') + op.create_index(op.f('ix_document_created'), 'document', ['created'], unique=False, schema=f'{get_inv()}') + op.create_index(op.f('ix_document_updated'), 'document', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_index('document_type_index', 'document', ['type'], unique=False, postgresql_using='hash', schema=f'{get_inv()}') + + + # ToErased table + op.create_table('to_erased', + # sa.Column('document_id', sa.BigInteger(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + # sa.ForeignKeyConstraint(['document_id'], [f'{get_inv()}.document.id'], ), + sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + + + +def downgrade(): + op.drop_table('to_erased', schema=f'{get_inv()}') + op.drop_table('document', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index d8bcb3ce..f9dfd45d 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1327,6 +1327,23 @@ class ToPrepare(ActionWithMultipleDevices): pass +class ToErased(ActionWithMultipleDevices): + """The device has been selected for insert one proof of erease disk. + """ + document_comment = """The user that gets the device due this deal.""" + # document_id = db.Column(BigInteger, + # db.ForeignKey('document.id', + # use_alter=True, + # name='document'), + # nullable=False) + # document = relationship('EraseDocument', + # backref=backref('actions', + # lazy=True, + # uselist=False, + # cascade=CASCADE_OWN), + # primaryjoin='ToErased.document_id == EraseDocument.id') + + class Prepare(ActionWithMultipleDevices): """Work has been performed to the device to a defined point of acceptance. diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 087af53d..f667eebc 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -430,6 +430,10 @@ class Prepare(ActionWithMultipleDevices): __doc__ = m.Prepare.__doc__ +class ToErased(ActionWithMultipleDevices): + __doc__ = m.ToErased.__doc__ + + class Live(ActionWithOneDevice): __doc__ = m.Live.__doc__ """ diff --git a/ereuse_devicehub/resources/action/views/documents.py b/ereuse_devicehub/resources/action/views/documents.py new file mode 100644 index 00000000..58d7b579 --- /dev/null +++ b/ereuse_devicehub/resources/action/views/documents.py @@ -0,0 +1,63 @@ +import copy + +from flask import g +from sqlalchemy.util import OrderedSet +from teal.marshmallow import ValidationError + +from ereuse_devicehub.db import db +from ereuse_devicehub.resources.action.models import (Trade, Confirm, ConfirmRevoke, + Revoke, RevokeDocument, ConfirmDocument, + ConfirmRevokeDocument) +from ereuse_devicehub.resources.user.models import User +from ereuse_devicehub.resources.action.models import ToErased +from ereuse_devicehub.resources.documents.models import EraseDocument +from ereuse_devicehub.resources.documents.schemas import EraseDocument as sh_document + + +class ErasedView(): + """Handler for manager the trade action register from post + + request_post = { + 'type': 'Trade', + 'devices': [device_id], + 'documents': [document_id], + 'userFrom': user2.email, + 'userTo': user.email, + 'price': 10, + 'date': "2020-12-01T02:00:00+00:00", + 'lot': lot['id'], + 'confirm': True, + } + + """ + + def __init__(self, data, schema): + self.schema = schema + self.insert_document(copy.copy(data)) + self.insert_action(copy.copy(data)) + + def post(self): + # import pdb; pdb.set_trace() + db.session().final_flush() + ret = self.schema.jsonify(self.erased) + ret.status_code = 201 + db.session.commit() + return ret + + def insert_document(self, data): + # import pdb; pdb.set_trace() + schema = sh_document() + [data.pop(x) for x in ['severity', 'devices', 'name', 'description']] + doc_data = schema.load(data) + doc_data['type'] = 'ToErased' + self.document = EraseDocument(**doc_data) + db.session.add(self.document) + db.session.commit() + + def insert_action(self, data): + import pdb; pdb.set_trace() + [data.pop(x, None) for x in ['url', 'documentId', 'filename', 'hash']] + self.data = self.schema.load(data) + # self.data['document'] = self.document + self.erased = ToErased(**self.data) + db.session.add(self.erased) diff --git a/ereuse_devicehub/resources/action/views/views.py b/ereuse_devicehub/resources/action/views/views.py index e51a8e7a..c42089a2 100644 --- a/ereuse_devicehub/resources/action/views/views.py +++ b/ereuse_devicehub/resources/action/views/views.py @@ -18,6 +18,7 @@ from ereuse_devicehub.resources.action.models import (Action, Snapshot, VisualTe Trade, Confirm, ConfirmRevoke, Revoke) from ereuse_devicehub.resources.action.views import trade as trade_view from ereuse_devicehub.resources.action.views.snapshot import SnapshotView, save_json, move_json +from ereuse_devicehub.resources.action.views.documents import ErasedView from ereuse_devicehub.resources.device.models import Device, Computer, DataStorage from ereuse_devicehub.resources.enums import Severity @@ -250,6 +251,10 @@ class ActionView(View): confirm_revoke = trade_view.ConfirmRevokeDocumentView(json, resource_def, self.schema) return confirm_revoke.post() + if json['type'] == 'ToErased': + erased = ErasedView(json, resource_def.schema) + return erased.post() + a = resource_def.schema.load(json) Model = db.Model._decl_class_registry.data[json['type']]() action = Model(**a) diff --git a/ereuse_devicehub/resources/documents/models.py b/ereuse_devicehub/resources/documents/models.py new file mode 100644 index 00000000..700cf59d --- /dev/null +++ b/ereuse_devicehub/resources/documents/models.py @@ -0,0 +1,47 @@ +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 ereuse_devicehub.resources.models import Thing, STR_SM_SIZE + +from sqlalchemy import BigInteger, Column, Sequence, Unicode +from teal.db import URL + + +class Document(Thing): + """This represent a generic document.""" + + 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, nullable=True) + date.comment = """The date of document, some documents need to have one date + """ + id_document = Column(CIText(), nullable=True) + id_document.comment = """The id of one document like invoice so they can be linked.""" + 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(), nullable=False) + file_name.comment = """This is the name of the file when user up the document.""" + file_hash = Column(db.CIText(), nullable=False) + file_hash.comment = """This is the hash of the file produced from frontend.""" + url = db.Column(URL(), nullable=False) + url.comment = """This is the url where resides the document.""" + + def __str__(self) -> str: + return '{0.file_name}'.format(self) + + +class EraseDocument(Document): + """This represent a document involved in a erase manual action. + This represent the proof. + + """ + pass diff --git a/ereuse_devicehub/resources/documents/schemas.py b/ereuse_devicehub/resources/documents/schemas.py new file mode 100644 index 00000000..636caf56 --- /dev/null +++ b/ereuse_devicehub/resources/documents/schemas.py @@ -0,0 +1,26 @@ +from marshmallow.fields import DateTime, Integer, validate +from teal.marshmallow import SanitizedStr, URL +from ereuse_devicehub.resources.schemas import Thing +from ereuse_devicehub.resources.documents import models as m +# from marshmallow import ValidationError, validates_schema + + +class EraseDocument(Thing): + __doc__ = m.EraseDocument.__doc__ + id = Integer(description=m.EraseDocument.id.comment, dump_only=True) + type = SanitizedStr(default='EraseDocument') + date = DateTime(data_key='endTime', + required=False, + description=m.EraseDocument.date.comment) + id_document = SanitizedStr(data_key='documentId', + default='', + description=m.EraseDocument.id_document.comment) + file_name = SanitizedStr(data_key='filename', + default='', + description=m.EraseDocument.file_name.comment, + validate=validate.Length(max=100)) + file_hash = SanitizedStr(data_key='hash', + default='', + description=m.EraseDocument.file_hash.comment, + validate=validate.Length(max=64)) + url = URL(description=m.EraseDocument.url.comment)