diff --git a/ereuse_devicehub/config.py b/ereuse_devicehub/config.py index cc4a65f5..449f104d 100644 --- a/ereuse_devicehub/config.py +++ b/ereuse_devicehub/config.py @@ -9,7 +9,7 @@ from teal.enums import Currency from teal.utils import import_resource from ereuse_devicehub.resources import action, agent, deliverynote, inventory, \ - lot, proof, tag, user + lot, tag, user from ereuse_devicehub.resources.device import definitions from ereuse_devicehub.resources.documents import documents from ereuse_devicehub.resources.enums import PriceSoftware @@ -26,7 +26,6 @@ class DevicehubConfig(Config): import_resource(agent), import_resource(lot), import_resource(deliverynote), - import_resource(proof), import_resource(documents), import_resource(inventory), import_resource(versions), diff --git a/ereuse_devicehub/migrations/versions/eca457d8b2a4_change_deliverynote.py b/ereuse_devicehub/migrations/versions/eca457d8b2a4_change_deliverynote.py index e35d87e7..f437b0cd 100644 --- a/ereuse_devicehub/migrations/versions/eca457d8b2a4_change_deliverynote.py +++ b/ereuse_devicehub/migrations/versions/eca457d8b2a4_change_deliverynote.py @@ -5,12 +5,11 @@ Revises: 0cbd839b09ef Create Date: 2021-02-03 22:12:41.033661 """ -from alembic import context -from alembic import op -import sqlalchemy as sa -import sqlalchemy_utils import citext -import teal +import sqlalchemy as sa +from alembic import op +from alembic import context +from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. @@ -30,9 +29,101 @@ def upgrade(): op.drop_column('deliverynote', 'ethereum_address', schema=f'{get_inv()}') op.drop_column('computer', 'deliverynote_address', schema=f'{get_inv()}') op.drop_column('lot', 'deliverynote_address', schema=f'{get_inv()}') + op.drop_table('proof_function', schema=f'{get_inv()}') + op.drop_table('proof_data_wipe', schema=f'{get_inv()}') + op.drop_table('proof_transfer', schema=f'{get_inv()}') + op.drop_table('proof_reuse', schema=f'{get_inv()}') + op.drop_table('proof_recycling', schema=f'{get_inv()}') + op.drop_index(op.f('ix_proof_updated'), table_name='proof', schema=f'{get_inv()}') + op.drop_index(op.f('ix_proof_created'), table_name='proof', schema=f'{get_inv()}') + op.drop_table('proof', schema=f'{get_inv()}') def downgrade(): op.add_column('deliverynote', sa.Column('ethereum_address', citext.CIText(), nullable=True), schema=f'{get_inv()}') op.add_column('computer', sa.Column('deliverynote_address', citext.CIText(), nullable=True), schema=f'{get_inv()}') op.add_column('lot', sa.Column('deliverynote_address', citext.CIText(), nullable=True), schema=f'{get_inv()}') + op.create_table('proof', + sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub 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 Devicehub created this.'), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('type', sa.Unicode(), nullable=False), + sa.Column('ethereum_hash', citext.CIText(), nullable=False), + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + op.create_index(op.f('ix_proof_created'), 'proof', ['created'], unique=False, schema=f'{get_inv()}') + op.create_index(op.f('ix_proof_updated'), 'proof', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_table('proof_recycling', + sa.Column('collection_point', citext.CIText(), nullable=False), + sa.Column('date', sa.DateTime(), nullable=False), + sa.Column('contact', citext.CIText(), nullable=False), + sa.Column('ticket', citext.CIText(), nullable=False), + sa.Column('gps_location', citext.CIText(), nullable=False), + sa.Column('recycler_code', citext.CIText(), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + + # Proof reuse table + op.create_table('proof_reuse', + sa.Column('receiver_segment', citext.CIText(), nullable=False), + sa.Column('id_receipt', citext.CIText(), nullable=False), + sa.Column('supplier_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('receiver_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('price', sa.Integer(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), + sa.ForeignKeyConstraint(['receiver_id'], ['common.user.id'], ), + sa.ForeignKeyConstraint(['supplier_id'], ['common.user.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + + # Proof transfer table + op.create_table('proof_transfer', + sa.Column('supplier_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('receiver_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('deposit', sa.Integer(), nullable=True), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), + sa.ForeignKeyConstraint(['receiver_id'], ['common.user.id'], ), + sa.ForeignKeyConstraint(['supplier_id'], ['common.user.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + + + # ProofDataWipe table + op.create_table('proof_data_wipe', + sa.Column('date', sa.DateTime(), nullable=False), + sa.Column('result', sa.Boolean(), nullable=False, comment='Identifies proof datawipe as a result.'), + sa.Column('proof_author_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('erasure_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint(['erasure_id'], [f'{get_inv()}.erase_basic.id'], ), + sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), + sa.ForeignKeyConstraint(['proof_author_id'], ['common.user.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + + # PRoofFuntion + op.create_table('proof_function', + sa.Column('disk_usage', sa.Integer(), nullable=True), + sa.Column('proof_author_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('rate_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.proof.id'], ), + sa.ForeignKeyConstraint(['proof_author_id'], ['common.user.id'], ), + sa.ForeignKeyConstraint(['rate_id'], [f'{get_inv()}.rate.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) diff --git a/ereuse_devicehub/resources/proof/__init__.py b/ereuse_devicehub/resources/proof/__init__.py deleted file mode 100644 index ef3a68a6..00000000 --- a/ereuse_devicehub/resources/proof/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -from teal.resource import Converters, Resource - -from ereuse_devicehub.resources.proof import schemas -from ereuse_devicehub.resources.proof.views import ProofView - - -class ProofDef(Resource): - SCHEMA = schemas.Proof - VIEW = ProofView - # AUTH = True - AUTH = False - ID_CONVERTER = Converters.uuid - - -class ProofTransferDef(ProofDef): - VIEW = None - SCHEMA = schemas.ProofTransfer - - -class ProofDataWipeDef(ProofDef): - VIEW = None - SCHEMA = schemas.ProofDataWipe - - -class ProofFunction(ProofDef): - VIEW = None - SCHEMA = schemas.ProofFunction - - -class ProofReuse(ProofDef): - VIEW = None - SCHEMA = schemas.ProofReuse - - -class ProofRecycling(ProofDef): - VIEW = None - SCHEMA = schemas.ProofRecycling diff --git a/ereuse_devicehub/resources/proof/models.py b/ereuse_devicehub/resources/proof/models.py deleted file mode 100644 index 632ec194..00000000 --- a/ereuse_devicehub/resources/proof/models.py +++ /dev/null @@ -1,149 +0,0 @@ -"""This file contains all proofs related to actions - -""" - -from datetime import datetime -from uuid import uuid4 - -from boltons import urlutils -from citext import CIText -from flask import g -from sqlalchemy import BigInteger, Column, ForeignKey, Unicode -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.ext.declarative import declared_attr -from sqlalchemy.orm import backref, relationship -from teal.db import CASCADE_OWN, INHERIT_COND, POLYMORPHIC_ID, \ - POLYMORPHIC_ON -from teal.resource import url_for_resource - -from ereuse_devicehub.db import db -from ereuse_devicehub.resources.action.models import EraseBasic, Rate -from ereuse_devicehub.resources.device.models import Device -from ereuse_devicehub.resources.models import Thing -from ereuse_devicehub.resources.user import User - - -class JoinedTableMixin: - # noinspection PyMethodParameters - @declared_attr - def id(cls): - return Column(UUID(as_uuid=True), ForeignKey(Proof.id), primary_key=True) - - -class Proof(Thing): - """Proof over an action. - - """ - id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4) - type = Column(Unicode, nullable=False) - ethereum_hash = Column(CIText(), default='', nullable=False) - device_id = db.Column(BigInteger, - db.ForeignKey(Device.id), - nullable=False) - device = db.relationship(Device, - backref=db.backref('proofs_device', uselist=True, lazy=True), - lazy=True, - primaryjoin=Device.id == device_id) - - @property - def url(self) -> urlutils.URL: - """The URL where to GET this proof.""" - return urlutils.URL(url_for_resource(Proof, item_id=self.id)) - - # noinspection PyMethodParameters - @declared_attr - def __mapper_args__(cls): - """Defines inheritance. - - From `the guide `_ - """ - args = {POLYMORPHIC_ID: cls.t} - if cls.t == 'Proof': - args[POLYMORPHIC_ON] = cls.type - # noinspection PyUnresolvedReferences - if JoinedTableMixin in cls.mro(): - args[INHERIT_COND] = cls.id == Proof.id - return args - - def __init__(self, **kwargs) -> None: - # sortedset forces us to do this before calling our parent init - super().__init__(**kwargs) - - def __repr__(self): - return '<{0.t} {0.id} >'.format(self) - - -class ProofTransfer(JoinedTableMixin, Proof): - supplier_id = db.Column(UUID(as_uuid=True), - db.ForeignKey(User.id), - nullable=False, - default=lambda: g.user.id) - supplier = db.relationship(User, primaryjoin=lambda: ProofTransfer.supplier_id == User.id) - receiver_id = db.Column(UUID(as_uuid=True), - db.ForeignKey(User.id), - nullable=False) - receiver = db.relationship(User, primaryjoin=lambda: ProofTransfer.receiver_id == User.id) - deposit = Column(db.Integer, default=0) - - -class ProofDataWipe(JoinedTableMixin, Proof): - # erasure_type = Column(CIText(), default='', nullable=False) - date = Column(db.DateTime, nullable=False, default=datetime.utcnow) - result = Column(db.Boolean, default=False, nullable=False) - result.comment = """Identifies proof datawipe as a result.""" - proof_author_id = Column(UUID(as_uuid=True), - db.ForeignKey(User.id), - nullable=False, - default=lambda: g.user.id) - proof_author = relationship(User, primaryjoin=lambda: ProofDataWipe.proof_author_id == User.id) - erasure_id = Column(UUID(as_uuid=True), ForeignKey(EraseBasic.id), nullable=False) - erasure = relationship(EraseBasic, - backref=backref('proof_datawipe', - lazy=True, - uselist=False, - cascade=CASCADE_OWN), - primaryjoin=EraseBasic.id == erasure_id) - - -class ProofFunction(JoinedTableMixin, Proof): - disk_usage = Column(db.Integer, default=0) - proof_author_id = Column(UUID(as_uuid=True), - db.ForeignKey(User.id), - nullable=False, - default=lambda: g.user.id) - proof_author = db.relationship(User, primaryjoin=lambda: ProofFunction.proof_author_id == User.id) - rate_id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), nullable=False) - rate = relationship(Rate, - backref=backref('proof_function', - lazy=True, - uselist=False, - cascade=CASCADE_OWN), - primaryjoin=Rate.id == rate_id) - - -class ProofReuse(JoinedTableMixin, Proof): - receiver_segment = Column(CIText(), default='', nullable=False) - id_receipt = Column(CIText(), default='', nullable=False) - supplier_id = db.Column(UUID(as_uuid=True), - db.ForeignKey(User.id), - # nullable=False, - # default=lambda: g.user.id) - nullable=True) - supplier = db.relationship(User, primaryjoin=lambda: ProofReuse.supplier_id == User.id) - receiver_id = db.Column(UUID(as_uuid=True), - db.ForeignKey(User.id), - # nullable=False) - nullable=True) - receiver = db.relationship(User, primaryjoin=lambda: ProofReuse.receiver_id == User.id) - price = Column(db.Integer) - - -class ProofRecycling(JoinedTableMixin, Proof): - collection_point = Column(CIText(), default='', nullable=False) - date = Column(db.DateTime, nullable=False, default=datetime.utcnow) - contact = Column(CIText(), default='', nullable=False) - ticket = Column(CIText(), default='', nullable=False) - gps_location = Column(CIText(), default='', nullable=False) - recycler_code = Column(CIText(), default='', nullable=False) diff --git a/ereuse_devicehub/resources/proof/schemas.py b/ereuse_devicehub/resources/proof/schemas.py deleted file mode 100644 index 2714e5b8..00000000 --- a/ereuse_devicehub/resources/proof/schemas.py +++ /dev/null @@ -1,70 +0,0 @@ -from marshmallow import fields as f -from marshmallow import fields as f -from marshmallow.fields import Boolean, DateTime, Integer, String, UUID -from marshmallow.validate import Length -from teal.marshmallow import SanitizedStr, URL - -from ereuse_devicehub.marshmallow import NestedOn -from ereuse_devicehub.resources.action import schemas as s_action -from ereuse_devicehub.resources.device import schemas as s_device -from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE -from ereuse_devicehub.resources.proof import models as m -from ereuse_devicehub.resources.schemas import Thing -from ereuse_devicehub.resources.user import schemas as s_user - - -class Proof(Thing): - __doc__ = m.Proof.__doc__ - id = UUID(dump_only=True) - ethereum_hash = SanitizedStr(default='', validate=Length(max=STR_BIG_SIZE), - data_key="ethereumHash", required=True) - url = URL(dump_only=True, description=m.Proof.url.__doc__) - device_id = Integer(load_only=True, data_key='deviceID') - device = NestedOn(s_device.Device, dump_only=True) - - -class ProofTransfer(Proof): - __doc__ = m.ProofTransfer.__doc__ - deposit = Integer(validate=f.validate.Range(min=0, max=100)) - supplier_id = UUID(load_only=True, required=True, data_key='supplierID') - receiver_id = UUID(load_only=True, required=True, data_key='receiverID') - - -class ProofDataWipe(Proof): - __doc__ = m.ProofDataWipe.__doc__ - # erasure_type = String(default='', data_key='erasureType') - date = DateTime('iso', required=True) - result = Boolean(required=True) - proof_author_id = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), - load_only=True, required=True, data_key='proofAuthorID') - proof_author = NestedOn(s_user.User, dump_only=True) - erasure = NestedOn(s_action.EraseBasic, only_query='id', data_key='erasureID') - - -class ProofFunction(Proof): - __doc__ = m.ProofFunction.__doc__ - disk_usage = Integer(validate=f.validate.Range(min=0, max=100), data_key='diskUsage') - proof_author_id = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), - load_only=True, required=True, data_key='proofAuthorID') - proof_author = NestedOn(s_user.User, dump_only=True) - rate = NestedOn(s_action.Rate, required=True, - only_query='id', data_key='rateID') - - -class ProofReuse(Proof): - __doc__ = m.ProofReuse.__doc__ - receiver_segment = String(default='', data_key='receiverSegment', required=True) - id_receipt = String(default='', data_key='idReceipt', required=True) - supplier_id = UUID(load_only=True, required=False, data_key='supplierID') - receiver_id = UUID(load_only=True, required=False, data_key='receiverID') - price = Integer(required=True) - - -class ProofRecycling(Proof): - __doc__ = m.ProofRecycling.__doc__ - collection_point = SanitizedStr(default='', data_key='collectionPoint', required=True) - date = DateTime('iso', required=True) - contact = SanitizedStr(default='', required=True) - ticket = SanitizedStr(default='', required=True) - gps_location = SanitizedStr(default='', data_key='gpsLocation', required=True) - recycler_code = SanitizedStr(default='', data_key='recyclerCode', required=True) diff --git a/ereuse_devicehub/resources/proof/views.py b/ereuse_devicehub/resources/proof/views.py deleted file mode 100644 index ec4754d5..00000000 --- a/ereuse_devicehub/resources/proof/views.py +++ /dev/null @@ -1,36 +0,0 @@ -from distutils.version import StrictVersion - -from flask import current_app as app, request, jsonify -from teal.marshmallow import ValidationError -from teal.resource import View - -from ereuse_devicehub.db import db - -SUPPORTED_WORKBENCH = StrictVersion('11.0') - - -class ProofView(View): - def post(self): - """Posts batches of proofs.""" - json = request.get_json(validate=False) - if not json: - raise ValidationError('JSON is not correct.') - # todo there should be a way to better get subclassess resource - # defs - proofs = list() - if json['batch']: - for prf in json['proofs']: - resource_def = app.resources[prf['type']] - p = resource_def.schema.load(prf) - Model = db.Model._decl_class_registry.data[prf['type']]() - proof = Model(**p) - db.session.add(proof) - proofs.append(resource_def.schema.dump(proof)) - db.session().final_flush() - db.session.commit() - response = jsonify({ - 'items': proofs, - 'url': request.path - }) - response.status_code = 201 - return response diff --git a/tests/test_basic.py b/tests/test_basic.py index 53cda3c4..34cb6596 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -80,7 +80,6 @@ def test_api_docs(client: Client): '/pack-of-screwdrivers/{dev1_id}/merge/{dev2_id}', '/printers/{dev1_id}/merge/{dev2_id}', '/processors/{dev1_id}/merge/{dev2_id}', - '/proofs/', '/rackets/{dev1_id}/merge/{dev2_id}', '/ram-modules/{dev1_id}/merge/{dev2_id}', '/recreations/{dev1_id}/merge/{dev2_id}', @@ -119,4 +118,4 @@ def test_api_docs(client: Client): 'scheme': 'basic', 'name': 'Authorization' } - assert len(docs['definitions']) == 124 + assert len(docs['definitions']) == 118