diff --git a/CHANGELOG.md b/CHANGELOG.md index 93602dea..2e3c4d45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ ml). - [addend] #102 adding endpoint for download metrics - [bugfix] #100 fixing bug of scheme live - [bugfix] #101 fixing bug when 2 users have one device and launch one live +- [changes] #114 clean blockchain of all models +- [remove] #114 remove proof system ## [1.0.3-beta] - [addend] #85 add mac of network adapter to device hid 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/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index ed402141..376ccd1b 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -65,10 +65,10 @@ class Dummy: with click_spinner.spinner(): out = runner.invoke('org', 'add', *self.ORG).output org_id = json.loads(out)['id'] - user1 = self.user_client('user@dhub.com', '1234', 'user1', '0xC79F7fE80B5676fe38D8187b79d55F7A61e702b2') - user2 = self.user_client('user2@dhub.com', '1234', 'user2', '0x56EbFdbAA98f52027A9776456e4fcD5d91090818') - user3 = self.user_client('user3@dhub.com', '1234', 'user3', '0xF88618956696aB7e56Cb7bc87d9848E921C4FDaA') - user4 = self.user_client('user4@dhub.com', '1234', 'user4', '0x37be35ae7eced44ca25e4683e98425fc7830a8a5') + user1 = self.user_client('user@dhub.com', '1234', 'user1') + user2 = self.user_client('user2@dhub.com', '1234', 'user2') + user3 = self.user_client('user3@dhub.com', '1234', 'user3') + user4 = self.user_client('user4@dhub.com', '1234', 'user4') # todo put user's agent into Org for id in self.TAGS: @@ -188,8 +188,8 @@ class Dummy: # For netbook: to preapre -> torepair -> to dispose -> disposed print('⭐ Done.') - def user_client(self, email: str, password: str, name: str, ethereum_address: str): - user = User(email=email, password=password, ethereum_address=ethereum_address) + def user_client(self, email: str, password: str, name: str): + user = User(email=email, password=password) user.individuals.add(Person(name=name)) db.session.add(user) diff --git a/ereuse_devicehub/migrations/versions/eca457d8b2a4_change_deliverynote.py b/ereuse_devicehub/migrations/versions/eca457d8b2a4_change_deliverynote.py new file mode 100644 index 00000000..0ce69c20 --- /dev/null +++ b/ereuse_devicehub/migrations/versions/eca457d8b2a4_change_deliverynote.py @@ -0,0 +1,158 @@ +"""change deliverynote + +Revision ID: eca457d8b2a4 +Revises: 0cbd839b09ef +Create Date: 2021-02-03 22:12:41.033661 + +""" +import citext +import sqlalchemy as sa +from alembic import op +from alembic import context +from sqlalchemy.dialects import postgresql + + +# revision identifiers, used by Alembic. +revision = 'eca457d8b2a4' +down_revision = '0cbd839b09ef' +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(): + op.drop_column('deliverynote', 'ethereum_address', schema=f'{get_inv()}') + op.alter_column('deliverynote', 'deposit', new_column_name='amount', schema=f'{get_inv()}') + op.alter_column('computer', 'deposit', new_column_name='amount', schema=f'{get_inv()}') + op.alter_column('lot', 'deposit', new_column_name='amount', schema=f'{get_inv()}') + op.drop_column('lot', 'deliverynote_address', schema=f'{get_inv()}') + op.drop_column('computer', 'deliverynote_address', schema=f'{get_inv()}') + op.drop_column('computer', 'ethereum_address', schema=f'{get_inv()}') + op.drop_column('lot', 'receiver_address', schema=f'{get_inv()}') + op.add_column('lot', sa.Column('receiver_address', citext.CIText(), + sa.ForeignKey('common.user.email'), nullable=True), + schema=f'{get_inv()}') + + op.drop_column('user', 'ethereum_address', schema='common') + + + 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.alter_column('deliverynote', 'amount', new_column_name='deposit', 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.alter_column('computer', 'amount', new_column_name='deposit', schema=f'{get_inv()}') + op.alter_column('lot', 'amount', new_column_name='deposit', schema=f'{get_inv()}') + + # ===== + op.add_column('computer', sa.Column('ethereum_address', citext.CIText(), nullable=True), schema=f'{get_inv()}') + op.add_column('user', sa.Column('ethereum_address', citext.CIText(), unique=True, nullable=True), schema='common') + + + op.drop_column('lot', 'receiver_address', schema=f'{get_inv()}') + op.add_column('lot', sa.Column('receiver_address', citext.CIText(), + sa.ForeignKey('common.user.ethereum_address'), 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/deliverynote/models.py b/ereuse_devicehub/resources/deliverynote/models.py index 03b11310..57eefe08 100644 --- a/ereuse_devicehub/resources/deliverynote/models.py +++ b/ereuse_devicehub/resources/deliverynote/models.py @@ -36,7 +36,7 @@ class Deliverynote(Thing): receiver = db.relationship(User, primaryjoin=lambda: Deliverynote.receiver_address == User.email) date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) date.comment = 'The date the DeliveryNote initiated' - deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) + amount = db.Column(db.Integer, check_range('amount', min=0, max=100), default=0) # The following fields are supposed to be 0:N relationships # to SnapshotDelivery entity. # At this stage of implementation they will treated as a @@ -46,7 +46,6 @@ class Deliverynote(Thing): transferred_devices = db.Column(db.ARRAY(db.Integer, dimensions=1), nullable=True) transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state.comment = TransferState.__doc__ - ethereum_address = db.Column(CIText(), unique=True, default=None) lot_id = db.Column(UUID(as_uuid=True), db.ForeignKey(Lot.id), nullable=False) @@ -55,14 +54,14 @@ class Deliverynote(Thing): lazy=True, primaryjoin=Lot.id == lot_id) - def __init__(self, document_id: str, deposit: str, date, + def __init__(self, document_id: str, amount: str, date, supplier_email: str, expected_devices: Iterable, transfer_state: TransferState) -> None: """Initializes a delivery note """ super().__init__(id=uuid.uuid4(), - document_id=document_id, deposit=deposit, date=date, + document_id=document_id, amount=amount, date=date, supplier_email=supplier_email, expected_devices=expected_devices, transfer_state=transfer_state) diff --git a/ereuse_devicehub/resources/deliverynote/models.pyi b/ereuse_devicehub/resources/deliverynote/models.pyi index 3ed6be5c..1a5714f9 100644 --- a/ereuse_devicehub/resources/deliverynote/models.pyi +++ b/ereuse_devicehub/resources/deliverynote/models.pyi @@ -24,13 +24,12 @@ class Lot(Thing): description = ... # type: Column all_devices = ... # type: relationship parents = ... # type: relationship - deposit = ... # type: Column + amount = ... # type: Column owner_address = ... # type: Column owner = ... # type: relationship transfer_state = ... # type: Column receiver_address = ... # type: Column receiver = ... # type: relationship - deliverynote_address = ... # type: Column def __init__(self, name: str, closed: bool = closed.default.arg) -> None: super().__init__() @@ -46,7 +45,6 @@ class Lot(Thing): self.owner_address = ... # type: UUID self.transfer_state = ... self.receiver_address = ... # type: str - self.deliverynote_address = ... # type: str def add_children(self, *children: Union[Lot, uuid.UUID]): pass diff --git a/ereuse_devicehub/resources/deliverynote/schemas.py b/ereuse_devicehub/resources/deliverynote/schemas.py index 58808f6d..f0dbecdc 100644 --- a/ereuse_devicehub/resources/deliverynote/schemas.py +++ b/ereuse_devicehub/resources/deliverynote/schemas.py @@ -19,9 +19,8 @@ class Deliverynote(Thing): supplier = NestedOn(s_user.User, dump_only=True) receiver = NestedOn(s_user.User, dump_only=True) date = f.DateTime('iso', required=True) - deposit = f.Integer(validate=f.validate.Range(min=0, max=100), - description=m.Deliverynote.deposit.__doc__) - ethereum_address = f.String(description='User identifier address inside the Blockchain') + amount = f.Integer(validate=f.validate.Range(min=0, max=100), + description=m.Deliverynote.amount.__doc__) expected_devices = f.List(f.Dict, required=True, data_key='expectedDevices') transferred_devices = f.List(f.Integer(), required=False, data_key='transferredDevices') transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) diff --git a/ereuse_devicehub/resources/deliverynote/views.py b/ereuse_devicehub/resources/deliverynote/views.py index 5a4670dd..1f21a6a1 100644 --- a/ereuse_devicehub/resources/deliverynote/views.py +++ b/ereuse_devicehub/resources/deliverynote/views.py @@ -28,21 +28,13 @@ class DeliverynoteView(View): return ret def patch(self, id): - patch_schema = self.resource_def.SCHEMA(only=('transfer_state', - 'ethereum_address'), partial=True) + patch_schema = self.resource_def.SCHEMA(only=('transfer_state'), partial=True) d = request.get_json(schema=patch_schema) dlvnote = Deliverynote.query.filter_by(id=id).one() # device_fields = ['transfer_state', 'deliverynote_address'] # computers = [x for x in dlvnote.transferred_devices if isinstance(x, Computer)] for key, value in d.items(): setattr(dlvnote, key, value) - # Transalate ethereum_address attribute - # devKey = key - # if key == 'ethereum_address': - # devKey = 'deliverynote_address' - # if devKey in device_fields: - # for dev in computers: - # setattr(dev, devKey, value) db.session.commit() return Response(status=204) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index a91beb5f..a9efc105 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -471,8 +471,7 @@ class Computer(Device): It is a subset of the Linux definition of DMI / DMI decode. """ - ethereum_address = Column(CIText(), unique=True, default=None) - deposit = Column(Integer, check_range('deposit', min=0, max=100), default=0) + amount = Column(Integer, check_range('amount', min=0, max=100), default=0) owner_id = db.Column(UUID(as_uuid=True), db.ForeignKey(User.id), nullable=False, @@ -484,7 +483,6 @@ class Computer(Device): db.ForeignKey(User.id), nullable=True) receiver = db.relationship(User, primaryjoin=receiver_id == User.id) - deliverynote_address = db.Column(CIText(), nullable=True) def __init__(self, *args, **kwargs) -> None: if args: diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index 9fa9665b..837a737f 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -141,11 +141,10 @@ class DisplayMixin: class Computer(DisplayMixin, Device): components = ... # type: Column chassis = ... # type: Column - deposit = ... # type: Column + amount = ... # type: Column owner_address = ... # type: Column transfer_state = ... # type: Column - receiver_address = ... # type: Column - deliverynote_address = ... # type: Column + receiver_id = ... # uuid: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) @@ -155,7 +154,6 @@ class Computer(DisplayMixin, Device): self.owner_address = ... # type: UUID self.transfer_state = ... self.receiver_address = ... # type: str - self.deliverynote_address = ... # type: str @property def actions(self) -> List: diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index ea5b3211..706b7e03 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -123,14 +123,12 @@ class Computer(Device): dump_only=True, collection_class=set, description=m.Computer.privacy.__doc__) - ethereum_address = SanitizedStr(validate=f.validate.Length(max=42)) - deposit = Integer(validate=f.validate.Range(min=0, max=100), - description=m.Computer.deposit.__doc__) + amount = Integer(validate=f.validate.Range(min=0, max=100), + description=m.Computer.amount.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') owner_id = UUID(data_key='ownerID') transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment) receiver_id = UUID(data_key='receiverID') - deliverynote_address = SanitizedStr(validate=f.validate.Length(max=42)) class Desktop(Computer): diff --git a/ereuse_devicehub/resources/device/views.py b/ereuse_devicehub/resources/device/views.py index fcb0e17f..3dde5e3b 100644 --- a/ereuse_devicehub/resources/device/views.py +++ b/ereuse_devicehub/resources/device/views.py @@ -106,7 +106,7 @@ class DeviceView(View): resource_def = app.resources['Computer'] # TODO check how to handle the 'actions_one' patch_schema = resource_def.SCHEMA( - only=['ethereum_address', 'transfer_state', 'deliverynote_address', 'actions_one'], partial=True) + only=['transfer_state', 'actions_one'], partial=True) json = request.get_json(schema=patch_schema) # TODO check how to handle the 'actions_one' json.pop('actions_one') diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index eb5a55d0..f8a9066b 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -63,7 +63,7 @@ class Lot(Thing): """All devices, including components, inside this lot and its descendants. """ - deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) + amount = db.Column(db.Integer, check_range('amount', min=0, max=100), default=0) owner_id = db.Column(UUID(as_uuid=True), db.ForeignKey(User.id), nullable=False, @@ -72,10 +72,10 @@ class Lot(Thing): transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state.comment = TransferState.__doc__ receiver_address = db.Column(CIText(), - db.ForeignKey(User.ethereum_address), - nullable=True) - receiver = db.relationship(User, primaryjoin=receiver_address == User.ethereum_address) - deliverynote_address = db.Column(CIText(), nullable=True) + db.ForeignKey(User.email), + nullable=False, + default=lambda: g.user.email) + receiver = db.relationship(User, primaryjoin=receiver_address == User.email) def __init__(self, name: str, closed: bool = closed.default.arg, description: str = None) -> None: diff --git a/ereuse_devicehub/resources/lot/models.pyi b/ereuse_devicehub/resources/lot/models.pyi index cf7c0769..f70078c7 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -24,13 +24,12 @@ class Lot(Thing): description = ... # type: Column all_devices = ... # type: relationship parents = ... # type: relationship - deposit = ... # type: Column + amount = ... # type: Column owner_address = ... # type: Column owner = ... # type: relationship transfer_state = ... # type: Column receiver_address = ... # type: Column receiver = ... # type: relationship - deliverynote_address = ... # type: Column def __init__(self, name: str, closed: bool = closed.default.arg) -> None: super().__init__() @@ -46,7 +45,6 @@ class Lot(Thing): self.owner_address = ... # type: UUID self.transfer_state = ... self.receiver_address = ... # type: str - self.deliverynote_address = ... # type: str def add_children(self, *children: Union[Lot, uuid.UUID]): pass diff --git a/ereuse_devicehub/resources/lot/schemas.py b/ereuse_devicehub/resources/lot/schemas.py index c30f3890..72e49efe 100644 --- a/ereuse_devicehub/resources/lot/schemas.py +++ b/ereuse_devicehub/resources/lot/schemas.py @@ -19,8 +19,8 @@ class Lot(Thing): children = NestedOn('Lot', many=True, dump_only=True) parents = NestedOn('Lot', many=True, dump_only=True) url = URL(dump_only=True, description=m.Lot.url.__doc__) - deposit = f.Integer(validate=f.validate.Range(min=0, max=100), - description=m.Lot.deposit.__doc__) + amount = f.Integer(validate=f.validate.Range(min=0, max=100), + description=m.Lot.amount.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') owner_id = f.UUID(data_key='ownerID') transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index e0ac1691..e9395ff5 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -41,11 +41,11 @@ class LotView(View): def patch(self, id): patch_schema = self.resource_def.SCHEMA(only=( - 'name', 'description', 'transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'devices', + 'name', 'description', 'transfer_state', 'receiver_address', 'amount', 'devices', 'owner_address'), partial=True) l = request.get_json(schema=patch_schema) lot = Lot.query.filter_by(id=id).one() - device_fields = ['transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'owner_address'] + device_fields = ['transfer_state', 'receiver_address', 'amount', 'owner_address'] computers = [x for x in lot.all_devices if isinstance(x, Computer)] for key, value in l.items(): setattr(lot, key, value) @@ -142,9 +142,9 @@ class LotView(View): if path: cls._p(node['nodes'], path) - def get_lot_deposit(self, l: Lot): - """Return lot deposit value""" - return l.deposit + def get_lot_amount(self, l: Lot): + """Return lot amount value""" + return l.amount def change_state(self): """Change state of Lot""" 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/ereuse_devicehub/resources/user/models.py b/ereuse_devicehub/resources/user/models.py index 98a25598..0ab670c8 100644 --- a/ereuse_devicehub/resources/user/models.py +++ b/ereuse_devicehub/resources/user/models.py @@ -25,11 +25,10 @@ class User(Thing): backref=db.backref('users', lazy=True, collection_class=set), secondary=lambda: UserInventory.__table__, collection_class=set) - ethereum_address = Column(CIText(), unique=True, default=None) # todo set restriction that user has, at least, one active db - def __init__(self, email, password=None, ethereum_address=None, inventories=None) -> None: + def __init__(self, email, password=None, inventories=None) -> None: """Creates an user. :param email: :param password: @@ -38,7 +37,7 @@ class User(Thing): inventory. """ inventories = inventories or {Inventory.current} - super().__init__(email=email, password=password, ethereum_address=ethereum_address, inventories=inventories) + super().__init__(email=email, password=password, inventories=inventories) def __repr__(self) -> str: return ''.format(self) @@ -52,11 +51,6 @@ class User(Thing): """The individual associated for this database, or None.""" return next(iter(self.individuals), None) - @property - def get_ethereum_address(self): - """The ethereum address in Blockchain, or None.""" - return next(iter(self.ethereum_address), None) - class UserInventory(db.Model): """Relationship between users and their inventories.""" diff --git a/ereuse_devicehub/resources/user/models.pyi b/ereuse_devicehub/resources/user/models.pyi index 15874464..6e8d03b9 100644 --- a/ereuse_devicehub/resources/user/models.pyi +++ b/ereuse_devicehub/resources/user/models.pyi @@ -17,7 +17,6 @@ class User(Thing): password = ... # type: Column token = ... # type: Column inventories = ... # type: relationship - ethereum_address = ... # type: Column def __init__(self, email: str, password: str = None, inventories: Set[Inventory] = None) -> None: @@ -28,7 +27,6 @@ class User(Thing): self.individuals = ... # type: Set[Individual] self.token = ... # type: UUID self.inventories = ... # type: Set[Inventory] - self.ethereum_address = ... # type: str @property def individual(self) -> Union[Individual, None]: diff --git a/ereuse_devicehub/resources/user/schemas.py b/ereuse_devicehub/resources/user/schemas.py index 00d022ad..e8d0d768 100644 --- a/ereuse_devicehub/resources/user/schemas.py +++ b/ereuse_devicehub/resources/user/schemas.py @@ -19,7 +19,6 @@ class User(Thing): description='Use this token in an Authorization header to access the app.' 'The token can change overtime.') inventories = NestedOn(Inventory, many=True, dump_only=True) - ethereum_address = String(description='User identifier address inside the Blockchain') def __init__(self, only=None, diff --git a/ereuse_devicehub/resources/user/views.py b/ereuse_devicehub/resources/user/views.py index 19624b80..7053eea7 100644 --- a/ereuse_devicehub/resources/user/views.py +++ b/ereuse_devicehub/resources/user/views.py @@ -15,7 +15,7 @@ class UserView(View): def login(): # We use custom schema as we only want to parse a subset of user - user_s = g.resource_def.SCHEMA(only=('email', 'password', 'ethereum_address')) # type: UserS + user_s = g.resource_def.SCHEMA(only=('email', 'password')) # type: UserS # noinspection PyArgumentList u = request.get_json(schema=user_s) user = User.query.filter_by(email=u['email']).one_or_none() diff --git a/tests/files/devices_deliverynote.csv b/tests/files/devices_deliverynote.csv new file mode 100644 index 00000000..2e7de40b --- /dev/null +++ b/tests/files/devices_deliverynote.csv @@ -0,0 +1,10 @@ +Box;;Nº Inventary;Type of device;Sub Type;Brand;Model;serial number +;;N006536;PC;;Acer;Veriton M480G;PSV75EZ0070170002C14j00 +;;N006549;PC ;;Acer;Veriton M480G;PSV75EZ0070170003714j00 +;;N006541;PC ;;Acer;Veriton M480G; +;;N006556;PC;;Acer;Veriton M480G;PSV75EZ0070170001D14j00 +;;N006538;PC;;Acer;Veriton M480G; +;;N007465;PC;;Acer;Veriton M480G;PSV75EZ0070170003A14j00 +;;;PC;;Acer;Veriton M480G;PSV75EZ007017000361800 +;;N006537;PC;;Acer;Veriton M480G;PSV75EZ0070170002214j00 +;;N006530;PC;;Acer;Veriton M480G;PSV75EZ0070170000314j00 diff --git a/tests/test_action.py b/tests/test_action.py index d6f99494..421a5c80 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -279,6 +279,8 @@ def test_live(user: UserClient, client: Client, app: Devicehub): assert action_live[0].serial_number == 'wd-wx11a80w7430' assert action_live[0].licence_version == '1.0' assert str(action_live[0].snapshot_uuid) == acer['uuid'] + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -303,6 +305,8 @@ def test_live_example(user: UserClient, client: Client, app: Devicehub): action_live = [a for a in db_device.actions if a.type == 'Live'] assert len(action_live) == 1 assert str(action_live[0].snapshot_uuid) == acer['uuid'] + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -330,6 +334,8 @@ def test_live_two_users(user: UserClient, user2: UserClient, client: Client, app action_live = [a for a in db_device.actions if a.type == 'Live'] assert len(action_live) == 1 assert str(action_live[0].snapshot_uuid) == acer['uuid'] + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -363,6 +369,8 @@ def test_live_two_allocated(user: UserClient, user2: UserClient, client: Client, live, _ = client.post(acer, res=models.Live, status=422) message = 'Expected only one Device but multiple where found' assert live['message'] == message + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -394,6 +402,8 @@ def test_live_without_TestDataStorage(user: UserClient, client: Client, app: Dev assert live['description'] == description db_live = models.Live.query.filter_by(id=live['id']).one() assert db_live.usage_time_hdd is None + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -421,6 +431,8 @@ def test_live_without_hdd_1(user: UserClient, client: Client, app: Devicehub): acer['licence_version'] = '1.0.0' response, _ = client.post(acer, res=models.Live, status=404) assert "The There aren't any disk in this device" in response['message'] + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -448,6 +460,8 @@ def test_live_without_hdd_2(user: UserClient, client: Client, app: Devicehub): acer['licence_version'] = '1.0.0' response, _ = client.post(acer, res=models.Live, status=404) assert "The There aren't any disk in this device" in response['message'] + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -482,6 +496,8 @@ def test_live_without_hdd_3(user: UserClient, client: Client, app: Devicehub): db_live = models.Live.query.filter_by(id=live['id']).one() assert str(db_live.usage_time_hdd) == '195 days, 12:00:00' assert str(db_live.usage_time_allocate) == '0:00:00' + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -516,6 +532,8 @@ def test_live_with_hdd_with_old_time(user: UserClient, client: Client, app: Devi db_live = models.Live.query.filter_by(id=live['id']).one() assert str(db_live.usage_time_hdd) == '191 days, 8:00:00' assert str(db_live.usage_time_allocate) == '0:00:00' + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -546,6 +564,8 @@ def test_live_search_last_allocate(user: UserClient, client: Client, app: Device acer['components'][7]['actions'] = actions live, _ = client.post(acer, res=models.Live) assert live['usageTimeAllocate'] == 1000 + tmp_snapshots = app.config['TMP_LIVES'] + shutil.rmtree(tmp_snapshots) @pytest.mark.mvp @@ -580,7 +600,7 @@ def test_save_live_json(app: Devicehub, user: UserClient, client: Client): snapshot = {'debug': ''} if files: - path_snapshot = os.path.join(path_dir_base, files[0]) + path_snapshot = os.path.join(path_dir_base, files[-1]) with open(path_snapshot) as file_snapshot: snapshot = json.loads(file_snapshot.read()) diff --git a/tests/test_basic.py b/tests/test_basic.py index c770f9af..bcab605e 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']) == 123 + assert len(docs['definitions']) == 117 diff --git a/tests/test_deliverynote.py b/tests/test_deliverynote.py new file mode 100644 index 00000000..9940e14f --- /dev/null +++ b/tests/test_deliverynote.py @@ -0,0 +1,38 @@ +import os +import ipaddress +import json +import shutil +import copy +import pytest +from datetime import datetime +from dateutil.tz import tzutc +from ereuse_devicehub.client import UserClient +from ereuse_devicehub.devicehub import Devicehub +from ereuse_devicehub.resources.deliverynote.models import Deliverynote +from tests import conftest + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_simple_deliverynote(user: UserClient, app: Devicehub): + """ + This test create only one deliverinote with the expected Devices + """ + inventory = [{'n_inventory': 'N006536', + 'type': 'PC', + 'brand': 'Acer', + 'model': 'Veriton M480G', + 'serial_number': 'PSV75EZ0070170002C14j00' + }] + note = {'date': datetime(2020, 2, 14, 23, 0, tzinfo=tzutc()), + 'documentID': 'DocBBE001', + 'amount': 0, + 'transfer_state': "Initial", + 'expectedDevices': inventory, + 'supplierEmail': user.user['email']} + + deliverynote, _ = user.post(note, res=Deliverynote) + db_note = Deliverynote.query.filter_by(id=deliverynote['id']).one() + + assert deliverynote['documentID'] == note['documentID'] + assert user.user['email'] in db_note.lot.name diff --git a/tests/test_device.py b/tests/test_device.py index d9170c5c..851698fb 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -125,9 +125,7 @@ def test_physical_properties(): } assert pc.physical_properties == { 'chassis': ComputerChassis.Tower, - 'deliverynote_address': None, - 'deposit': 0, - 'ethereum_address': None, + 'amount': 0, 'manufacturer': 'bar', 'model': 'foo', 'receiver_id': None, @@ -252,7 +250,7 @@ def test_sync_execute_register_desktop_existing_no_tag(): **conftest.file('pc-components.db')['device']) # Create a new transient non-db object # 1: device exists on DB db_pc = Sync().execute_register(pc) - pc.deposit = 0 + pc.amount = 0 pc.owner_id = db_pc.owner_id pc.transfer_state = TransferState.Initial assert pc.physical_properties == db_pc.physical_properties