From f600a6edde91093c7c632d7148e19c044c0036d5 Mon Sep 17 00:00:00 2001 From: "jordi.nadeu" Date: Mon, 9 Dec 2019 17:24:24 +0100 Subject: [PATCH 01/29] first iteration integration with blockchain --- ereuse_devicehub/resources/action/models.py | 26 +++ ereuse_devicehub/resources/action/models.pyi | 4 + ereuse_devicehub/resources/action/schemas.py | 9 ++ ereuse_devicehub/resources/device/models.py | 3 +- .../documents/templates/documents/layout.html | 2 +- ereuse_devicehub/resources/lot/dag.sql | 3 - ereuse_devicehub/resources/lot/models.py | 12 ++ ereuse_devicehub/resources/lot/models.pyi | 12 ++ tests/files/acer.happy.battery.snapshot.yaml | 151 ++++++++++++++++++ tests/test_rate.py | 1 + tests/test_snapshot.py | 11 +- 11 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 tests/files/acer.happy.battery.snapshot.yaml diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index b0bc435f..4e732ed2 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1445,6 +1445,32 @@ class Receive(JoinedTableMixin, ActionWithMultipleDevices): default=ReceiverRole.Intermediary) +class ShareDeliveryNote(JoinedTableMixin, ActionWithMultipleDevices): + """To share a DeliveryNote to between owners.""" + # New variables for DeliveryNote + supplier = db.Column() # String, nullable, ... + supplier.comment = """Name of the organization/agent that create DeliveryNote.""" + date_delivery_note = db.Column() + date_delivery_note.comment = """Date of note creation.""" + # Is the same of lot id?? + id_delivery_note = db.Column(UUID(as_uuid=True)) + id_delivery_note.comment = """Unique id of lot and delivery note.""" + deposit = db.Column() + deposit.comment = """Total amount of deposit devices in Lot.""" + address_note = db.Column(UUID(as_uuid=True)) + address_note.comment = """Address identifier in the blockchain.""" + + agent_id = Column(UUID(as_uuid=True), + ForeignKey(Agent.id), + nullable=False, + default=lambda: g.user.individual.id) + + +class ConfirmDeliveryNote(JoinedTableMixin, ActionWithMultipleDevices): + """To confirm a DeliveryNote that has been shared.""" + pass + + class Migrate(JoinedTableMixin, ActionWithMultipleDevices): """Moves the devices to a new database/inventory. Devices cannot be modified anymore at the previous database. diff --git a/ereuse_devicehub/resources/action/models.pyi b/ereuse_devicehub/resources/action/models.pyi index 6d9ab5b4..c7969094 100644 --- a/ereuse_devicehub/resources/action/models.pyi +++ b/ereuse_devicehub/resources/action/models.pyi @@ -529,6 +529,10 @@ class Receive(ActionWithMultipleDevices): self.role = ... # type: ReceiverRole +class ShareDeliveryNote(ActionWithMultipleDevices): + pass + + class Migrate(ActionWithMultipleDevices): other = ... # type: Column diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 63ed2d3a..b8eeafcf 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -430,6 +430,15 @@ class Receive(ActionWithMultipleDevices): role = EnumField(ReceiverRole) +class ShareDeliveryNote(ActionWithMultipleDevices): + __doc__ = m.ShareDeliveryNote.__doc__ + supplier = SanitizedStr(validate=Length(max=STR_SIZE), data_key='supplierName') + date_delivery_note = DateTime(data_key='dateDeliveryNote') + deposit = Integer(data_key='depositValue') + address_note = UUID(dump_only=True) + id_delivery_note = UUID(dump_only=True) + + class Migrate(ActionWithMultipleDevices): __doc__ = m.Migrate.__doc__ other = URL() diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index ce5ac219..5a92cfbe 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -92,8 +92,7 @@ class Device(Thing): color.comment = """The predominant color of the device.""" production_date = Column(db.DateTime) production_date.comment = """The date of production of the device. - This is timezone naive, as Workbench cannot report this data - with timezone information. + This is timezone naive, as Workbench cannot report this data with timezone information. """ variant = Column(db.CIText()) variant.comment = """A variant or sub-model of the device.""" diff --git a/ereuse_devicehub/resources/documents/templates/documents/layout.html b/ereuse_devicehub/resources/documents/templates/documents/layout.html index 45f16861..3794f343 100644 --- a/ereuse_devicehub/resources/documents/templates/documents/layout.html +++ b/ereuse_devicehub/resources/documents/templates/documents/layout.html @@ -11,7 +11,7 @@ - Devicehub | {{ title }} + USOdy | {{ title }}
diff --git a/ereuse_devicehub/resources/lot/dag.sql b/ereuse_devicehub/resources/lot/dag.sql index dd4a94c8..c1eef99c 100644 --- a/ereuse_devicehub/resources/lot/dag.sql +++ b/ereuse_devicehub/resources/lot/dag.sql @@ -110,6 +110,3 @@ BEGIN END $$ LANGUAGE plpgsql; - - - diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index 5d08ab88..81530eb8 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -25,6 +25,18 @@ class Lot(Thing): description.comment = """A comment about the lot.""" closed = db.Column(db.Boolean, default=False, nullable=False) closed.comment = """A closed lot cannot be modified anymore.""" + + # New variables for DeliveryNote + supplier = db.Column() # String, nullable, ... + supplier.comment = """Name of the organization/agent that create DeliveryNote.""" + date_delivery_note = db.Column() + date_delivery_note.comment = """Date of note creation.""" + # Is the same of lot id?? + id_delivery_note = db.Column(UUID(as_uuid=True)) + id_delivery_note.comment = """Unique id of lot and delivery note""" + # deposit = db.Column() + # deposit.comment = """Total amount of deposit devices in Lot.""" + devices = db.relationship(Device, backref=db.backref('lots', lazy=True, collection_class=set), secondary=lambda: LotDevice.__table__, diff --git a/ereuse_devicehub/resources/lot/models.pyi b/ereuse_devicehub/resources/lot/models.pyi index 2c820b0c..55ad40e0 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -77,6 +77,18 @@ class Path: self.path = ... # type: Ltree self.created = ... # type: datetime + @classmethod + def has_lot(cls, id, id1): + pass + + @classmethod + def delete(cls, id, id1): + pass + + @classmethod + def add(cls, id, id1): + pass + class LotDeviceDescendants(db.Model): device_id = ... # type: Column diff --git a/tests/files/acer.happy.battery.snapshot.yaml b/tests/files/acer.happy.battery.snapshot.yaml new file mode 100644 index 00000000..7d164b53 --- /dev/null +++ b/tests/files/acer.happy.battery.snapshot.yaml @@ -0,0 +1,151 @@ +--- +components: + - size: 10.030411318500475 + technology: LCD + resolutionWidth: 1024 + model: AUO LCD Monitor + actions: [] + type: Display + refreshRate: 60 + productionDate: '2009-01-04T00:00:00' + manufacturer: AUO "AUO" + serialNumber: + resolutionHeight: 600 + - generation: + actions: + - rate: 164.4981 + type: BenchmarkProcessorSysbench + elapsed: 165 + - rate: 6650.48 + type: BenchmarkProcessor + elapsed: 0 + speed: 1 + cores: 1 + model: Intel Atom CPU N450 @ 1.66GHz + address: 64 + type: Processor + threads: 2 + manufacturer: Intel Corp. + serialNumber: + brand: Atom + - memory: + model: Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller + actions: [] + type: GraphicCard + manufacturer: Intel Corporation + serialNumber: + - size: 2200 + technology: LiIon + actions: + - size: 641 + type: MeasureBattery + voltage: 12608 + cycleCount: + severity: Info + model: AL10A31 + type: Battery + manufacturer: SANYO + serialNumber: + - type: SoundCard + actions: [] + manufacturer: Intel Corporation + serialNumber: + model: NM10/ICH7 Family High Definition Audio Controller + - type: SoundCard + actions: [] + manufacturer: XPA970VW0 + serialNumber: + model: 1.3M WebCam + - size: 1024 + actions: [] + format: SODIMM + model: 48594D503131325336344350362D53362020 + interface: DDR2 + type: RamModule + manufacturer: Hynix Semiconductor + serialNumber: 4F43487B + speed: 667 + - size: 160041.88569599998 + variant: 1A01 + actions: + - type: EraseBasic + steps: + - type: StepRandom + endTime: '2019-10-23T08:35:31.400587+00:00' + severity: Info + startTime: '2019-10-23T07:49:54.410830+00:00' + endTime: '2019-10-23T08:35:31.400988+00:00' + severity: Info + startTime: '2019-10-23T07:49:54.410193+00:00' + - elapsed: 22 + writeSpeed: 17.3 + readSpeed: 41.6 + type: BenchmarkDataStorage + - status: Completed without error + reallocatedSectorCount: 0 + currentPendingSectorCount: 0 + assessment: true + severity: Info + offlineUncorrectable: 0 + lifetime: 4692 + type: TestDataStorage + length: Short + elapsed: 118 + powerCycleCount: 5293 + model: WDC WD1600BEVT-2 + interface: ATA + type: HardDrive + manufacturer: Western Digital + serialNumber: WD-WX11A80W7430 + - variant: c1 + actions: [] + speed: 100 + model: AR8152 v1.1 Fast Ethernet + wireless: false + type: NetworkAdapter + serialNumber: 88:ae:1d:a6:f3:d0 + manufacturer: Qualcomm Atheros + - variant: '00' + actions: [] + speed: + model: Centrino Wireless-N 1000 Condor Peak + wireless: true + type: NetworkAdapter + serialNumber: 00:26:c7:8e:cb:8c + manufacturer: Intel Corporation + - ramMaxSize: 4 + slots: 1 + model: AOHAPPY + pcmcia: 0 + type: Motherboard + version: V3.05(DDR2) + ramSlots: 2 + serialNumber: Base Board Serial Number + manufacturer: Acer + serial: 1 + actions: [] + biosDate: '2010-08-12T00:00:00' + firewire: 0 + usb: 5 +software: Workbench +device: + sku: + chassis: Netbook + actions: + - type: StressTest + elapsed: 60 + severity: Info + - rate: 19.2726 + type: BenchmarkRamSysbench + elapsed: 19 + model: AOHAPPY + type: Laptop + version: V3.05 + manufacturer: Acer + serialNumber: LUSEA0D010038879A01601 +uuid: 490fb8c0-81a1-42e9-95e0-5e7db7038ec2 +type: Snapshot +version: 11.0b9 +endTime: '2019-10-23T07:43:13.625104+00:00' +elapsed: 3138 +closed: true \ No newline at end of file diff --git a/tests/test_rate.py b/tests/test_rate.py index 9a092fe4..7a37dca3 100644 --- a/tests/test_rate.py +++ b/tests/test_rate.py @@ -139,6 +139,7 @@ def test_multiple_rates(user: UserClient): This ensures that rates only takes all the correct actions and components rates in case device have new tests/benchmarks. """ + pc = Desktop(chassis=ComputerChassis.Tower) hdd = HardDrive(size=476940) hdd.actions_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8)) diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index fc5045e7..c3c211d7 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -12,7 +12,8 @@ from ereuse_devicehub.client import UserClient from ereuse_devicehub.db import db from ereuse_devicehub.devicehub import Devicehub from ereuse_devicehub.resources.action.models import Action, BenchmarkDataStorage, \ - BenchmarkProcessor, EraseSectors, RateComputer, Snapshot, SnapshotRequest, VisualTest + BenchmarkProcessor, EraseSectors, RateComputer, Snapshot, SnapshotRequest, VisualTest, \ + MeasureBattery, BenchmarkRamSysbench, StressTest from ereuse_devicehub.resources.device import models as m from ereuse_devicehub.resources.device.exceptions import NeedsId from ereuse_devicehub.resources.device.models import SolidStateDrive @@ -492,3 +493,11 @@ def test_pc_rating_rate_none(user: UserClient): def test_pc_2(user: UserClient): s = file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot') snapshot, _ = user.post(res=Snapshot, data=s) + + +@pytest.mark.xfail(reason='Add battery component assets') +def test_snapshot_pc_with_battery_component(user: UserClient): + pc1 = file('acer.happy.battery.snapshot') + snapshot = snapshot_and_check(user, pc1, + action_types=(StressTest.t, BenchmarkRamSysbench.t), + perform_second_snapshot=False) From 06cd114a8bf9602aab338fe2544c8cf4b09db833 Mon Sep 17 00:00:00 2001 From: "jordi.nadeu" Date: Thu, 12 Dec 2019 21:17:35 +0100 Subject: [PATCH 02/29] add change in Lot and Actions views --- ereuse_devicehub/resources/action/models.py | 30 +++----------------- ereuse_devicehub/resources/action/models.pyi | 8 +++--- ereuse_devicehub/resources/action/schemas.py | 13 +++------ ereuse_devicehub/resources/action/views.py | 9 +++++- ereuse_devicehub/resources/lot/models.py | 11 ------- ereuse_devicehub/resources/lot/views.py | 17 +++++++++++ 6 files changed, 37 insertions(+), 51 deletions(-) diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 4e732ed2..72c29e70 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1377,6 +1377,10 @@ class Trade(JoinedTableMixin, ActionWithMultipleDevices): """ +class InitTransfer(Trade): + """The act of transfer ownership of devices between two agents""" + + class Sell(Trade): """The act of taking money from a buyer in exchange of a device.""" @@ -1445,32 +1449,6 @@ class Receive(JoinedTableMixin, ActionWithMultipleDevices): default=ReceiverRole.Intermediary) -class ShareDeliveryNote(JoinedTableMixin, ActionWithMultipleDevices): - """To share a DeliveryNote to between owners.""" - # New variables for DeliveryNote - supplier = db.Column() # String, nullable, ... - supplier.comment = """Name of the organization/agent that create DeliveryNote.""" - date_delivery_note = db.Column() - date_delivery_note.comment = """Date of note creation.""" - # Is the same of lot id?? - id_delivery_note = db.Column(UUID(as_uuid=True)) - id_delivery_note.comment = """Unique id of lot and delivery note.""" - deposit = db.Column() - deposit.comment = """Total amount of deposit devices in Lot.""" - address_note = db.Column(UUID(as_uuid=True)) - address_note.comment = """Address identifier in the blockchain.""" - - agent_id = Column(UUID(as_uuid=True), - ForeignKey(Agent.id), - nullable=False, - default=lambda: g.user.individual.id) - - -class ConfirmDeliveryNote(JoinedTableMixin, ActionWithMultipleDevices): - """To confirm a DeliveryNote that has been shared.""" - pass - - class Migrate(JoinedTableMixin, ActionWithMultipleDevices): """Moves the devices to a new database/inventory. Devices cannot be modified anymore at the previous database. diff --git a/ereuse_devicehub/resources/action/models.pyi b/ereuse_devicehub/resources/action/models.pyi index c7969094..42780ef0 100644 --- a/ereuse_devicehub/resources/action/models.pyi +++ b/ereuse_devicehub/resources/action/models.pyi @@ -493,6 +493,10 @@ class Trade(ActionWithMultipleDevices): self.confirms = ... # type: Organize +class InitTransfer(Trade): + pass + + class Sell(Trade): pass @@ -529,10 +533,6 @@ class Receive(ActionWithMultipleDevices): self.role = ... # type: ReceiverRole -class ShareDeliveryNote(ActionWithMultipleDevices): - pass - - class Migrate(ActionWithMultipleDevices): other = ... # type: Column diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index b8eeafcf..68fdebf9 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -397,6 +397,10 @@ class Trade(ActionWithMultipleDevices): confirms = NestedOn(Organize) +class InitTransfer(Trade): + __doc__ = m.InitTransfer.__doc__ + + class Sell(Trade): __doc__ = m.Sell.__doc__ @@ -430,15 +434,6 @@ class Receive(ActionWithMultipleDevices): role = EnumField(ReceiverRole) -class ShareDeliveryNote(ActionWithMultipleDevices): - __doc__ = m.ShareDeliveryNote.__doc__ - supplier = SanitizedStr(validate=Length(max=STR_SIZE), data_key='supplierName') - date_delivery_note = DateTime(data_key='dateDeliveryNote') - deposit = Integer(data_key='depositValue') - address_note = UUID(dump_only=True) - id_delivery_note = UUID(dump_only=True) - - class Migrate(ActionWithMultipleDevices): __doc__ = m.Migrate.__doc__ other = URL() diff --git a/ereuse_devicehub/resources/action/views.py b/ereuse_devicehub/resources/action/views.py index de6805b4..9a7b176c 100644 --- a/ereuse_devicehub/resources/action/views.py +++ b/ereuse_devicehub/resources/action/views.py @@ -8,7 +8,8 @@ from teal.marshmallow import ValidationError from teal.resource import View from ereuse_devicehub.db import db -from ereuse_devicehub.resources.action.models import Action, RateComputer, Snapshot, VisualTest +from ereuse_devicehub.resources.action.models import Action, RateComputer, Snapshot, VisualTest, \ + InitTransfer from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate from ereuse_devicehub.resources.device.models import Component, Computer from ereuse_devicehub.resources.enums import SnapshotSoftware @@ -31,6 +32,8 @@ class ActionView(View): if json['type'] == VisualTest.t: pass # TODO JN add compute rate with new visual test and old components device + if json['type'] == InitTransfer.t: + return self.transfer_ownership() Model = db.Model._decl_class_registry.data[json['type']]() action = Model(**a) db.session.add(action) @@ -101,3 +104,7 @@ class ActionView(View): ret.status_code = 201 db.session.commit() return ret + + def transfer_ownership(self): + """Perform a InitTransfer action to change author_id of device""" + pass diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index bb214f6f..d9a037d8 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -27,17 +27,6 @@ class Lot(Thing): closed = db.Column(db.Boolean, default=False, nullable=False) closed.comment = """A closed lot cannot be modified anymore.""" - # New variables for DeliveryNote - supplier = db.Column() # String, nullable, ... - supplier.comment = """Name of the organization/agent that create DeliveryNote.""" - date_delivery_note = db.Column() - date_delivery_note.comment = """Date of note creation.""" - # Is the same of lot id?? - id_delivery_note = db.Column(UUID(as_uuid=True)) - id_delivery_note.comment = """Unique id of lot and delivery note""" - # deposit = db.Column() - # deposit.comment = """Total amount of deposit devices in Lot.""" - devices = db.relationship(Device, backref=db.backref('lots', lazy=True, collection_class=set), secondary=lambda: LotDevice.__table__, diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index 4e3a9f06..dca07061 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -43,6 +43,11 @@ class LotView(View): patch_schema = self.resource_def.SCHEMA(only=('name', 'description'), partial=True) l = request.get_json(schema=patch_schema) lot = Lot.query.filter_by(id=id).one() + if lot.transfer_state.name == 'Initial': + # Initial lot transfer state case + # deposit = self.get_lot_deposit(lot) + # Do something with deposit variable + pass for key, value in l.items(): setattr(lot, key, value) db.session.commit() @@ -123,6 +128,18 @@ 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 change_state(self): + """Change state of Lot""" + pass + + def transfer_ownership_lot(self): + """Perform a InitTransfer action to change author_id of lot""" + pass + class LotBaseChildrenView(View): """Base class for adding / removing children devices and From 7d9c737d49ddaf439a13d27931a2bda2f352403a Mon Sep 17 00:00:00 2001 From: nad Date: Tue, 18 Feb 2020 13:38:29 +0100 Subject: [PATCH 03/29] minores fixes, adds tqdm requirements and add vscode folder to gitignore --- .gitignore | 2 +- requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cc28e9a1..f1de35b5 100644 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,4 @@ ENV/ .idea/ .DS_Store /app.py - +.vscode/ diff --git a/requirements.txt b/requirements.txt index cb6b9256..725d1524 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,3 +32,4 @@ flask-weasyprint==0.5 weasyprint==44 psycopg2-binary==2.8.3 sortedcontainers==2.1.0 +tqdm==4.32.2 \ No newline at end of file From a93c2737f610941e4d871e7e3a65de87ab5e0651 Mon Sep 17 00:00:00 2001 From: nad Date: Tue, 18 Feb 2020 13:50:48 +0100 Subject: [PATCH 04/29] first iteration with mobile schema/model --- ereuse_devicehub/resources/device/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index ce5ac219..7d0791bc 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -492,9 +492,10 @@ class Mobile(Device): meid.comment = """The Mobile Equipment Identifier as a hexadecimal string. """ - ram_size = db.Column(db.Integer, check_range(1, )) + ram_size = db.Column(db.Integer, check_range('ram_size', min=128, max=36000)) ram_size.comment = """The total of RAM of the device in MB.""" - data_storage_size = db.Column(db.Integer) + data_storage_size = db.Column(db.Integer, check_range('data_storage_size', 0, 10 ** 8)) + data_storage_size.comment = """The total of data storage of the device in MB""" @validates('imei') def validate_imei(self, _, value: int): From 1497f916848c0f9b4580b8e99e8a79086c017585 Mon Sep 17 00:00:00 2001 From: nad Date: Tue, 3 Mar 2020 12:03:09 +0100 Subject: [PATCH 05/29] Adds display size to mobile device schema --- ereuse_devicehub/resources/action/schemas.py | 10 +++++++--- ereuse_devicehub/resources/action/views.py | 6 ++++-- ereuse_devicehub/resources/device/models.py | 2 ++ ereuse_devicehub/resources/device/models.pyi | 2 ++ ereuse_devicehub/resources/device/schemas.py | 3 +++ ereuse_devicehub/resources/enums.py | 1 + 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 63ed2d3a..1c86d358 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -318,7 +318,7 @@ class Snapshot(ActionWithOneDevice): @validates_schema def validate_components_only_workbench(self, data: dict): - if data['software'] != SnapshotSoftware.Workbench: + if (data['software'] != SnapshotSoftware.Workbench) and (data['software'] != SnapshotSoftware.WorkbenchAndroid): if data.get('components', None) is not None: raise ValidationError('Only Workbench can add component info', field_names=['components']) @@ -329,14 +329,18 @@ class Snapshot(ActionWithOneDevice): # todo test if data['software'] == SnapshotSoftware.Workbench: if not data.get('uuid', None): - raise ValidationError('Snapshots from Workbench must have uuid', + raise ValidationError('Snapshots from Workbench and WorkbenchAndroid must have uuid', field_names=['uuid']) if data.get('elapsed', None) is None: raise ValidationError('Snapshots from Workbench must have elapsed', field_names=['elapsed']) + elif data['software'] == SnapshotSoftware.WorkbenchAndroid: + if not data.get('uuid', None): + raise ValidationError('Snapshots from Workbench and WorkbenchAndroid must have uuid', + field_names=['uuid']) else: if data.get('uuid', None): - raise ValidationError('Only Snapshots from Workbench can have uuid', + raise ValidationError('Only Snapshots from Workbench or WorkbenchAndroid can have uuid', field_names=['uuid']) if data.get('elapsed', None): raise ValidationError('Only Snapshots from Workbench can have elapsed', diff --git a/ereuse_devicehub/resources/action/views.py b/ereuse_devicehub/resources/action/views.py index de6805b4..30c35633 100644 --- a/ereuse_devicehub/resources/action/views.py +++ b/ereuse_devicehub/resources/action/views.py @@ -55,8 +55,8 @@ class ActionView(View): # snapshot, and we want to wait to flush snapshot at the end device = snapshot_json.pop('device') # type: Computer components = None - if snapshot_json['software'] == SnapshotSoftware.Workbench: - components = snapshot_json.pop('components') # type: List[Component] + if snapshot_json['software'] == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid): + components = snapshot_json.pop('components', None) # type: List[Component] snapshot = Snapshot(**snapshot_json) # Remove new actions from devices so they don't interfere with sync @@ -94,6 +94,8 @@ class ActionView(View): snapshot.actions.add(rate_computer) if price: snapshot.actions.add(price) + elif snapshot.software == SnapshotSoftware.WorkbenchAndroid: + pass # TODO try except to compute RateMobile db.session.add(snapshot) db.session().final_flush() diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 7d0791bc..cf508882 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -496,6 +496,8 @@ class Mobile(Device): ram_size.comment = """The total of RAM of the device in MB.""" data_storage_size = db.Column(db.Integer, check_range('data_storage_size', 0, 10 ** 8)) data_storage_size.comment = """The total of data storage of the device in MB""" + display_size = db.Column(db.Float(decimal_return_scale=1), check_range('display_size', min=0.1, max=30.0)) + display_size.comment = """The total size of the device screen""" @validates('imei') def validate_imei(self, _, value: int): diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index 51a8ea53..fe4e47e3 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -210,6 +210,7 @@ class Mobile(Device): meid = ... # type: Column ram_size = ... # type: Column data_storage_size = ... # type: Column + display_size = ... # type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) @@ -217,6 +218,7 @@ class Mobile(Device): self.meid = ... # type: Optional[str] self.ram_size = ... # type: Optional[int] self.data_storage_size = ... # type: Optional[int] + self.display_size = ... # type: Optional[float] class Smartphone(Mobile): diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index e5ce1c77..6fc4c1a5 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -189,6 +189,9 @@ class Mobile(Device): data_storage_size = Integer(validate=Range(0, 10 ** 8), data_key='dataStorageSize', description=m.Mobile.data_storage_size) + display_size = Float(validate=Range(min=0.1, max=30.0), + data_key='displaySize', + description=m.Mobile.display_size.comment) @pre_load def convert_check_imei(self, data): diff --git a/ereuse_devicehub/resources/enums.py b/ereuse_devicehub/resources/enums.py index 9e74217c..2838e80d 100644 --- a/ereuse_devicehub/resources/enums.py +++ b/ereuse_devicehub/resources/enums.py @@ -9,6 +9,7 @@ import inflection class SnapshotSoftware(Enum): """The software used to perform the Snapshot.""" Workbench = 'Workbench' + WorkbenchAndroid = 'WorkbenchAndroid' AndroidApp = 'AndroidApp' Web = 'Web' DesktopApp = 'DesktopApp' From 4fc63782588c3291158314911301dc2748df31f3 Mon Sep 17 00:00:00 2001 From: "jordi.nadeu" Date: Mon, 9 Dec 2019 17:24:24 +0100 Subject: [PATCH 06/29] first iteration integration with blockchain --- ereuse_devicehub/resources/action/models.py | 26 +++ ereuse_devicehub/resources/action/models.pyi | 4 + ereuse_devicehub/resources/action/schemas.py | 9 ++ ereuse_devicehub/resources/device/models.py | 3 +- .../documents/templates/documents/layout.html | 2 +- ereuse_devicehub/resources/lot/dag.sql | 3 - ereuse_devicehub/resources/lot/models.py | 12 ++ ereuse_devicehub/resources/lot/models.pyi | 12 ++ tests/files/acer.happy.battery.snapshot.yaml | 151 ++++++++++++++++++ tests/test_rate.py | 1 + tests/test_snapshot.py | 11 +- 11 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 tests/files/acer.happy.battery.snapshot.yaml diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index b0bc435f..4e732ed2 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1445,6 +1445,32 @@ class Receive(JoinedTableMixin, ActionWithMultipleDevices): default=ReceiverRole.Intermediary) +class ShareDeliveryNote(JoinedTableMixin, ActionWithMultipleDevices): + """To share a DeliveryNote to between owners.""" + # New variables for DeliveryNote + supplier = db.Column() # String, nullable, ... + supplier.comment = """Name of the organization/agent that create DeliveryNote.""" + date_delivery_note = db.Column() + date_delivery_note.comment = """Date of note creation.""" + # Is the same of lot id?? + id_delivery_note = db.Column(UUID(as_uuid=True)) + id_delivery_note.comment = """Unique id of lot and delivery note.""" + deposit = db.Column() + deposit.comment = """Total amount of deposit devices in Lot.""" + address_note = db.Column(UUID(as_uuid=True)) + address_note.comment = """Address identifier in the blockchain.""" + + agent_id = Column(UUID(as_uuid=True), + ForeignKey(Agent.id), + nullable=False, + default=lambda: g.user.individual.id) + + +class ConfirmDeliveryNote(JoinedTableMixin, ActionWithMultipleDevices): + """To confirm a DeliveryNote that has been shared.""" + pass + + class Migrate(JoinedTableMixin, ActionWithMultipleDevices): """Moves the devices to a new database/inventory. Devices cannot be modified anymore at the previous database. diff --git a/ereuse_devicehub/resources/action/models.pyi b/ereuse_devicehub/resources/action/models.pyi index 6d9ab5b4..c7969094 100644 --- a/ereuse_devicehub/resources/action/models.pyi +++ b/ereuse_devicehub/resources/action/models.pyi @@ -529,6 +529,10 @@ class Receive(ActionWithMultipleDevices): self.role = ... # type: ReceiverRole +class ShareDeliveryNote(ActionWithMultipleDevices): + pass + + class Migrate(ActionWithMultipleDevices): other = ... # type: Column diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 1c86d358..0f1e4544 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -434,6 +434,15 @@ class Receive(ActionWithMultipleDevices): role = EnumField(ReceiverRole) +class ShareDeliveryNote(ActionWithMultipleDevices): + __doc__ = m.ShareDeliveryNote.__doc__ + supplier = SanitizedStr(validate=Length(max=STR_SIZE), data_key='supplierName') + date_delivery_note = DateTime(data_key='dateDeliveryNote') + deposit = Integer(data_key='depositValue') + address_note = UUID(dump_only=True) + id_delivery_note = UUID(dump_only=True) + + class Migrate(ActionWithMultipleDevices): __doc__ = m.Migrate.__doc__ other = URL() diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index cf508882..26298909 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -92,8 +92,7 @@ class Device(Thing): color.comment = """The predominant color of the device.""" production_date = Column(db.DateTime) production_date.comment = """The date of production of the device. - This is timezone naive, as Workbench cannot report this data - with timezone information. + This is timezone naive, as Workbench cannot report this data with timezone information. """ variant = Column(db.CIText()) variant.comment = """A variant or sub-model of the device.""" diff --git a/ereuse_devicehub/resources/documents/templates/documents/layout.html b/ereuse_devicehub/resources/documents/templates/documents/layout.html index 45f16861..3794f343 100644 --- a/ereuse_devicehub/resources/documents/templates/documents/layout.html +++ b/ereuse_devicehub/resources/documents/templates/documents/layout.html @@ -11,7 +11,7 @@ - Devicehub | {{ title }} + USOdy | {{ title }}
diff --git a/ereuse_devicehub/resources/lot/dag.sql b/ereuse_devicehub/resources/lot/dag.sql index dd4a94c8..c1eef99c 100644 --- a/ereuse_devicehub/resources/lot/dag.sql +++ b/ereuse_devicehub/resources/lot/dag.sql @@ -110,6 +110,3 @@ BEGIN END $$ LANGUAGE plpgsql; - - - diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index 5d08ab88..81530eb8 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -25,6 +25,18 @@ class Lot(Thing): description.comment = """A comment about the lot.""" closed = db.Column(db.Boolean, default=False, nullable=False) closed.comment = """A closed lot cannot be modified anymore.""" + + # New variables for DeliveryNote + supplier = db.Column() # String, nullable, ... + supplier.comment = """Name of the organization/agent that create DeliveryNote.""" + date_delivery_note = db.Column() + date_delivery_note.comment = """Date of note creation.""" + # Is the same of lot id?? + id_delivery_note = db.Column(UUID(as_uuid=True)) + id_delivery_note.comment = """Unique id of lot and delivery note""" + # deposit = db.Column() + # deposit.comment = """Total amount of deposit devices in Lot.""" + devices = db.relationship(Device, backref=db.backref('lots', lazy=True, collection_class=set), secondary=lambda: LotDevice.__table__, diff --git a/ereuse_devicehub/resources/lot/models.pyi b/ereuse_devicehub/resources/lot/models.pyi index 2c820b0c..55ad40e0 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -77,6 +77,18 @@ class Path: self.path = ... # type: Ltree self.created = ... # type: datetime + @classmethod + def has_lot(cls, id, id1): + pass + + @classmethod + def delete(cls, id, id1): + pass + + @classmethod + def add(cls, id, id1): + pass + class LotDeviceDescendants(db.Model): device_id = ... # type: Column diff --git a/tests/files/acer.happy.battery.snapshot.yaml b/tests/files/acer.happy.battery.snapshot.yaml new file mode 100644 index 00000000..7d164b53 --- /dev/null +++ b/tests/files/acer.happy.battery.snapshot.yaml @@ -0,0 +1,151 @@ +--- +components: + - size: 10.030411318500475 + technology: LCD + resolutionWidth: 1024 + model: AUO LCD Monitor + actions: [] + type: Display + refreshRate: 60 + productionDate: '2009-01-04T00:00:00' + manufacturer: AUO "AUO" + serialNumber: + resolutionHeight: 600 + - generation: + actions: + - rate: 164.4981 + type: BenchmarkProcessorSysbench + elapsed: 165 + - rate: 6650.48 + type: BenchmarkProcessor + elapsed: 0 + speed: 1 + cores: 1 + model: Intel Atom CPU N450 @ 1.66GHz + address: 64 + type: Processor + threads: 2 + manufacturer: Intel Corp. + serialNumber: + brand: Atom + - memory: + model: Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller + actions: [] + type: GraphicCard + manufacturer: Intel Corporation + serialNumber: + - size: 2200 + technology: LiIon + actions: + - size: 641 + type: MeasureBattery + voltage: 12608 + cycleCount: + severity: Info + model: AL10A31 + type: Battery + manufacturer: SANYO + serialNumber: + - type: SoundCard + actions: [] + manufacturer: Intel Corporation + serialNumber: + model: NM10/ICH7 Family High Definition Audio Controller + - type: SoundCard + actions: [] + manufacturer: XPA970VW0 + serialNumber: + model: 1.3M WebCam + - size: 1024 + actions: [] + format: SODIMM + model: 48594D503131325336344350362D53362020 + interface: DDR2 + type: RamModule + manufacturer: Hynix Semiconductor + serialNumber: 4F43487B + speed: 667 + - size: 160041.88569599998 + variant: 1A01 + actions: + - type: EraseBasic + steps: + - type: StepRandom + endTime: '2019-10-23T08:35:31.400587+00:00' + severity: Info + startTime: '2019-10-23T07:49:54.410830+00:00' + endTime: '2019-10-23T08:35:31.400988+00:00' + severity: Info + startTime: '2019-10-23T07:49:54.410193+00:00' + - elapsed: 22 + writeSpeed: 17.3 + readSpeed: 41.6 + type: BenchmarkDataStorage + - status: Completed without error + reallocatedSectorCount: 0 + currentPendingSectorCount: 0 + assessment: true + severity: Info + offlineUncorrectable: 0 + lifetime: 4692 + type: TestDataStorage + length: Short + elapsed: 118 + powerCycleCount: 5293 + model: WDC WD1600BEVT-2 + interface: ATA + type: HardDrive + manufacturer: Western Digital + serialNumber: WD-WX11A80W7430 + - variant: c1 + actions: [] + speed: 100 + model: AR8152 v1.1 Fast Ethernet + wireless: false + type: NetworkAdapter + serialNumber: 88:ae:1d:a6:f3:d0 + manufacturer: Qualcomm Atheros + - variant: '00' + actions: [] + speed: + model: Centrino Wireless-N 1000 Condor Peak + wireless: true + type: NetworkAdapter + serialNumber: 00:26:c7:8e:cb:8c + manufacturer: Intel Corporation + - ramMaxSize: 4 + slots: 1 + model: AOHAPPY + pcmcia: 0 + type: Motherboard + version: V3.05(DDR2) + ramSlots: 2 + serialNumber: Base Board Serial Number + manufacturer: Acer + serial: 1 + actions: [] + biosDate: '2010-08-12T00:00:00' + firewire: 0 + usb: 5 +software: Workbench +device: + sku: + chassis: Netbook + actions: + - type: StressTest + elapsed: 60 + severity: Info + - rate: 19.2726 + type: BenchmarkRamSysbench + elapsed: 19 + model: AOHAPPY + type: Laptop + version: V3.05 + manufacturer: Acer + serialNumber: LUSEA0D010038879A01601 +uuid: 490fb8c0-81a1-42e9-95e0-5e7db7038ec2 +type: Snapshot +version: 11.0b9 +endTime: '2019-10-23T07:43:13.625104+00:00' +elapsed: 3138 +closed: true \ No newline at end of file diff --git a/tests/test_rate.py b/tests/test_rate.py index 9a092fe4..7a37dca3 100644 --- a/tests/test_rate.py +++ b/tests/test_rate.py @@ -139,6 +139,7 @@ def test_multiple_rates(user: UserClient): This ensures that rates only takes all the correct actions and components rates in case device have new tests/benchmarks. """ + pc = Desktop(chassis=ComputerChassis.Tower) hdd = HardDrive(size=476940) hdd.actions_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8)) diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index fc5045e7..c3c211d7 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -12,7 +12,8 @@ from ereuse_devicehub.client import UserClient from ereuse_devicehub.db import db from ereuse_devicehub.devicehub import Devicehub from ereuse_devicehub.resources.action.models import Action, BenchmarkDataStorage, \ - BenchmarkProcessor, EraseSectors, RateComputer, Snapshot, SnapshotRequest, VisualTest + BenchmarkProcessor, EraseSectors, RateComputer, Snapshot, SnapshotRequest, VisualTest, \ + MeasureBattery, BenchmarkRamSysbench, StressTest from ereuse_devicehub.resources.device import models as m from ereuse_devicehub.resources.device.exceptions import NeedsId from ereuse_devicehub.resources.device.models import SolidStateDrive @@ -492,3 +493,11 @@ def test_pc_rating_rate_none(user: UserClient): def test_pc_2(user: UserClient): s = file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot') snapshot, _ = user.post(res=Snapshot, data=s) + + +@pytest.mark.xfail(reason='Add battery component assets') +def test_snapshot_pc_with_battery_component(user: UserClient): + pc1 = file('acer.happy.battery.snapshot') + snapshot = snapshot_and_check(user, pc1, + action_types=(StressTest.t, BenchmarkRamSysbench.t), + perform_second_snapshot=False) From 91f5fa4e75248af98456e463d8fff758d9344b62 Mon Sep 17 00:00:00 2001 From: emmdim Date: Wed, 11 Dec 2019 00:35:17 +0100 Subject: [PATCH 07/29] Adds deposit field in Computer --- ereuse_devicehub/resources/device/models.py | 3 ++- ereuse_devicehub/resources/device/models.pyi | 1 + ereuse_devicehub/resources/device/schemas.py | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 26298909..4d809f80 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -375,9 +375,10 @@ class Computer(Device): id = Column(BigInteger, ForeignKey(Device.id), primary_key=True) chassis = Column(DBEnum(ComputerChassis), nullable=False) chassis.comment = """The physical form of the computer. - + It is a subset of the Linux definition of DMI / DMI decode. """ + deposit = Column(Integer, check_range('deposit',min=0,max=100), default=0) def __init__(self, chassis, **kwargs) -> None: chassis = ComputerChassis(chassis) diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index fe4e47e3..5e75b933 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -141,6 +141,7 @@ class DisplayMixin: class Computer(DisplayMixin, Device): components = ... # type: Column chassis = ... # type: Column + deposit = ... # type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index 6fc4c1a5..14c34fef 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -121,7 +121,9 @@ class Computer(Device): dump_only=True, collection_class=set, description=m.Computer.privacy.__doc__) - + deposit = Integer(dump_only=True, + data_key='deposit', + description=m.Computer.deposit.__doc__) class Desktop(Computer): __doc__ = m.Desktop.__doc__ From 06071f7001e45718ff113cfae2efa2c5d6e4d64f Mon Sep 17 00:00:00 2001 From: emmdim Date: Wed, 11 Dec 2019 02:19:13 +0100 Subject: [PATCH 08/29] Adds author_id to Computer resource --- ereuse_devicehub/resources/device/models.py | 8 ++++++++ ereuse_devicehub/resources/device/models.pyi | 4 +++- ereuse_devicehub/resources/device/schemas.py | 6 +++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 4d809f80..cb04f425 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -7,6 +7,7 @@ from typing import Dict, List, Set from boltons import urlutils from citext import CIText +from flask import g from ereuse_utils.naming import HID_CONVERSION_DOC, Naming from more_itertools import unique_everseen from sqlalchemy import BigInteger, Boolean, Column, Enum as DBEnum, Float, ForeignKey, Integer, \ @@ -16,6 +17,7 @@ from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import ColumnProperty, backref, relationship, validates from sqlalchemy.util import OrderedSet from sqlalchemy_utils import ColorType +from sqlalchemy.dialects.postgresql import UUID from stdnum import imei, meid from teal.db import CASCADE_DEL, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, URL, \ check_lower, check_range @@ -27,6 +29,7 @@ from ereuse_devicehub.db import db from ereuse_devicehub.resources.enums import BatteryTechnology, CameraFacing, ComputerChassis, \ DataStorageInterface, DisplayTech, PrinterTechnology, RamFormat, RamInterface, Severity from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing +from ereuse_devicehub.resources.user.models import User class Device(Thing): @@ -379,6 +382,11 @@ class Computer(Device): It is a subset of the Linux definition of DMI / DMI decode. """ deposit = Column(Integer, check_range('deposit',min=0,max=100), default=0) + author_id = db.Column(UUID(as_uuid=True), + db.ForeignKey(User.id), + nullable=False, + default=lambda: g.user.id) + author = db.relationship(User, primaryjoin=author_id == User.id) def __init__(self, chassis, **kwargs) -> None: chassis = ComputerChassis(chassis) diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index 5e75b933..77740a76 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -142,13 +142,15 @@ class Computer(DisplayMixin, Device): components = ... # type: Column chassis = ... # type: Column deposit = ... # type: Column + author_id = ... # type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.components = ... # type: Set[Component] self.actions_parent = ... # type: Set[e.Action] self.chassis = ... # type: ComputerChassis - + self.author_id = ... + @property def actions(self) -> List: pass diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index 14c34fef..ec9b85a2 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -1,7 +1,7 @@ import datetime from marshmallow import post_load, pre_load -from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String +from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String, UUID from marshmallow.validate import Length, OneOf, Range from sqlalchemy.util import OrderedSet from stdnum import imei, meid @@ -14,6 +14,7 @@ from ereuse_devicehub.resources import enums from ereuse_devicehub.resources.device import models as m, states from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE from ereuse_devicehub.resources.schemas import Thing, UnitCodes +from ereuse_devicehub.resources.user import schemas as s_user class Device(Thing): @@ -124,6 +125,9 @@ class Computer(Device): deposit = Integer(dump_only=True, data_key='deposit', description=m.Computer.deposit.__doc__) + # author_id = NestedOn(s_user.User,only_query='author_id') + author_id = UUID(dump_only=True, + data_key='author_id') class Desktop(Computer): __doc__ = m.Desktop.__doc__ From 087acfd18040706b316721bf5df0969579ccf1ae Mon Sep 17 00:00:00 2001 From: emmdim Date: Wed, 11 Dec 2019 02:49:47 +0100 Subject: [PATCH 09/29] Adds deposit and author is lot resources --- ereuse_devicehub/resources/lot/models.py | 8 +++++++- ereuse_devicehub/resources/lot/models.pyi | 3 +++ ereuse_devicehub/resources/lot/schemas.py | 6 ++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index 81530eb8..dfc08ebc 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -9,7 +9,7 @@ from sqlalchemy import TEXT from sqlalchemy.dialects.postgresql import UUID from sqlalchemy_utils import LtreeType from sqlalchemy_utils.types.ltree import LQUERY -from teal.db import CASCADE_OWN, UUIDLtree +from teal.db import CASCADE_OWN, UUIDLtree, check_range from teal.resource import url_for_resource from ereuse_devicehub.db import create_view, db, exp, f @@ -73,6 +73,12 @@ 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) + author_id = db.Column(UUID(as_uuid=True), + db.ForeignKey(User.id), + nullable=False, + default=lambda: g.user.id) + author = db.relationship(User, primaryjoin=author_id == User.id) 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 55ad40e0..3ad95ab3 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -24,6 +24,8 @@ class Lot(Thing): description = ... # type: Column all_devices = ... # type: relationship parents = ... # type: relationship + deposit = ... # type: Column + author_id = ... # type: Column def __init__(self, name: str, closed: bool = closed.default.arg) -> None: super().__init__() @@ -36,6 +38,7 @@ class Lot(Thing): self.all_devices = ... # type: Set[Device] self.parents = ... # type: Set[Lot] self.children = ... # type: Set[Lot] + self.author_id = ... 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 c6550e86..6fd4f889 100644 --- a/ereuse_devicehub/resources/lot/schemas.py +++ b/ereuse_devicehub/resources/lot/schemas.py @@ -17,3 +17,9 @@ 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(dump_only=True, + data_key='deposit', + description=m.Lot.deposit.__doc__) + # author_id = NestedOn(s_user.User,only_query='author_id') + author_id = f.UUID(dump_only=True, + data_key='author_id') From c90d72708f591f44add778b8c96e4d69fa1da08c Mon Sep 17 00:00:00 2001 From: emmdim Date: Thu, 12 Dec 2019 01:25:11 +0100 Subject: [PATCH 10/29] Adds TransferStateEnum and makes it a Lot field --- ereuse_devicehub/resources/enums.py | 22 ++++++++++++++++++++++ ereuse_devicehub/resources/lot/models.py | 7 +++++-- ereuse_devicehub/resources/lot/models.pyi | 4 +++- ereuse_devicehub/resources/lot/schemas.py | 7 ++++--- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/ereuse_devicehub/resources/enums.py b/ereuse_devicehub/resources/enums.py index 2838e80d..81f95c78 100644 --- a/ereuse_devicehub/resources/enums.py +++ b/ereuse_devicehub/resources/enums.py @@ -369,3 +369,25 @@ class ErasureStandards(Enum): and all(isinstance(step, actions.StepRandom) for step in other_steps): standards.add(cls.HMG_IS5) return standards + +@unique +class TransferState(Enum): + """State of transfer for a given Lot of devices. + """ + + """ + * Initial: No transfer action in place. + * Initiated: The transfer action has been initiated by orginator. + * Accepted: The transfer action has been accepted by destinator. + + Devicehub specially raises user awareness when an action + has a Severity of ``Warning`` or greater. + """ + + Initial = 0 + Initiated = 1 + Accepted = 2 + Completed = 3 + + def __str__(self): + return self.name diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index dfc08ebc..bb214f6f 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -5,17 +5,18 @@ from typing import Union from boltons import urlutils from citext import CIText from flask import g -from sqlalchemy import TEXT +from sqlalchemy import TEXT, Enum as DBEnum from sqlalchemy.dialects.postgresql import UUID from sqlalchemy_utils import LtreeType from sqlalchemy_utils.types.ltree import LQUERY -from teal.db import CASCADE_OWN, UUIDLtree, check_range +from teal.db import CASCADE_OWN, UUIDLtree, check_range, IntEnum from teal.resource import url_for_resource from ereuse_devicehub.db import create_view, db, exp, f from ereuse_devicehub.resources.device.models import Component, Device from ereuse_devicehub.resources.models import Thing from ereuse_devicehub.resources.user.models import User +from ereuse_devicehub.resources.enums import TransferState class Lot(Thing): @@ -79,6 +80,8 @@ class Lot(Thing): nullable=False, default=lambda: g.user.id) author = db.relationship(User, primaryjoin=author_id == User.id) + transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) + transfer_state.comment = TransferState.__doc__ 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 3ad95ab3..80b2bbfb 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -26,6 +26,7 @@ class Lot(Thing): parents = ... # type: relationship deposit = ... # type: Column author_id = ... # type: Column + transfer_state = ... # type: Column def __init__(self, name: str, closed: bool = closed.default.arg) -> None: super().__init__() @@ -38,7 +39,8 @@ class Lot(Thing): self.all_devices = ... # type: Set[Device] self.parents = ... # type: Set[Lot] self.children = ... # type: Set[Lot] - self.author_id = ... + self.author_id = ... # type: UUID + self.transfer_state = ... 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 6fd4f889..670a09a6 100644 --- a/ereuse_devicehub/resources/lot/schemas.py +++ b/ereuse_devicehub/resources/lot/schemas.py @@ -1,11 +1,12 @@ from marshmallow import fields as f -from teal.marshmallow import SanitizedStr, URL +from teal.marshmallow import SanitizedStr, URL, EnumField from ereuse_devicehub.marshmallow import NestedOn from ereuse_devicehub.resources.device import schemas as s_device from ereuse_devicehub.resources.lot import models as m from ereuse_devicehub.resources.models import STR_SIZE from ereuse_devicehub.resources.schemas import Thing +from ereuse_devicehub.resources.enums import TransferState class Lot(Thing): @@ -21,5 +22,5 @@ class Lot(Thing): data_key='deposit', description=m.Lot.deposit.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') - author_id = f.UUID(dump_only=True, - data_key='author_id') + author_id = f.UUID(dump_only=True) + tranfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) \ No newline at end of file From 047bac9096ecaa8f0c87e085a4c307f2e323b703 Mon Sep 17 00:00:00 2001 From: "jordi.nadeu" Date: Thu, 12 Dec 2019 21:17:35 +0100 Subject: [PATCH 11/29] add change in Lot and Actions views --- ereuse_devicehub/resources/action/models.py | 30 +++----------------- ereuse_devicehub/resources/action/models.pyi | 8 +++--- ereuse_devicehub/resources/action/schemas.py | 13 +++------ ereuse_devicehub/resources/action/views.py | 9 +++++- ereuse_devicehub/resources/lot/models.py | 11 ------- ereuse_devicehub/resources/lot/views.py | 17 +++++++++++ 6 files changed, 37 insertions(+), 51 deletions(-) diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 4e732ed2..72c29e70 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1377,6 +1377,10 @@ class Trade(JoinedTableMixin, ActionWithMultipleDevices): """ +class InitTransfer(Trade): + """The act of transfer ownership of devices between two agents""" + + class Sell(Trade): """The act of taking money from a buyer in exchange of a device.""" @@ -1445,32 +1449,6 @@ class Receive(JoinedTableMixin, ActionWithMultipleDevices): default=ReceiverRole.Intermediary) -class ShareDeliveryNote(JoinedTableMixin, ActionWithMultipleDevices): - """To share a DeliveryNote to between owners.""" - # New variables for DeliveryNote - supplier = db.Column() # String, nullable, ... - supplier.comment = """Name of the organization/agent that create DeliveryNote.""" - date_delivery_note = db.Column() - date_delivery_note.comment = """Date of note creation.""" - # Is the same of lot id?? - id_delivery_note = db.Column(UUID(as_uuid=True)) - id_delivery_note.comment = """Unique id of lot and delivery note.""" - deposit = db.Column() - deposit.comment = """Total amount of deposit devices in Lot.""" - address_note = db.Column(UUID(as_uuid=True)) - address_note.comment = """Address identifier in the blockchain.""" - - agent_id = Column(UUID(as_uuid=True), - ForeignKey(Agent.id), - nullable=False, - default=lambda: g.user.individual.id) - - -class ConfirmDeliveryNote(JoinedTableMixin, ActionWithMultipleDevices): - """To confirm a DeliveryNote that has been shared.""" - pass - - class Migrate(JoinedTableMixin, ActionWithMultipleDevices): """Moves the devices to a new database/inventory. Devices cannot be modified anymore at the previous database. diff --git a/ereuse_devicehub/resources/action/models.pyi b/ereuse_devicehub/resources/action/models.pyi index c7969094..42780ef0 100644 --- a/ereuse_devicehub/resources/action/models.pyi +++ b/ereuse_devicehub/resources/action/models.pyi @@ -493,6 +493,10 @@ class Trade(ActionWithMultipleDevices): self.confirms = ... # type: Organize +class InitTransfer(Trade): + pass + + class Sell(Trade): pass @@ -529,10 +533,6 @@ class Receive(ActionWithMultipleDevices): self.role = ... # type: ReceiverRole -class ShareDeliveryNote(ActionWithMultipleDevices): - pass - - class Migrate(ActionWithMultipleDevices): other = ... # type: Column diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 0f1e4544..c61abc5b 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -401,6 +401,10 @@ class Trade(ActionWithMultipleDevices): confirms = NestedOn(Organize) +class InitTransfer(Trade): + __doc__ = m.InitTransfer.__doc__ + + class Sell(Trade): __doc__ = m.Sell.__doc__ @@ -434,15 +438,6 @@ class Receive(ActionWithMultipleDevices): role = EnumField(ReceiverRole) -class ShareDeliveryNote(ActionWithMultipleDevices): - __doc__ = m.ShareDeliveryNote.__doc__ - supplier = SanitizedStr(validate=Length(max=STR_SIZE), data_key='supplierName') - date_delivery_note = DateTime(data_key='dateDeliveryNote') - deposit = Integer(data_key='depositValue') - address_note = UUID(dump_only=True) - id_delivery_note = UUID(dump_only=True) - - class Migrate(ActionWithMultipleDevices): __doc__ = m.Migrate.__doc__ other = URL() diff --git a/ereuse_devicehub/resources/action/views.py b/ereuse_devicehub/resources/action/views.py index 30c35633..0c098352 100644 --- a/ereuse_devicehub/resources/action/views.py +++ b/ereuse_devicehub/resources/action/views.py @@ -8,7 +8,8 @@ from teal.marshmallow import ValidationError from teal.resource import View from ereuse_devicehub.db import db -from ereuse_devicehub.resources.action.models import Action, RateComputer, Snapshot, VisualTest +from ereuse_devicehub.resources.action.models import Action, RateComputer, Snapshot, VisualTest, \ + InitTransfer from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate from ereuse_devicehub.resources.device.models import Component, Computer from ereuse_devicehub.resources.enums import SnapshotSoftware @@ -31,6 +32,8 @@ class ActionView(View): if json['type'] == VisualTest.t: pass # TODO JN add compute rate with new visual test and old components device + if json['type'] == InitTransfer.t: + return self.transfer_ownership() Model = db.Model._decl_class_registry.data[json['type']]() action = Model(**a) db.session.add(action) @@ -103,3 +106,7 @@ class ActionView(View): ret.status_code = 201 db.session.commit() return ret + + def transfer_ownership(self): + """Perform a InitTransfer action to change author_id of device""" + pass diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index bb214f6f..d9a037d8 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -27,17 +27,6 @@ class Lot(Thing): closed = db.Column(db.Boolean, default=False, nullable=False) closed.comment = """A closed lot cannot be modified anymore.""" - # New variables for DeliveryNote - supplier = db.Column() # String, nullable, ... - supplier.comment = """Name of the organization/agent that create DeliveryNote.""" - date_delivery_note = db.Column() - date_delivery_note.comment = """Date of note creation.""" - # Is the same of lot id?? - id_delivery_note = db.Column(UUID(as_uuid=True)) - id_delivery_note.comment = """Unique id of lot and delivery note""" - # deposit = db.Column() - # deposit.comment = """Total amount of deposit devices in Lot.""" - devices = db.relationship(Device, backref=db.backref('lots', lazy=True, collection_class=set), secondary=lambda: LotDevice.__table__, diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index 4e3a9f06..dca07061 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -43,6 +43,11 @@ class LotView(View): patch_schema = self.resource_def.SCHEMA(only=('name', 'description'), partial=True) l = request.get_json(schema=patch_schema) lot = Lot.query.filter_by(id=id).one() + if lot.transfer_state.name == 'Initial': + # Initial lot transfer state case + # deposit = self.get_lot_deposit(lot) + # Do something with deposit variable + pass for key, value in l.items(): setattr(lot, key, value) db.session.commit() @@ -123,6 +128,18 @@ 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 change_state(self): + """Change state of Lot""" + pass + + def transfer_ownership_lot(self): + """Perform a InitTransfer action to change author_id of lot""" + pass + class LotBaseChildrenView(View): """Base class for adding / removing children devices and From 0898a41e622643a74c1471d39703b3b4763b237a Mon Sep 17 00:00:00 2001 From: emmdim Date: Sat, 14 Dec 2019 22:11:28 +0100 Subject: [PATCH 12/29] Adds receiver column in Lot model and updates Lot views PATCH to be able to modify the needed values --- ereuse_devicehub/resources/lot/models.py | 1 + ereuse_devicehub/resources/lot/models.pyi | 2 ++ ereuse_devicehub/resources/lot/schemas.py | 3 ++- ereuse_devicehub/resources/lot/views.py | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index d9a037d8..39fd1804 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -71,6 +71,7 @@ class Lot(Thing): author = db.relationship(User, primaryjoin=author_id == User.id) transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state.comment = TransferState.__doc__ + receiver = db.Column(CIText(), default='', nullable=False) 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 80b2bbfb..e084a3fa 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -27,6 +27,7 @@ class Lot(Thing): deposit = ... # type: Column author_id = ... # type: Column transfer_state = ... # type: Column + receiver = ... # type: Column def __init__(self, name: str, closed: bool = closed.default.arg) -> None: super().__init__() @@ -41,6 +42,7 @@ class Lot(Thing): self.children = ... # type: Set[Lot] self.author_id = ... # type: UUID self.transfer_state = ... + self.receiver = ... # 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 670a09a6..b9789021 100644 --- a/ereuse_devicehub/resources/lot/schemas.py +++ b/ereuse_devicehub/resources/lot/schemas.py @@ -23,4 +23,5 @@ class Lot(Thing): description=m.Lot.deposit.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') author_id = f.UUID(dump_only=True) - tranfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) \ No newline at end of file + tranfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) + receiver = SanitizedStr(validate=f.validate.Length(max=42)) \ No newline at end of file diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index dca07061..2e0b7e3e 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -40,7 +40,7 @@ class LotView(View): return ret def patch(self, id): - patch_schema = self.resource_def.SCHEMA(only=('name', 'description'), partial=True) + patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver', 'deposit'), partial=True) l = request.get_json(schema=patch_schema) lot = Lot.query.filter_by(id=id).one() if lot.transfer_state.name == 'Initial': From a85ebf0a8bd16ff887a3306692f874517d6d0947 Mon Sep 17 00:00:00 2001 From: "jordi.nadeu" Date: Mon, 16 Dec 2019 19:17:30 +0100 Subject: [PATCH 13/29] add new attributes in Lot and User models --- ereuse_devicehub/resources/lot/models.py | 7 ++++++- ereuse_devicehub/resources/lot/models.pyi | 4 +++- ereuse_devicehub/resources/lot/schemas.py | 4 ++-- ereuse_devicehub/resources/user/models.py | 1 + ereuse_devicehub/resources/user/models.pyi | 1 + ereuse_devicehub/resources/user/schemas.py | 1 + 6 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index 39fd1804..e7afd49d 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -71,7 +71,12 @@ class Lot(Thing): author = db.relationship(User, primaryjoin=author_id == User.id) transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state.comment = TransferState.__doc__ - receiver = db.Column(CIText(), default='', nullable=False) + receiver_id = db.Column(UUID(as_uuid=False), + db.ForeignKey(User.ethereum_address), + nullable=True, + default=lambda: g.user.ethereum_address) + receiver = db.relationship(User, primaryjoin=receiver_id == User.ethereum_address) + delivery_note_address = db.Column(CIText(), nullable=True) 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 e084a3fa..c320fd07 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -27,7 +27,9 @@ class Lot(Thing): deposit = ... # type: Column author_id = ... # type: Column transfer_state = ... # type: Column - receiver = ... # type: Column + receiver_id = ... # type: Column + receiver = ... # type: relationship + delivery_note_address = ... # type: Column def __init__(self, name: str, closed: bool = closed.default.arg) -> None: super().__init__() diff --git a/ereuse_devicehub/resources/lot/schemas.py b/ereuse_devicehub/resources/lot/schemas.py index b9789021..6155313e 100644 --- a/ereuse_devicehub/resources/lot/schemas.py +++ b/ereuse_devicehub/resources/lot/schemas.py @@ -23,5 +23,5 @@ class Lot(Thing): description=m.Lot.deposit.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') author_id = f.UUID(dump_only=True) - tranfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) - receiver = SanitizedStr(validate=f.validate.Length(max=42)) \ No newline at end of file + transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) + receiver_id = SanitizedStr(validate=f.validate.Length(max=42)) \ No newline at end of file diff --git a/ereuse_devicehub/resources/user/models.py b/ereuse_devicehub/resources/user/models.py index 1dd2cad7..a254aaf0 100644 --- a/ereuse_devicehub/resources/user/models.py +++ b/ereuse_devicehub/resources/user/models.py @@ -24,6 +24,7 @@ class User(Thing): backref=db.backref('users', lazy=True, collection_class=set), secondary=lambda: UserInventory.__table__, collection_class=set) + ethereum_address = Column(UUID(as_uuid=False), unique=True) # todo set restriction that user has, at least, one active db diff --git a/ereuse_devicehub/resources/user/models.pyi b/ereuse_devicehub/resources/user/models.pyi index 6e8d03b9..1cacb052 100644 --- a/ereuse_devicehub/resources/user/models.pyi +++ b/ereuse_devicehub/resources/user/models.pyi @@ -17,6 +17,7 @@ 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: diff --git a/ereuse_devicehub/resources/user/schemas.py b/ereuse_devicehub/resources/user/schemas.py index e8d0d768..00d022ad 100644 --- a/ereuse_devicehub/resources/user/schemas.py +++ b/ereuse_devicehub/resources/user/schemas.py @@ -19,6 +19,7 @@ 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, From 80e9badbeeefd20716ee10eb444cf21989fa7ee9 Mon Sep 17 00:00:00 2001 From: "jordi.nadeu" Date: Mon, 16 Dec 2019 20:38:41 +0100 Subject: [PATCH 14/29] adds to dummy more users with ethereum address --- ereuse_devicehub/dummy/dummy.py | 62 ++++++++++++---------- ereuse_devicehub/resources/enums.py | 2 +- ereuse_devicehub/resources/lot/models.py | 4 +- ereuse_devicehub/resources/lot/models.pyi | 2 +- ereuse_devicehub/resources/lot/schemas.py | 2 +- ereuse_devicehub/resources/user/models.py | 12 +++-- ereuse_devicehub/resources/user/models.pyi | 1 + ereuse_devicehub/resources/user/views.py | 2 +- 8 files changed, 51 insertions(+), 36 deletions(-) diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index 89d39893..3528e40a 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -65,10 +65,11 @@ class Dummy: with click_spinner.spinner(): out = runner.invoke('org', 'add', *self.ORG).output org_id = json.loads(out)['id'] - user = self.user_client('user@dhub.com', '1234') + user1 = self.user_client('user@dhub.com', '1234', 'user1', '0xC79F7fE80B5676fe38D8187b79d55F7A61e702b2') + # todo put user's agent into Org for id in self.TAGS: - user.post({'id': id}, res=Tag) + user1.post({'id': id}, res=Tag) for id, sec in self.ET: runner.invoke('tag', 'add', id, '-p', 'https://t.devicetag.io', @@ -86,7 +87,7 @@ class Dummy: for path in bar: with path.open() as f: snapshot = yaml.load(f) - s, _ = user.post(res=m.Snapshot, data=snapshot) + s, _ = user1.post(res=m.Snapshot, data=snapshot) if s.get('uuid', None) == 'ec23c11b-80b6-42cd-ac5c-73ba7acddbc4': sample_pc = s['device']['id'] else: @@ -94,70 +95,70 @@ class Dummy: if s.get('uuid', None) == 'de4f495e-c58b-40e1-a33e-46ab5e84767e': # oreo # Make one hdd ErasePhysical hdd = next(hdd for hdd in s['components'] if hdd['type'] == 'HardDrive') - user.post({'type': 'ErasePhysical', 'method': 'Shred', 'device': hdd['id']}, + user1.post({'type': 'ErasePhysical', 'method': 'Shred', 'device': hdd['id']}, res=m.Action) assert sample_pc print('PC sample is', sample_pc) # Link tags and eTags for tag, pc in zip((self.TAGS[1], self.TAGS[2], self.ET[0][0], self.ET[1][1]), pcs): - user.put({}, res=Tag, item='{}/device/{}'.format(tag, pc), status=204) + user1.put({}, res=Tag, item='{}/device/{}'.format(tag, pc), status=204) # Perform generic actions for pc, model in zip(pcs, {m.ToRepair, m.Repair, m.ToPrepare, m.Ready, m.ToPrepare, m.Prepare}): - user.post({'type': model.t, 'devices': [pc]}, res=m.Action) + user1.post({'type': model.t, 'devices': [pc]}, res=m.Action) # Perform a Sell to several devices - user.post( + user1.post( { 'type': m.Sell.t, - 'to': user.user['individuals'][0]['id'], + 'to': user1.user['individuals'][0]['id'], 'devices': list(itertools.islice(pcs, len(pcs) // 2)) }, res=m.Action) - parent, _ = user.post(({'name': 'Parent'}), res=Lot) - child, _ = user.post(({'name': 'Child'}), res=Lot) - parent, _ = user.post({}, + parent, _ = user1.post(({'name': 'Parent'}), res=Lot) + child, _ = user1.post(({'name': 'Child'}), res=Lot) + parent, _ = user1.post({}, res=Lot, item='{}/children'.format(parent['id']), query=[('id', child['id'])]) - lot, _ = user.post({}, + lot, _ = user1.post({}, res=Lot, item='{}/devices'.format(child['id']), query=[('id', pc) for pc in itertools.islice(pcs, len(pcs) // 3)]) assert len(lot['devices']) # Keep this at the bottom - inventory, _ = user.get(res=Device) + inventory, _ = user1.get(res=Device) assert len(inventory['items']) - i, _ = user.get(res=Device, query=[('search', 'intel')]) + i, _ = user1.get(res=Device, query=[('search', 'intel')]) assert 12 == len(i['items']) - i, _ = user.get(res=Device, query=[('search', 'pc')]) + i, _ = user1.get(res=Device, query=[('search', 'pc')]) assert 14 == len(i['items']) # Let's create a set of actions for the pc device # Make device Ready - user.post({'type': m.ToPrepare.t, 'devices': [sample_pc]}, res=m.Action) - user.post({'type': m.Prepare.t, 'devices': [sample_pc]}, res=m.Action) - user.post({'type': m.Ready.t, 'devices': [sample_pc]}, res=m.Action) - user.post({'type': m.Price.t, 'device': sample_pc, 'currency': 'EUR', 'price': 85}, + user1.post({'type': m.ToPrepare.t, 'devices': [sample_pc]}, res=m.Action) + user1.post({'type': m.Prepare.t, 'devices': [sample_pc]}, res=m.Action) + user1.post({'type': m.Ready.t, 'devices': [sample_pc]}, res=m.Action) + user1.post({'type': m.Price.t, 'device': sample_pc, 'currency': 'EUR', 'price': 85}, res=m.Action) # todo test reserve - user.post( # Sell device + user1.post( # Sell device { 'type': m.Sell.t, - 'to': user.user['individuals'][0]['id'], + 'to': user1.user['individuals'][0]['id'], 'devices': [sample_pc] }, res=m.Action) # todo Receive - user.get(res=Device, item=sample_pc) # Test + user1.get(res=Device, item=sample_pc) # Test anonymous = self.app.test_client() html, _ = anonymous.get(res=Device, item=sample_pc, accept=ANY) assert 'intel core2 duo cpu' in html @@ -165,12 +166,19 @@ class Dummy: # For netbook: to preapre -> torepair -> to dispose -> disposed print('⭐ Done.') - def user_client(self, email: str, password: str): - user = User(email=email, password=password) - user.individuals.add(Person(name='Timmy')) - db.session.add(user) + def user_client(self, email: str, password: str, name: str, ethereum_address: str): + user1 = User(email=email, password=password, ethereum_address=ethereum_address) + + user2 = User(email='user2@test.com', password='1234', ethereum_address='0x56EbFdbAA98f52027A9776456e4fcD5d91090818') + user3 = User(email='user3@test.com', password='1234', ethereum_address='0xF88618956696aB7e56Cb7bc87d9848E921C4FDaA') + + user1.individuals.add(Person(name=name)) + db.session.add(user1) + db.session.add(user2) + db.session.add(user3) + db.session.commit() - client = UserClient(self.app, user.email, password, + client = UserClient(self.app, user1.email, password, response_wrapper=self.app.response_class) client.login() return client diff --git a/ereuse_devicehub/resources/enums.py b/ereuse_devicehub/resources/enums.py index 81f95c78..50d7ce33 100644 --- a/ereuse_devicehub/resources/enums.py +++ b/ereuse_devicehub/resources/enums.py @@ -371,7 +371,7 @@ class ErasureStandards(Enum): return standards @unique -class TransferState(Enum): +class TransferState(IntEnum): """State of transfer for a given Lot of devices. """ diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index e7afd49d..db90596a 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) + deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) author_id = db.Column(UUID(as_uuid=True), db.ForeignKey(User.id), nullable=False, @@ -71,7 +71,7 @@ class Lot(Thing): author = db.relationship(User, primaryjoin=author_id == User.id) transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state.comment = TransferState.__doc__ - receiver_id = db.Column(UUID(as_uuid=False), + receiver_id = db.Column(CIText(), db.ForeignKey(User.ethereum_address), nullable=True, default=lambda: g.user.ethereum_address) diff --git a/ereuse_devicehub/resources/lot/models.pyi b/ereuse_devicehub/resources/lot/models.pyi index c320fd07..6523c970 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -44,7 +44,7 @@ class Lot(Thing): self.children = ... # type: Set[Lot] self.author_id = ... # type: UUID self.transfer_state = ... - self.receiver = ... # type: str + self.receiver_id = ... # 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 6155313e..95d4190d 100644 --- a/ereuse_devicehub/resources/lot/schemas.py +++ b/ereuse_devicehub/resources/lot/schemas.py @@ -24,4 +24,4 @@ class Lot(Thing): # author_id = NestedOn(s_user.User,only_query='author_id') author_id = f.UUID(dump_only=True) transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) - receiver_id = SanitizedStr(validate=f.validate.Length(max=42)) \ No newline at end of file + receiver_id = SanitizedStr(validate=f.validate.Length(max=42)) diff --git a/ereuse_devicehub/resources/user/models.py b/ereuse_devicehub/resources/user/models.py index a254aaf0..52d32349 100644 --- a/ereuse_devicehub/resources/user/models.py +++ b/ereuse_devicehub/resources/user/models.py @@ -4,6 +4,7 @@ from flask import current_app as app from sqlalchemy import Column from sqlalchemy.dialects.postgresql import UUID from sqlalchemy_utils import EmailType, PasswordType +from citext import CIText from ereuse_devicehub.db import db from ereuse_devicehub.resources.inventory.model import Inventory @@ -24,11 +25,11 @@ class User(Thing): backref=db.backref('users', lazy=True, collection_class=set), secondary=lambda: UserInventory.__table__, collection_class=set) - ethereum_address = Column(UUID(as_uuid=False), unique=True) + 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, inventories=None) -> None: + def __init__(self, email, password=None, ethereum_address=None, inventories=None) -> None: """Creates an user. :param email: :param password: @@ -37,7 +38,7 @@ class User(Thing): inventory. """ inventories = inventories or {Inventory.current} - super().__init__(email=email, password=password, inventories=inventories) + super().__init__(email=email, password=password, ethereum_address=ethereum_address, inventories=inventories) def __repr__(self) -> str: return ''.format(self) @@ -51,6 +52,11 @@ 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 1cacb052..b3454055 100644 --- a/ereuse_devicehub/resources/user/models.pyi +++ b/ereuse_devicehub/resources/user/models.pyi @@ -28,6 +28,7 @@ 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/views.py b/ereuse_devicehub/resources/user/views.py index 7053eea7..19624b80 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')) # type: UserS + user_s = g.resource_def.SCHEMA(only=('email', 'password', 'ethereum_address')) # type: UserS # noinspection PyArgumentList u = request.get_json(schema=user_s) user = User.query.filter_by(email=u['email']).one_or_none() From 619e7f27e996cfb0684b5955e7d93da3dbf02e5e Mon Sep 17 00:00:00 2001 From: emmdim Date: Tue, 17 Dec 2019 17:16:13 +0100 Subject: [PATCH 15/29] Minor updates --- ereuse_devicehub/resources/lot/models.py | 3 +-- ereuse_devicehub/resources/lot/views.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index db90596a..c0782bf3 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -73,8 +73,7 @@ class Lot(Thing): transfer_state.comment = TransferState.__doc__ receiver_id = db.Column(CIText(), db.ForeignKey(User.ethereum_address), - nullable=True, - default=lambda: g.user.ethereum_address) + nullable=True) receiver = db.relationship(User, primaryjoin=receiver_id == User.ethereum_address) delivery_note_address = db.Column(CIText(), nullable=True) diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index 2e0b7e3e..31b8ae96 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -40,7 +40,7 @@ class LotView(View): return ret def patch(self, id): - patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver', 'deposit'), partial=True) + patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_id', 'deposit', 'delivery_note_address'), partial=True) l = request.get_json(schema=patch_schema) lot = Lot.query.filter_by(id=id).one() if lot.transfer_state.name == 'Initial': From ede36eabd6d2be9cf041bfb1c7f96c16c6d58f93 Mon Sep 17 00:00:00 2001 From: JNadeu Date: Tue, 17 Dec 2019 22:30:29 +0100 Subject: [PATCH 16/29] minor fixes --- .gitignore | 3 ++- requirements.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f1de35b5..bed364c2 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,7 @@ ENV/ # Other .idea/ +.vscode/ .DS_Store /app.py -.vscode/ + diff --git a/requirements.txt b/requirements.txt index 725d1524..726d9636 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,4 +32,4 @@ flask-weasyprint==0.5 weasyprint==44 psycopg2-binary==2.8.3 sortedcontainers==2.1.0 -tqdm==4.32.2 \ No newline at end of file +tqdm==4.32.2 From 8be56c93f98520d282c21f2ad98c824b7a81704c Mon Sep 17 00:00:00 2001 From: JNadeu Date: Tue, 17 Dec 2019 23:57:55 +0100 Subject: [PATCH 17/29] adds new attributes on Computer model --- .gitignore | 1 - ereuse_devicehub/resources/device/models.py | 11 +++++++++-- ereuse_devicehub/resources/device/models.pyi | 7 ++++++- ereuse_devicehub/resources/device/schemas.py | 5 ++++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index bed364c2..e5012c76 100644 --- a/.gitignore +++ b/.gitignore @@ -109,4 +109,3 @@ ENV/ .vscode/ .DS_Store /app.py - diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index cb04f425..2cc5333d 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -20,14 +20,14 @@ from sqlalchemy_utils import ColorType from sqlalchemy.dialects.postgresql import UUID from stdnum import imei, meid from teal.db import CASCADE_DEL, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, URL, \ - check_lower, check_range + check_lower, check_range, IntEnum from teal.enums import Layouts from teal.marshmallow import ValidationError from teal.resource import url_for_resource from ereuse_devicehub.db import db from ereuse_devicehub.resources.enums import BatteryTechnology, CameraFacing, ComputerChassis, \ - DataStorageInterface, DisplayTech, PrinterTechnology, RamFormat, RamInterface, Severity + DataStorageInterface, DisplayTech, PrinterTechnology, RamFormat, RamInterface, Severity, TransferState from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing from ereuse_devicehub.resources.user.models import User @@ -387,6 +387,13 @@ class Computer(Device): nullable=False, default=lambda: g.user.id) author = db.relationship(User, primaryjoin=author_id == User.id) + transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) + transfer_state.comment = TransferState.__doc__ + receiver_id = db.Column(CIText(), + db.ForeignKey(User.ethereum_address), + nullable=True) + receiver = db.relationship(User, primaryjoin=receiver_id == User.ethereum_address) + delivery_note_address = db.Column(CIText(), nullable=True) def __init__(self, chassis, **kwargs) -> None: chassis = ComputerChassis(chassis) diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index 77740a76..f50a8577 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -143,13 +143,18 @@ class Computer(DisplayMixin, Device): chassis = ... # type: Column deposit = ... # type: Column author_id = ... # type: Column + transfer_state = ... # type: Column + receiver_id = ... # type: Column + delivery_note_address = ... # type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.components = ... # type: Set[Component] self.actions_parent = ... # type: Set[e.Action] self.chassis = ... # type: ComputerChassis - self.author_id = ... + self.author_id = ... # type: UUID + self.transfer_state = ... + self.receiver_id = ... # type: str @property def actions(self) -> List: diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index ec9b85a2..36f96698 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -1,6 +1,6 @@ import datetime -from marshmallow import post_load, pre_load +from marshmallow import post_load, pre_load, fields as f from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String, UUID from marshmallow.validate import Length, OneOf, Range from sqlalchemy.util import OrderedSet @@ -128,6 +128,9 @@ class Computer(Device): # author_id = NestedOn(s_user.User,only_query='author_id') author_id = UUID(dump_only=True, data_key='author_id') + transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment) + receiver_id = SanitizedStr(validate=f.validate.Length(max=42)) + class Desktop(Computer): __doc__ = m.Desktop.__doc__ From 5748a45d2ce5f29daaf9bdca66813959d68f27d2 Mon Sep 17 00:00:00 2001 From: emmdim Date: Wed, 18 Dec 2019 14:29:25 +0100 Subject: [PATCH 18/29] Propagates PATCH /lot updates to the involved devices --- ereuse_devicehub/resources/lot/views.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index 31b8ae96..197391ef 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -10,10 +10,11 @@ from flask import Response, jsonify, request from marshmallow import Schema as MarshmallowSchema, fields as f from teal.marshmallow import EnumField from teal.resource import View +from sqlalchemy.orm import joinedload from ereuse_devicehub.db import db from ereuse_devicehub.query import things_response -from ereuse_devicehub.resources.device.models import Device +from ereuse_devicehub.resources.device.models import Device, Computer from ereuse_devicehub.resources.lot.models import Lot, Path @@ -40,16 +41,16 @@ class LotView(View): return ret def patch(self, id): - patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_id', 'deposit', 'delivery_note_address'), partial=True) + patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_id', 'deposit', 'delivery_note_address', 'devices'), partial=True) l = request.get_json(schema=patch_schema) lot = Lot.query.filter_by(id=id).one() - if lot.transfer_state.name == 'Initial': - # Initial lot transfer state case - # deposit = self.get_lot_deposit(lot) - # Do something with deposit variable - pass + device_fields = ['transfer_state', 'receiver_id', 'deposit', 'delivery_note_address'] + computers = [x for x in lot.all_devices if isinstance(x, Computer)] for key, value in l.items(): setattr(lot, key, value) + if key in device_fields: + for dev in computers: + setattr(dev, key, value) db.session.commit() return Response(status=204) From c9d4ae964630fe206528c24136f4c2d4db9bc1c0 Mon Sep 17 00:00:00 2001 From: emmdim Date: Wed, 18 Dec 2019 16:37:45 +0100 Subject: [PATCH 19/29] Adds 'author_id' in /lot PATCH --- ereuse_devicehub/resources/lot/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index 197391ef..c4c63638 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -41,10 +41,10 @@ class LotView(View): return ret def patch(self, id): - patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_id', 'deposit', 'delivery_note_address', 'devices'), partial=True) + patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_id', 'deposit', 'delivery_note_address', 'devices', 'author_id'), partial=True) l = request.get_json(schema=patch_schema) lot = Lot.query.filter_by(id=id).one() - device_fields = ['transfer_state', 'receiver_id', 'deposit', 'delivery_note_address'] + device_fields = ['transfer_state', 'receiver_id', 'deposit', 'delivery_note_address', 'author_id'] computers = [x for x in lot.all_devices if isinstance(x, Computer)] for key, value in l.items(): setattr(lot, key, value) From 6ef3e8dded175268d52473f1766e1ff91341d00f Mon Sep 17 00:00:00 2001 From: emmdim Date: Thu, 19 Dec 2019 01:25:29 +0100 Subject: [PATCH 20/29] Updates dummy users --- ereuse_devicehub/dummy/dummy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index 3528e40a..3a4e2a01 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -169,8 +169,8 @@ class Dummy: def user_client(self, email: str, password: str, name: str, ethereum_address: str): user1 = User(email=email, password=password, ethereum_address=ethereum_address) - user2 = User(email='user2@test.com', password='1234', ethereum_address='0x56EbFdbAA98f52027A9776456e4fcD5d91090818') - user3 = User(email='user3@test.com', password='1234', ethereum_address='0xF88618956696aB7e56Cb7bc87d9848E921C4FDaA') + user2 = User(email='user2@dhub.com', password='1234', ethereum_address='0x56EbFdbAA98f52027A9776456e4fcD5d91090818') + user3 = User(email='user3@dhub.com', password='1234', ethereum_address='0xF88618956696aB7e56Cb7bc87d9848E921C4FDaA') user1.individuals.add(Person(name=name)) db.session.add(user1) From dd82f0e2d9f4219adc2788b2167f12e2a03929db Mon Sep 17 00:00:00 2001 From: emmdim Date: Thu, 19 Dec 2019 01:38:03 +0100 Subject: [PATCH 21/29] Changes author_id (uid) to owner_address (ethereum) and receiver_id to receiver_address (just the name) + minor impvrovements --- ereuse_devicehub/resources/device/models.py | 14 +++++++------- ereuse_devicehub/resources/device/models.pyi | 11 ++++++----- ereuse_devicehub/resources/device/schemas.py | 6 +++--- ereuse_devicehub/resources/lot/models.py | 14 +++++++------- ereuse_devicehub/resources/lot/models.pyi | 12 +++++++----- ereuse_devicehub/resources/lot/schemas.py | 5 +++-- ereuse_devicehub/resources/lot/views.py | 4 ++-- 7 files changed, 35 insertions(+), 31 deletions(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 2cc5333d..96fc459b 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -382,18 +382,18 @@ class Computer(Device): It is a subset of the Linux definition of DMI / DMI decode. """ deposit = Column(Integer, check_range('deposit',min=0,max=100), default=0) - author_id = db.Column(UUID(as_uuid=True), - db.ForeignKey(User.id), + owner_address = db.Column(CIText(), + db.ForeignKey(User.ethereum_address), nullable=False, - default=lambda: g.user.id) - author = db.relationship(User, primaryjoin=author_id == User.id) + default=lambda: g.user.ethereum_address) + author = db.relationship(User, primaryjoin=owner_address == User.ethereum_address) transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state.comment = TransferState.__doc__ - receiver_id = db.Column(CIText(), + receiver_address = db.Column(CIText(), db.ForeignKey(User.ethereum_address), nullable=True) - receiver = db.relationship(User, primaryjoin=receiver_id == User.ethereum_address) - delivery_note_address = db.Column(CIText(), nullable=True) + receiver = db.relationship(User, primaryjoin=receiver_address == User.ethereum_address) + deliverynote_address = db.Column(CIText(), nullable=True) def __init__(self, chassis, **kwargs) -> None: chassis = ComputerChassis(chassis) diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index f50a8577..36a9b156 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -142,19 +142,20 @@ class Computer(DisplayMixin, Device): components = ... # type: Column chassis = ... # type: Column deposit = ... # type: Column - author_id = ... # type: Column + owner_address = ... # type: Column transfer_state = ... # type: Column - receiver_id = ... # type: Column - delivery_note_address = ... # type: Column + receiver_address = ... # type: Column + deliverynote_address = ... # type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.components = ... # type: Set[Component] self.actions_parent = ... # type: Set[e.Action] self.chassis = ... # type: ComputerChassis - self.author_id = ... # type: UUID + self.owner_address = ... # type: UUID self.transfer_state = ... - self.receiver_id = ... # type: str + 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 36f96698..d2381216 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -126,10 +126,10 @@ class Computer(Device): data_key='deposit', description=m.Computer.deposit.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') - author_id = UUID(dump_only=True, - data_key='author_id') + owner_address = SanitizedStr(validate=f.validate.Length(max=42)) transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment) - receiver_id = SanitizedStr(validate=f.validate.Length(max=42)) + receiver_address = SanitizedStr(validate=f.validate.Length(max=42)) + deliverynote_address = SanitizedStr(validate=f.validate.Length(max=42)) class Desktop(Computer): diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index c0782bf3..bc1a4aa9 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -64,18 +64,18 @@ class Lot(Thing): descendants. """ deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) - author_id = db.Column(UUID(as_uuid=True), - db.ForeignKey(User.id), + owner_address = db.Column(CIText(), + db.ForeignKey(User.ethereum_address), nullable=False, - default=lambda: g.user.id) - author = db.relationship(User, primaryjoin=author_id == User.id) + default=lambda: g.user.ethereum_address) + owner = db.relationship(User, primaryjoin=owner_address == User.ethereum_address) transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state.comment = TransferState.__doc__ - receiver_id = db.Column(CIText(), + receiver_address = db.Column(CIText(), db.ForeignKey(User.ethereum_address), nullable=True) - receiver = db.relationship(User, primaryjoin=receiver_id == User.ethereum_address) - delivery_note_address = db.Column(CIText(), nullable=True) + receiver = db.relationship(User, primaryjoin=receiver_address == User.ethereum_address) + deliverynote_address = db.Column(CIText(), nullable=True) 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 6523c970..4103c495 100644 --- a/ereuse_devicehub/resources/lot/models.pyi +++ b/ereuse_devicehub/resources/lot/models.pyi @@ -25,11 +25,12 @@ class Lot(Thing): all_devices = ... # type: relationship parents = ... # type: relationship deposit = ... # type: Column - author_id = ... # type: Column + owner_address = ... # type: Column + owner = ... # type: relationship transfer_state = ... # type: Column - receiver_id = ... # type: Column + receiver_address = ... # type: Column receiver = ... # type: relationship - delivery_note_address = ... # type: Column + deliverynote_address = ... # type: Column def __init__(self, name: str, closed: bool = closed.default.arg) -> None: super().__init__() @@ -42,9 +43,10 @@ class Lot(Thing): self.all_devices = ... # type: Set[Device] self.parents = ... # type: Set[Lot] self.children = ... # type: Set[Lot] - self.author_id = ... # type: UUID + self.owner_address = ... # type: UUID self.transfer_state = ... - self.receiver_id = ... # type: str + 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 95d4190d..861ef6a5 100644 --- a/ereuse_devicehub/resources/lot/schemas.py +++ b/ereuse_devicehub/resources/lot/schemas.py @@ -22,6 +22,7 @@ class Lot(Thing): data_key='deposit', description=m.Lot.deposit.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') - author_id = f.UUID(dump_only=True) + owner_address = SanitizedStr(validate=f.validate.Length(max=42)) transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) - receiver_id = SanitizedStr(validate=f.validate.Length(max=42)) + receiver_address = SanitizedStr(validate=f.validate.Length(max=42)) + deliverynote_address = SanitizedStr(validate=f.validate.Length(max=42)) \ No newline at end of file diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index c4c63638..af75a892 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -41,10 +41,10 @@ class LotView(View): return ret def patch(self, id): - patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_id', 'deposit', 'delivery_note_address', 'devices', 'author_id'), partial=True) + patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', '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_id', 'deposit', 'delivery_note_address', 'author_id'] + device_fields = ['transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'owner_address'] computers = [x for x in lot.all_devices if isinstance(x, Computer)] for key, value in l.items(): setattr(lot, key, value) From 2175cd5efa1ac377fac6058aac33043ebfefb127 Mon Sep 17 00:00:00 2001 From: emmdim Date: Thu, 19 Dec 2019 12:27:02 +0100 Subject: [PATCH 22/29] Makes deposit writable and applies range check from 0-100 --- ereuse_devicehub/resources/device/schemas.py | 3 +-- ereuse_devicehub/resources/lot/schemas.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index d2381216..f7d7fcb7 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -122,8 +122,7 @@ class Computer(Device): dump_only=True, collection_class=set, description=m.Computer.privacy.__doc__) - deposit = Integer(dump_only=True, - data_key='deposit', + deposit = Integer(validate=f.validate.Range(min=0, max=100), description=m.Computer.deposit.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') owner_address = SanitizedStr(validate=f.validate.Length(max=42)) diff --git a/ereuse_devicehub/resources/lot/schemas.py b/ereuse_devicehub/resources/lot/schemas.py index 861ef6a5..1cb10b24 100644 --- a/ereuse_devicehub/resources/lot/schemas.py +++ b/ereuse_devicehub/resources/lot/schemas.py @@ -18,8 +18,7 @@ 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(dump_only=True, - data_key='deposit', + deposit = f.Integer(validate=f.validate.Range(min=0, max=100), description=m.Lot.deposit.__doc__) # author_id = NestedOn(s_user.User,only_query='author_id') owner_address = SanitizedStr(validate=f.validate.Length(max=42)) From 862706e2c1aeca3655736b1c024676ab85208239 Mon Sep 17 00:00:00 2001 From: JNadeu Date: Sat, 21 Dec 2019 16:41:23 +0100 Subject: [PATCH 23/29] Adds new action TransferOwnership --- ereuse_devicehub/resources/action/models.py | 3 +++ ereuse_devicehub/resources/action/models.pyi | 4 ++++ ereuse_devicehub/resources/action/schemas.py | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 72c29e70..6e4c0bc5 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1423,6 +1423,9 @@ class DisposeProduct(Trade): # performing :class:`.ToDispose` + :class:`.Receive` to a # ``RecyclingCenter``. +class TransferOwnershipBlockchain(Trade): + """ The act of change owenership of devices between two users (ethereum address)""" + class MakeAvailable(ActionWithMultipleDevices): """The act of setting willingness for trading.""" diff --git a/ereuse_devicehub/resources/action/models.pyi b/ereuse_devicehub/resources/action/models.pyi index 42780ef0..77bd26ab 100644 --- a/ereuse_devicehub/resources/action/models.pyi +++ b/ereuse_devicehub/resources/action/models.pyi @@ -525,6 +525,10 @@ class DisposeProduct(Trade): pass +class TransferOwnershipBlockchain(Trade): + pass + + class Receive(ActionWithMultipleDevices): role = ... # type:Column diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index c61abc5b..cd9e821a 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -433,6 +433,10 @@ class DisposeProduct(Trade): __doc__ = m.DisposeProduct.__doc__ +class TransferOwnershipBlockchain(Trade): + __doc__ = m.TransferOwnershipBlockchain.__doc__ + + class Receive(ActionWithMultipleDevices): __doc__ = m.Receive.__doc__ role = EnumField(ReceiverRole) From 726c520440367071edb1438b2b05b4400fba744d Mon Sep 17 00:00:00 2001 From: JNadeu Date: Sat, 21 Dec 2019 17:51:29 +0100 Subject: [PATCH 24/29] adds in dummy new lots of diferents users --- ereuse_devicehub/dummy/dummy.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index 3a4e2a01..3c12ff3e 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -66,6 +66,8 @@ class Dummy: 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') # todo put user's agent into Org for id in self.TAGS: @@ -117,20 +119,21 @@ class Dummy: 'devices': list(itertools.islice(pcs, len(pcs) // 2)) }, res=m.Action) - - parent, _ = user1.post(({'name': 'Parent'}), res=Lot) - child, _ = user1.post(({'name': 'Child'}), res=Lot) - parent, _ = user1.post({}, - res=Lot, - item='{}/children'.format(parent['id']), - query=[('id', child['id'])]) + lot_user, _ = user1.post({'name': 'LotUser'}, res=Lot) lot, _ = user1.post({}, res=Lot, - item='{}/devices'.format(child['id']), + item='{}/devices'.format(lot_user['id']), query=[('id', pc) for pc in itertools.islice(pcs, len(pcs) // 3)]) assert len(lot['devices']) + # Adding Blockchaing Lot information (delivery note address & transfer state) + + lot_user2, _ = user2.post({'name': 'LotUser2'}, res=Lot) + + lot_user3, _ = user3.post(({'name': 'LotUser3'}), res=Lot) + + # Keep this at the bottom inventory, _ = user1.get(res=Device) assert len(inventory['items']) @@ -147,7 +150,8 @@ class Dummy: user1.post({'type': m.Prepare.t, 'devices': [sample_pc]}, res=m.Action) user1.post({'type': m.Ready.t, 'devices': [sample_pc]}, res=m.Action) user1.post({'type': m.Price.t, 'device': sample_pc, 'currency': 'EUR', 'price': 85}, - res=m.Action) + res=m.Action) + # todo test reserve user1.post( # Sell device { @@ -169,13 +173,8 @@ class Dummy: def user_client(self, email: str, password: str, name: str, ethereum_address: str): user1 = User(email=email, password=password, ethereum_address=ethereum_address) - user2 = User(email='user2@dhub.com', password='1234', ethereum_address='0x56EbFdbAA98f52027A9776456e4fcD5d91090818') - user3 = User(email='user3@dhub.com', password='1234', ethereum_address='0xF88618956696aB7e56Cb7bc87d9848E921C4FDaA') - user1.individuals.add(Person(name=name)) db.session.add(user1) - db.session.add(user2) - db.session.add(user3) db.session.commit() client = UserClient(self.app, user1.email, password, From 769fb5cce3ed56dfc4b51b37e689d6c9a95c5851 Mon Sep 17 00:00:00 2001 From: emmdim Date: Mon, 23 Dec 2019 13:31:54 +0100 Subject: [PATCH 25/29] Adds Transferred event --- ereuse_devicehub/resources/action/__init__.py | 4 ++++ ereuse_devicehub/resources/action/models.py | 4 ++++ ereuse_devicehub/resources/action/models.pyi | 3 +++ ereuse_devicehub/resources/action/schemas.py | 6 ++++++ 4 files changed, 17 insertions(+) diff --git a/ereuse_devicehub/resources/action/__init__.py b/ereuse_devicehub/resources/action/__init__.py index 0a940e5c..d6ef08cc 100644 --- a/ereuse_devicehub/resources/action/__init__.py +++ b/ereuse_devicehub/resources/action/__init__.py @@ -266,3 +266,7 @@ class MigrateToDef(ActionDef): class MigrateFromDef(ActionDef): VIEW = None SCHEMA = schemas.MigrateFrom + +class TransferredDef(ActionDef): + VIEW = None + SCHEMA = schemas.Transferred \ No newline at end of file diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 6e4c0bc5..8c04d708 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1550,3 +1550,7 @@ def update_parent(target: Union[EraseBasic, Test, Install], device: Device, _, _ class InvalidRangeForPrice(ValueError): pass + +class Transferred(ActionWithMultipleDevices): + """Transferred through blockchain.""" + pass \ No newline at end of file diff --git a/ereuse_devicehub/resources/action/models.pyi b/ereuse_devicehub/resources/action/models.pyi index 77bd26ab..61010fc8 100644 --- a/ereuse_devicehub/resources/action/models.pyi +++ b/ereuse_devicehub/resources/action/models.pyi @@ -551,3 +551,6 @@ class MigrateTo(Migrate): class MigrateFrom(Migrate): pass + +class Transferred(ActionWithMultipleDevices): + pass diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index cd9e821a..bb23f7b2 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -453,3 +453,9 @@ class MigrateTo(Migrate): class MigrateFrom(Migrate): __doc__ = m.MigrateFrom.__doc__ + + +class Transferred(ActionWithMultipleDevices): + __doc__ = m.Transferred.__doc__ + + From 01c385c3500c49dcdd3a018f6b3a4e714bad0e33 Mon Sep 17 00:00:00 2001 From: JNadeu Date: Mon, 23 Dec 2019 17:36:20 +0100 Subject: [PATCH 26/29] adds in dummy more lots and devices --- ereuse_devicehub/dummy/dummy.py | 36 ++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index 3c12ff3e..071ae61c 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -119,20 +119,36 @@ class Dummy: 'devices': list(itertools.islice(pcs, len(pcs) // 2)) }, res=m.Action) - lot_user, _ = user1.post({'name': 'LotUser'}, res=Lot) + + lot_user, _ = user1.post({'name': 'LoteStephan'}, res=Lot) + + lot_user2, _ = user1.post({'name': 'LoteSergio'}, res=Lot) + + lot_user3, _ = user1.post({'name': 'LoteManos'}, res=Lot) + + lot_user4, _ = user1.post({'name': 'LoteJordi'}, res=Lot) lot, _ = user1.post({}, res=Lot, item='{}/devices'.format(lot_user['id']), - query=[('id', pc) for pc in itertools.islice(pcs, len(pcs) // 3)]) + query=[('id', pc) for pc in itertools.islice(pcs, 1, 4)]) assert len(lot['devices']) - # Adding Blockchaing Lot information (delivery note address & transfer state) + lot2, _ = user1.post({}, + res=Lot, + item='{}/devices'.format(lot_user2['id']), + query=[('id', pc) for pc in itertools.islice(pcs, 4, 6)]) - lot_user2, _ = user2.post({'name': 'LotUser2'}, res=Lot) - - lot_user3, _ = user3.post(({'name': 'LotUser3'}), res=Lot) + lot3, _ = user1.post({}, + res=Lot, + item='{}/devices'.format(lot_user3['id']), + query=[('id', pc) for pc in itertools.islice(pcs, 11, 14)]) + + lot4, _ = user1.post({}, + res=Lot, + item='{}/devices'.format(lot_user4['id']), + query=[('id', pc) for pc in itertools.islice(pcs, 14, 16)]) # Keep this at the bottom inventory, _ = user1.get(res=Device) @@ -171,13 +187,13 @@ class Dummy: print('⭐ Done.') def user_client(self, email: str, password: str, name: str, ethereum_address: str): - user1 = User(email=email, password=password, ethereum_address=ethereum_address) + user = User(email=email, password=password, ethereum_address=ethereum_address) - user1.individuals.add(Person(name=name)) - db.session.add(user1) + user.individuals.add(Person(name=name)) + db.session.add(user) db.session.commit() - client = UserClient(self.app, user1.email, password, + client = UserClient(self.app, user.email, password, response_wrapper=self.app.response_class) client.login() return client From b831a48b60b8621ac2edd24787ebeb1f5796a646 Mon Sep 17 00:00:00 2001 From: yiorgos marinellis Date: Wed, 26 Feb 2020 20:21:59 +0100 Subject: [PATCH 27/29] Add a dummny DeliveryNote entity --- ereuse_devicehub/config.py | 3 +- .../resources/deliverynote/__init__.py | 30 ++++ .../resources/deliverynote/models.py | 140 ++++++++++++++++++ .../resources/deliverynote/models.pyi | 103 +++++++++++++ .../resources/deliverynote/schemas.py | 13 ++ .../resources/deliverynote/views.py | 87 +++++++++++ 6 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 ereuse_devicehub/resources/deliverynote/__init__.py create mode 100644 ereuse_devicehub/resources/deliverynote/models.py create mode 100644 ereuse_devicehub/resources/deliverynote/models.pyi create mode 100644 ereuse_devicehub/resources/deliverynote/schemas.py create mode 100644 ereuse_devicehub/resources/deliverynote/views.py diff --git a/ereuse_devicehub/config.py b/ereuse_devicehub/config.py index dea5e2a5..be15650d 100644 --- a/ereuse_devicehub/config.py +++ b/ereuse_devicehub/config.py @@ -7,7 +7,7 @@ from teal.config import Config from teal.enums import Currency from teal.utils import import_resource -from ereuse_devicehub.resources import action, agent, inventory, lot, tag, user +from ereuse_devicehub.resources import action, agent, deliverynote, inventory, lot, tag, user from ereuse_devicehub.resources.device import definitions from ereuse_devicehub.resources.documents import documents from ereuse_devicehub.resources.enums import PriceSoftware @@ -20,6 +20,7 @@ class DevicehubConfig(Config): import_resource(tag), import_resource(agent), import_resource(lot), + import_resource(deliverynote), import_resource(documents), import_resource(inventory)), ) diff --git a/ereuse_devicehub/resources/deliverynote/__init__.py b/ereuse_devicehub/resources/deliverynote/__init__.py new file mode 100644 index 00000000..d08e54ef --- /dev/null +++ b/ereuse_devicehub/resources/deliverynote/__init__.py @@ -0,0 +1,30 @@ +import pathlib +from typing import Callable, Iterable, Tuple + +from teal.resource import Converters, Resource + +from ereuse_devicehub.db import db +from ereuse_devicehub.resources.deliverynote import schemas +from ereuse_devicehub.resources.deliverynote.views import DeliveryNoteView + + +class DeliveryNoteDef(Resource): + SCHEMA = schemas.DeliveryNote + VIEW = DeliveryNoteView + # AUTH = True + AUTH = False + ID_CONVERTER = Converters.uuid + + def __init__(self, app, + import_name=__name__.split('.')[0], + static_folder=None, + static_url_path=None, + template_folder=None, + url_prefix=None, + subdomain=None, + url_defaults=None, + root_path=None, + cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): + import pdb; pdb.set_trace() + super().__init__(app, import_name, static_folder, static_url_path, template_folder, + url_prefix, subdomain, url_defaults, root_path, cli_commands) diff --git a/ereuse_devicehub/resources/deliverynote/models.py b/ereuse_devicehub/resources/deliverynote/models.py new file mode 100644 index 00000000..772a7bef --- /dev/null +++ b/ereuse_devicehub/resources/deliverynote/models.py @@ -0,0 +1,140 @@ +import uuid +from datetime import datetime +from typing import Union + +from boltons import urlutils +from citext import CIText +from flask import g +from sqlalchemy import TEXT, Enum as DBEnum +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy_utils import LtreeType +from sqlalchemy_utils.types.ltree import LQUERY +from teal.db import CASCADE_OWN, UUIDLtree, check_range, IntEnum +from teal.resource import url_for_resource + +from ereuse_devicehub.db import create_view, db, exp, f +from ereuse_devicehub.resources.models import Thing +from ereuse_devicehub.resources.user.models import User +from ereuse_devicehub.resources.enums import TransferState + + +class DeliveryNote(Thing): + id = db.Column(UUID(as_uuid=True), primary_key=True) # uuid is generated on init by default + # creator = db.relationship(User, primaryjoin=owner_address == User.ethereum_address) + documentID = db.Column(CIText(), nullable=False) + # supplier = db.relationship(User, primaryjoin=owner_address == User.ethereum_address) + # deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) + # owner_address = db.Column(CIText(), + # db.ForeignKey(User.ethereum_address), + # nullable=False, + # default=lambda: g.user.ethereum_address) + # transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) + # transfer_state.comment = TransferState.__doc__ + # lots = db.relationship(Lot, + # backref=db.backref('deliverynotes', lazy=True, collection_class=set), + # secondary=lambda: LotDevice.__table__, + # lazy=True, + # collection_class=set) + """The **children** devices that the lot has. + + # Note that the lot can have more devices, if they are inside + # descendant lots. + # """ + # parents = db.relationship(lambda: Lot, + # viewonly=True, + # lazy=True, + # collection_class=set, + # secondary=lambda: LotParent.__table__, + # primaryjoin=lambda: Lot.id == LotParent.child_id, + # secondaryjoin=lambda: LotParent.parent_id == Lot.id, + # cascade='refresh-expire', # propagate changes outside ORM + # backref=db.backref('children', + # viewonly=True, + # lazy=True, + # cascade='refresh-expire', + # collection_class=set) + # ) + # """The parent lots.""" + + # all_devices = db.relationship(Device, + # viewonly=True, + # lazy=True, + # collection_class=set, + # secondary=lambda: LotDeviceDescendants.__table__, + # primaryjoin=lambda: Lot.id == LotDeviceDescendants.ancestor_lot_id, + # secondaryjoin=lambda: LotDeviceDescendants.device_id == Device.id) + # """All devices, including components, inside this lot and its + # descendants. + # """ + # deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) + # owner_address = db.Column(CIText(), + # db.ForeignKey(User.ethereum_address), + # nullable=False, + # default=lambda: g.user.ethereum_address) + # owner = db.relationship(User, primaryjoin=owner_address == User.ethereum_address) + # 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) + + def __init__(self) -> None: + """Initializes a delivery note + """ + super().__init__(id=uuid.uuid4()) + + @property + def type(self) -> str: + return self.__class__.__name__ + + @property + def url(self) -> urlutils.URL: + """The URL where to GET this action.""" + return urlutils.URL(url_for_resource(DeliveryNote, item_id=self.id)) + + + # def delete(self): + # """Deletes the lot. + + # This method removes the children lots and children + # devices orphan from this lot and then marks this lot + # for deletion. + # """ + # self.remove_children(*self.children) + # db.session.delete(self) + + # def _refresh_models_with_relationships_to_lots(self): + # session = db.Session.object_session(self) + # for model in session: + # if isinstance(model, (Device, Lot, Path)): + # session.expire(model) + + # def __contains__(self, child: Union['Lot', Device]): + # if isinstance(child, Lot): + # return Path.has_lot(self.id, child.id) + # elif isinstance(child, Device): + # device = db.session.query(LotDeviceDescendants) \ + # .filter(LotDeviceDescendants.device_id == child.id) \ + # .filter(LotDeviceDescendants.ancestor_lot_id == self.id) \ + # .one_or_none() + # return device + # else: + # raise TypeError('Lot only contains devices and lots, not {}'.format(child.__class__)) + + def __repr__(self) -> str: + # return ''.format(self) + return ''.format(self) + + +# class LotDevice(db.Model): +# device_id = db.Column(db.BigInteger, db.ForeignKey(Device.id), primary_key=True) +# lot_id = db.Column(UUID(as_uuid=True), db.ForeignKey(Lot.id), primary_key=True) +# created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) +# author_id = db.Column(UUID(as_uuid=True), +# db.ForeignKey(User.id), +# nullable=False, +# default=lambda: g.user.id) +# author = db.relationship(User, primaryjoin=author_id == User.id) +# author_id.comment = """The user that put the device in the lot.""" diff --git a/ereuse_devicehub/resources/deliverynote/models.pyi b/ereuse_devicehub/resources/deliverynote/models.pyi new file mode 100644 index 00000000..e552bbf4 --- /dev/null +++ b/ereuse_devicehub/resources/deliverynote/models.pyi @@ -0,0 +1,103 @@ +import uuid +from datetime import datetime +from typing import Iterable, Optional, Set, Union +from uuid import UUID + +from boltons import urlutils +from sqlalchemy import Column +from sqlalchemy.orm import Query, relationship +from sqlalchemy_utils import Ltree + +from ereuse_devicehub.db import db +from ereuse_devicehub.resources.device.models import Device +from ereuse_devicehub.resources.models import Thing + +LotQuery = Union[Query, Iterable['Lot']] + + +class Lot(Thing): + id = ... # type: Column + name = ... # type: Column + closed = ... # type: Column + devices = ... # type: relationship + paths = ... # type: relationship + description = ... # type: Column + all_devices = ... # type: relationship + parents = ... # type: relationship + deposit = ... # 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__() + self.id = ... # type: UUID + self.name = ... # type: str + self.closed = ... # type: bool + self.devices = ... # type: Set[Device] + self.paths = ... # type: Set[Path] + self.description = ... # type: str + self.all_devices = ... # type: Set[Device] + self.parents = ... # type: Set[Lot] + self.children = ... # type: Set[Lot] + 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 + + def remove_children(self, *children: Union[Lot, uuid.UUID]): + pass + + @classmethod + def roots(cls) -> LotQuery: + pass + + @property + def descendants(self) -> LotQuery: + pass + + @classmethod + def descendantsq(cls, id) -> LotQuery: + pass + + @property + def url(self) -> urlutils.URL: + pass + + def delete(self): + pass + + +class Path: + id = ... # type: Column + lot_id = ... # type: Column + lot = ... # type: relationship + path = ... # type: Column + created = ... # type: Column + + def __init__(self, lot: Lot) -> None: + super().__init__() + self.id = ... # type: UUID + self.lot = ... # type: Lot + self.path = ... # type: Ltree + self.created = ... # type: datetime + + +class LotDeviceDescendants(db.Model): + device_id = ... # type: Column + ancestor_lot_id = ... # type: Column + parent_lot_id = ... # type: Column + device_parent_id = ... # type: Column + + def __init__(self) -> None: + super().__init__() + self.device_id = ... # type: int + self.ancestor_lot_id = ... # type: UUID + self.parent_lot_id = ... # type: UUID + self.device_parent_id = ... # type: Optional[int] diff --git a/ereuse_devicehub/resources/deliverynote/schemas.py b/ereuse_devicehub/resources/deliverynote/schemas.py new file mode 100644 index 00000000..2152c65b --- /dev/null +++ b/ereuse_devicehub/resources/deliverynote/schemas.py @@ -0,0 +1,13 @@ +from marshmallow import fields as f +from teal.marshmallow import SanitizedStr, URL, EnumField + +from ereuse_devicehub.marshmallow import NestedOn +from ereuse_devicehub.resources.deliverynote import models as m +from ereuse_devicehub.resources.models import STR_SIZE +from ereuse_devicehub.resources.schemas import Thing + + +class DeliveryNote(Thing): + id = f.UUID(dump_only=True) + documentID = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) + url = URL(dump_only=True, description=m.DeliveryNote.url.__doc__) diff --git a/ereuse_devicehub/resources/deliverynote/views.py b/ereuse_devicehub/resources/deliverynote/views.py new file mode 100644 index 00000000..0efe0cf4 --- /dev/null +++ b/ereuse_devicehub/resources/deliverynote/views.py @@ -0,0 +1,87 @@ +import datetime +import uuid +from collections import deque +from enum import Enum +from typing import Dict, List, Set, Union + +import marshmallow as ma +import teal.cache +from flask import Response, jsonify, request +from marshmallow import Schema as MarshmallowSchema, fields as f +from teal.marshmallow import EnumField +from teal.resource import View +from sqlalchemy.orm import joinedload + +from ereuse_devicehub.db import db +from ereuse_devicehub.query import things_response +from ereuse_devicehub.resources.deliverynote.models import DeliveryNote + + +class DeliveryNoteView(View): + class FindArgs(MarshmallowSchema): + """Allowed arguments for the ``find`` + method (GET collection) endpoint + """ + search = f.Str(missing=None) + + def post(self): + l = request.get_json() + dlvnote = DeliveryNote(**l) + db.session.add(dlvnote) + db.session().final_flush() + ret = self.schema.jsonify(dlvnote) + ret.status_code = 201 + db.session.commit() + return ret + + # def patch(self, id): + # patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'devices', 'owner_address'), partial=True) + # d = request.get_json(schema=patch_schema) + # dlvnote = DeliveryNote.query.filter_by(id=id).one() + # device_fields = ['transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'owner_address'] + # computers = [x for x in dlvnote.all_devices if isinstance(x, Computer)] + # for key, value in d.items(): + # setattr(dlvnote, key, value) + # if key in device_fields: + # for dev in computers: + # setattr(dev, key, value) + # db.session.commit() + # return Response(status=204) + + def one(self, id: uuid.UUID): + """Gets one action.""" + import pdb; pdb.set_trace() + deliverynote = DeliveryNote.query.filter_by(id=id).one() # type: DeliveryNote + return self.schema.jsonify(deliverynote) + + @teal.cache.cache(datetime.timedelta(minutes=5)) + def find(self, args: dict): + """Gets deliverynotes. + + By passing the value `UiTree` in the parameter `format` + of the query you get a recursive nested suited for ui-tree:: + + [ + {title: 'lot1', + nodes: [{title: 'child1', nodes:[]}] + ] + + Note that in this format filters are ignored. + + Otherwise it just returns the standard flat view of lots that + you can filter. + """ + query = DeliveryNote.query + if args['search']: + query = query.filter(DeliveryNote.name.ilike(args['search'] + '%')) + dlvnote = query.paginate(per_page=6 if args['search'] else 30) + return things_response( + self.schema.dump(dlvnote.items, many=True, nested=0), + dlvnote.page, dlvnote.per_page, dlvnote.total, dlvnote.prev_num, dlvnote.next_num + ) + + def delete(self, id): + dlvnote = DeliveryNote.query.filter_by(id=id).one() + dlvnote.delete() + db.session.commit() + return Response(status=204) From a387e7590c3ce7948171e9c0b4576a0f3d073887 Mon Sep 17 00:00:00 2001 From: yiorgos marinellis Date: Thu, 27 Feb 2020 18:29:26 +0100 Subject: [PATCH 28/29] Rename Deliverynote and add more fields, no tests --- .../resources/deliverynote/__init__.py | 8 +- .../resources/deliverynote/models.py | 82 +++++-------------- .../resources/deliverynote/schemas.py | 13 ++- .../resources/deliverynote/views.py | 16 ++-- 4 files changed, 45 insertions(+), 74 deletions(-) diff --git a/ereuse_devicehub/resources/deliverynote/__init__.py b/ereuse_devicehub/resources/deliverynote/__init__.py index d08e54ef..75a70f75 100644 --- a/ereuse_devicehub/resources/deliverynote/__init__.py +++ b/ereuse_devicehub/resources/deliverynote/__init__.py @@ -5,12 +5,12 @@ from teal.resource import Converters, Resource from ereuse_devicehub.db import db from ereuse_devicehub.resources.deliverynote import schemas -from ereuse_devicehub.resources.deliverynote.views import DeliveryNoteView +from ereuse_devicehub.resources.deliverynote.views import DeliverynoteView -class DeliveryNoteDef(Resource): - SCHEMA = schemas.DeliveryNote - VIEW = DeliveryNoteView +class DeliverynoteDef(Resource): + SCHEMA = schemas.Deliverynote + VIEW = DeliverynoteView # AUTH = True AUTH = False ID_CONVERTER = Converters.uuid diff --git a/ereuse_devicehub/resources/deliverynote/models.py b/ereuse_devicehub/resources/deliverynote/models.py index 772a7bef..18333e7a 100644 --- a/ereuse_devicehub/resources/deliverynote/models.py +++ b/ereuse_devicehub/resources/deliverynote/models.py @@ -18,67 +18,29 @@ from ereuse_devicehub.resources.user.models import User from ereuse_devicehub.resources.enums import TransferState -class DeliveryNote(Thing): +class Deliverynote(Thing): id = db.Column(UUID(as_uuid=True), primary_key=True) # uuid is generated on init by default - # creator = db.relationship(User, primaryjoin=owner_address == User.ethereum_address) documentID = db.Column(CIText(), nullable=False) - # supplier = db.relationship(User, primaryjoin=owner_address == User.ethereum_address) + creator_id = Column(Integer, ForeignKey(User.id)) + creator = db.relationship(User, primaryjoin=creator_id == User.id) + supplier_id = Column(Integer, ForeignKey(User.id)) + supplier = db.relationship(User, primaryjoin=supplier_id == User.id) + date = db.Column(db.DateTime, nullable=False) # deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) - # owner_address = db.Column(CIText(), - # db.ForeignKey(User.ethereum_address), - # nullable=False, - # default=lambda: g.user.ethereum_address) - # transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) - # transfer_state.comment = TransferState.__doc__ - # lots = db.relationship(Lot, - # backref=db.backref('deliverynotes', lazy=True, collection_class=set), - # secondary=lambda: LotDevice.__table__, - # lazy=True, - # collection_class=set) - """The **children** devices that the lot has. - - # Note that the lot can have more devices, if they are inside - # descendant lots. - # """ - # parents = db.relationship(lambda: Lot, - # viewonly=True, - # lazy=True, - # collection_class=set, - # secondary=lambda: LotParent.__table__, - # primaryjoin=lambda: Lot.id == LotParent.child_id, - # secondaryjoin=lambda: LotParent.parent_id == Lot.id, - # cascade='refresh-expire', # propagate changes outside ORM - # backref=db.backref('children', - # viewonly=True, - # lazy=True, - # cascade='refresh-expire', - # collection_class=set) - # ) - # """The parent lots.""" - - # all_devices = db.relationship(Device, - # viewonly=True, - # lazy=True, - # collection_class=set, - # secondary=lambda: LotDeviceDescendants.__table__, - # primaryjoin=lambda: Lot.id == LotDeviceDescendants.ancestor_lot_id, - # secondaryjoin=lambda: LotDeviceDescendants.device_id == Device.id) - # """All devices, including components, inside this lot and its - # descendants. - # """ - # deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) - # owner_address = db.Column(CIText(), - # db.ForeignKey(User.ethereum_address), - # nullable=False, - # default=lambda: g.user.ethereum_address) - # owner = db.relationship(User, primaryjoin=owner_address == User.ethereum_address) - # 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) + deposit = db.Column(CIText(), nullable=False) + # The following fiels are supposed to be 0:N relationships + # to SnapshotDelivery entity. + # At this stage of implementation they will treated as a + # comma-separated string of the devices expexted/transfered + expected_devices = db.Column(CIText(), nullable=False) + transferred_devices = db.Column(CIText(), nullable=False) + transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) + transfer_state.comment = TransferState.__doc__ + lots = db.relationship(Lot, + backref=db.backref('deliverynotes', lazy=True, collection_class=set), + secondary=lambda: LotDevice.__table__, + lazy=True, + collection_class=set) def __init__(self) -> None: """Initializes a delivery note @@ -92,7 +54,7 @@ class DeliveryNote(Thing): @property def url(self) -> urlutils.URL: """The URL where to GET this action.""" - return urlutils.URL(url_for_resource(DeliveryNote, item_id=self.id)) + return urlutils.URL(url_for_resource(Deliverynote, item_id=self.id)) # def delete(self): @@ -125,7 +87,7 @@ class DeliveryNote(Thing): def __repr__(self) -> str: # return ''.format(self) - return ''.format(self) + return ''.format(self) # class LotDevice(db.Model): diff --git a/ereuse_devicehub/resources/deliverynote/schemas.py b/ereuse_devicehub/resources/deliverynote/schemas.py index 2152c65b..c6f9bf2a 100644 --- a/ereuse_devicehub/resources/deliverynote/schemas.py +++ b/ereuse_devicehub/resources/deliverynote/schemas.py @@ -3,11 +3,20 @@ from teal.marshmallow import SanitizedStr, URL, EnumField from ereuse_devicehub.marshmallow import NestedOn from ereuse_devicehub.resources.deliverynote import models as m +from ereuse_devicehub.resources.user import schemas as s_user from ereuse_devicehub.resources.models import STR_SIZE from ereuse_devicehub.resources.schemas import Thing -class DeliveryNote(Thing): +class Deliverynote(Thing): id = f.UUID(dump_only=True) documentID = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) - url = URL(dump_only=True, description=m.DeliveryNote.url.__doc__) + url = URL(dump_only=True, description=m.Deliverynote.url.__doc__) + creator = NestedOn(s_user.User,only_query='id') + supplier = NestedOn(s_user.User,only_query='id') + # deposit = f.Integer(validate=f.validate.Range(min=0, max=100), + # description=m.Lot.deposit.__doc__) + deposit = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) + expected_devices = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) + transferred_devices = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) + 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 0efe0cf4..75610cd9 100644 --- a/ereuse_devicehub/resources/deliverynote/views.py +++ b/ereuse_devicehub/resources/deliverynote/views.py @@ -14,10 +14,10 @@ from sqlalchemy.orm import joinedload from ereuse_devicehub.db import db from ereuse_devicehub.query import things_response -from ereuse_devicehub.resources.deliverynote.models import DeliveryNote +from ereuse_devicehub.resources.deliverynote.models import Deliverynote -class DeliveryNoteView(View): +class DeliverynoteView(View): class FindArgs(MarshmallowSchema): """Allowed arguments for the ``find`` method (GET collection) endpoint @@ -26,7 +26,7 @@ class DeliveryNoteView(View): def post(self): l = request.get_json() - dlvnote = DeliveryNote(**l) + dlvnote = Deliverynote(**l) db.session.add(dlvnote) db.session().final_flush() ret = self.schema.jsonify(dlvnote) @@ -37,7 +37,7 @@ class DeliveryNoteView(View): # def patch(self, id): # patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'devices', 'owner_address'), partial=True) # d = request.get_json(schema=patch_schema) - # dlvnote = DeliveryNote.query.filter_by(id=id).one() + # dlvnote = Deliverynote.query.filter_by(id=id).one() # device_fields = ['transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'owner_address'] # computers = [x for x in dlvnote.all_devices if isinstance(x, Computer)] # for key, value in d.items(): @@ -51,7 +51,7 @@ class DeliveryNoteView(View): def one(self, id: uuid.UUID): """Gets one action.""" import pdb; pdb.set_trace() - deliverynote = DeliveryNote.query.filter_by(id=id).one() # type: DeliveryNote + deliverynote = Deliverynote.query.filter_by(id=id).one() # type: Deliverynote return self.schema.jsonify(deliverynote) @teal.cache.cache(datetime.timedelta(minutes=5)) @@ -71,9 +71,9 @@ class DeliveryNoteView(View): Otherwise it just returns the standard flat view of lots that you can filter. """ - query = DeliveryNote.query + query = Deliverynote.query if args['search']: - query = query.filter(DeliveryNote.name.ilike(args['search'] + '%')) + query = query.filter(Deliverynote.name.ilike(args['search'] + '%')) dlvnote = query.paginate(per_page=6 if args['search'] else 30) return things_response( self.schema.dump(dlvnote.items, many=True, nested=0), @@ -81,7 +81,7 @@ class DeliveryNoteView(View): ) def delete(self, id): - dlvnote = DeliveryNote.query.filter_by(id=id).one() + dlvnote = Deliverynote.query.filter_by(id=id).one() dlvnote.delete() db.session.commit() return Response(status=204) From 886271c99f32654df24d7723a3e3f96334cb4b45 Mon Sep 17 00:00:00 2001 From: Big Lebowski Date: Sat, 29 Feb 2020 22:58:49 +0100 Subject: [PATCH 29/29] Create, get, list collection for Deliverynote --- .gitignore | 4 +++ .../resources/deliverynote/__init__.py | 5 ++- .../resources/deliverynote/models.py | 34 ++++++++++++++----- .../resources/deliverynote/schemas.py | 4 ++- .../resources/deliverynote/views.py | 12 +++++-- 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index e5012c76..91073ab6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ __pycache__/ *.py[cod] *$py.class +# Vim swap files +*.swp +*.swo + # C extensions *.so diff --git a/ereuse_devicehub/resources/deliverynote/__init__.py b/ereuse_devicehub/resources/deliverynote/__init__.py index 75a70f75..b1c0d130 100644 --- a/ereuse_devicehub/resources/deliverynote/__init__.py +++ b/ereuse_devicehub/resources/deliverynote/__init__.py @@ -11,8 +11,8 @@ from ereuse_devicehub.resources.deliverynote.views import DeliverynoteView class DeliverynoteDef(Resource): SCHEMA = schemas.Deliverynote VIEW = DeliverynoteView - # AUTH = True - AUTH = False + AUTH = True + # AUTH = False ID_CONVERTER = Converters.uuid def __init__(self, app, @@ -25,6 +25,5 @@ class DeliverynoteDef(Resource): url_defaults=None, root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): - import pdb; pdb.set_trace() super().__init__(app, import_name, static_folder, static_url_path, template_folder, url_prefix, subdomain, url_defaults, root_path, cli_commands) diff --git a/ereuse_devicehub/resources/deliverynote/models.py b/ereuse_devicehub/resources/deliverynote/models.py index 18333e7a..221b3478 100644 --- a/ereuse_devicehub/resources/deliverynote/models.py +++ b/ereuse_devicehub/resources/deliverynote/models.py @@ -15,17 +15,25 @@ from teal.resource import url_for_resource from ereuse_devicehub.db import create_view, db, exp, f from ereuse_devicehub.resources.models import Thing from ereuse_devicehub.resources.user.models import User +from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.enums import TransferState class Deliverynote(Thing): id = db.Column(UUID(as_uuid=True), primary_key=True) # uuid is generated on init by default - documentID = db.Column(CIText(), nullable=False) - creator_id = Column(Integer, ForeignKey(User.id)) + document_id = db.Column(CIText(), nullable=False) + creator_id = db.Column(UUID(as_uuid=True), + db.ForeignKey(User.id), + nullable=False, + default=lambda: g.user.id) creator = db.relationship(User, primaryjoin=creator_id == User.id) - supplier_id = Column(Integer, ForeignKey(User.id)) - supplier = db.relationship(User, primaryjoin=supplier_id == User.id) - date = db.Column(db.DateTime, nullable=False) + supplier_email = db.Column(CIText(), + db.ForeignKey(User.email), + nullable=False, + default=lambda: g.user.email) + supplier = db.relationship(User, primaryjoin=lambda: Deliverynote.supplier_email == User.email) + # supplier = db.relationship(User) + date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) # deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0) deposit = db.Column(CIText(), nullable=False) # The following fiels are supposed to be 0:N relationships @@ -36,16 +44,26 @@ class Deliverynote(Thing): transferred_devices = db.Column(CIText(), nullable=False) transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state.comment = TransferState.__doc__ + lot_id = db.Column(UUID(as_uuid=True), + db.ForeignKey(Lot.id), + nullable=False) lots = db.relationship(Lot, backref=db.backref('deliverynotes', lazy=True, collection_class=set), - secondary=lambda: LotDevice.__table__, lazy=True, + primaryjoin=Lot.id == lot_id, collection_class=set) - def __init__(self) -> None: + def __init__(self, document_id: str, deposit: str, + supplier_email: str, + expected_devices: str, + transferred_devices: str) -> None: """Initializes a delivery note """ - super().__init__(id=uuid.uuid4()) + super().__init__(id=uuid.uuid4(), + document_id=document_id, deposit=deposit, + supplier_email=supplier_email, + expected_devices=expected_devices, + transferred_devices=transferred_devices) @property def type(self) -> str: diff --git a/ereuse_devicehub/resources/deliverynote/schemas.py b/ereuse_devicehub/resources/deliverynote/schemas.py index c6f9bf2a..7cabb6bd 100644 --- a/ereuse_devicehub/resources/deliverynote/schemas.py +++ b/ereuse_devicehub/resources/deliverynote/schemas.py @@ -6,13 +6,15 @@ from ereuse_devicehub.resources.deliverynote import models as m from ereuse_devicehub.resources.user import schemas as s_user from ereuse_devicehub.resources.models import STR_SIZE from ereuse_devicehub.resources.schemas import Thing +from ereuse_devicehub.resources.enums import TransferState class Deliverynote(Thing): id = f.UUID(dump_only=True) - documentID = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) + document_id = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) url = URL(dump_only=True, description=m.Deliverynote.url.__doc__) creator = NestedOn(s_user.User,only_query='id') + supplier_email = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) supplier = NestedOn(s_user.User,only_query='id') # deposit = f.Integer(validate=f.validate.Range(min=0, max=100), # description=m.Lot.deposit.__doc__) diff --git a/ereuse_devicehub/resources/deliverynote/views.py b/ereuse_devicehub/resources/deliverynote/views.py index 75610cd9..c0786134 100644 --- a/ereuse_devicehub/resources/deliverynote/views.py +++ b/ereuse_devicehub/resources/deliverynote/views.py @@ -15,6 +15,7 @@ from sqlalchemy.orm import joinedload from ereuse_devicehub.db import db from ereuse_devicehub.query import things_response from ereuse_devicehub.resources.deliverynote.models import Deliverynote +from ereuse_devicehub.resources.lot.models import Lot class DeliverynoteView(View): @@ -25,8 +26,14 @@ class DeliverynoteView(View): search = f.Str(missing=None) def post(self): - l = request.get_json() - dlvnote = Deliverynote(**l) + # Create delivery note + dn = request.get_json() + dlvnote = Deliverynote(**dn) + # Create a lot + lot_name = dlvnote.supplier_email + "_" + datetime.datetime.utcnow().strftime("%B-%d-%Y") + new_lot = Lot(name=lot_name) + dlvnote.lot_id = new_lot.id + db.session.add(new_lot) db.session.add(dlvnote) db.session().final_flush() ret = self.schema.jsonify(dlvnote) @@ -50,7 +57,6 @@ class DeliverynoteView(View): def one(self, id: uuid.UUID): """Gets one action.""" - import pdb; pdb.set_trace() deliverynote = Deliverynote.query.filter_by(id=id).one() # type: Deliverynote return self.schema.jsonify(deliverynote)