diff --git a/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py b/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py index 5656595e..19e65087 100644 --- a/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py +++ b/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py @@ -115,8 +115,20 @@ def upgrade(): op.create_index(op.f('ix_trade_document_created'), 'trade_document', ['created'], unique=False, schema=f'{get_inv()}') op.create_index(op.f('ix_trade_document_updated'), 'trade_document', ['updated'], unique=False, schema=f'{get_inv()}') + op.create_table('confirm_document', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), + + sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), + sa.ForeignKeyConstraint(['action_id'], [f'{get_inv()}.action.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['common.user.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) def downgrade(): op.drop_table('action_trade_document', schema=f'{get_inv()}') + op.drop_table('confirm_document', schema=f'{get_inv()}') op.drop_table('trade_document', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/action/__init__.py b/ereuse_devicehub/resources/action/__init__.py index 368b4528..ef19f907 100644 --- a/ereuse_devicehub/resources/action/__init__.py +++ b/ereuse_devicehub/resources/action/__init__.py @@ -270,6 +270,19 @@ class TradeDef(ActionDef): SCHEMA = schemas.Trade +class ConfirmDocumentDef(ActionDef): + VIEW = None + SCHEMA = schemas.ConfirmDocument + + +class RevokeDocumentDef(ActionDef): + VIEW = None + SCHEMA = schemas.RevokeDocument + + + + + class CancelTradeDef(ActionDef): VIEW = None SCHEMA = schemas.CancelTrade diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 60c6f91f..debe4a55 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -296,7 +296,7 @@ class ActionDevice(db.Model): primary_key=True) -class ActionWithMultipleTradeDocuments(Action): +class ActionWithMultipleTradeDocuments(ActionWithMultipleDevices): documents = relationship(TradeDocument, backref=backref('actions_docs', lazy=True, **_sorted_actions), secondary=lambda: ActionTradeDocument.__table__, @@ -1466,14 +1466,18 @@ class ConfirmDocument(JoinedTableMixin, ActionWithMultipleTradeDocuments): lazy=True, order_by=lambda: Action.end_time, collection_class=list), - primaryjoin='Confirm.action_id == Action.id') + primaryjoin='ConfirmDocument.action_id == Action.id') def __repr__(self) -> str: if self.action.t in ['Trade']: origin = 'To' if self.user == self.action.user_from: origin = 'From' - return '<{0.t} {0.id} accepted by {1}>'.format(self, origin) + return '<{0.t}app/views/inventory/ {0.id} accepted by {1}>'.format(self, origin) + + +class RevokeDocument(ConfirmDocument): + pass class Confirm(JoinedTableMixin, ActionWithMultipleDevices): @@ -1516,7 +1520,7 @@ class ConfirmRevoke(Confirm): return '<{0.t} {0.id} accepted by {0.user}>'.format(self) -class Trade(JoinedTableMixin, ActionWithMultipleDevices): +class Trade(JoinedTableMixin, ActionWithMultipleTradeDocuments): """Trade actions log the political exchange of devices between users. Every time a trade action is performed, the old user looses its political possession, for example ownership, in favor of another diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 7d935750..89ca4c07 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -55,15 +55,19 @@ class Action(Thing): class ActionWithOneDevice(Action): - __doc__ = m.ActionWithOneDevice.__doc__ - doc = NestedOn(s_document.TradeDocument, only_query='id') - - -class ActionWithOneDocument(Action): __doc__ = m.ActionWithOneDevice.__doc__ device = NestedOn(s_device.Device, only_query='id') +class ActionWithMultipleDocuments(Action): + __doc__ = m.ActionWithMultipleTradeDocuments.__doc__ + documents = NestedOn(s_document.TradeDocument, + many=True, + required=True, # todo test ensuring len(devices) >= 1 + only_query='id', + collection_class=OrderedSet) + + class ActionWithMultipleDevices(Action): __doc__ = m.ActionWithMultipleDevices.__doc__ devices = NestedOn(s_device.Device, @@ -476,7 +480,7 @@ class Confirm(ActionWithMultipleDevices): raise ValidationError(txt) -class ConfirmDocument(ActionWithOneDocument): +class ConfirmDocument(ActionWithMultipleDocuments): __doc__ = m.Confirm.__doc__ action = NestedOn('Action', only_query='id') @@ -580,8 +584,8 @@ class Revoke(ActionWithMultipleDevices): raise ValidationError(txt) -class RevokeDocument(ActionWithOneDocument): - __doc__ = m.Revoke.__doc__ +class RevokeDocument(ActionWithMultipleDocuments): + __doc__ = m.RevokeDocument.__doc__ action = NestedOn('Action', only_query='id') @validates_schema @@ -598,7 +602,8 @@ class RevokeDocument(ActionWithOneDocument): This is not checked in the view becouse the list of documents is inmutable """ - if not data['devices'] == OrderedSet(): + import pdb; pdb.set_trace() + if not data['documents'] == OrderedSet(): return documents = [] @@ -684,7 +689,7 @@ class ConfirmRevoke(ActionWithMultipleDevices): raise ValidationError(txt) -class ConfirmRevokeDocument(ActionWithOneDocument): +class ConfirmRevokeDocument(ActionWithMultipleDocuments): __doc__ = m.ConfirmRevoke.__doc__ action = NestedOn('Action', only_query='id') diff --git a/ereuse_devicehub/resources/action/views/trade.py b/ereuse_devicehub/resources/action/views/trade.py index 1a2eb4d1..6e9d71f5 100644 --- a/ereuse_devicehub/resources/action/views/trade.py +++ b/ereuse_devicehub/resources/action/views/trade.py @@ -5,7 +5,8 @@ from sqlalchemy.util import OrderedSet from teal.marshmallow import ValidationError from ereuse_devicehub.db import db -from ereuse_devicehub.resources.action.models import Trade, Confirm, ConfirmRevoke, Revoke +from ereuse_devicehub.resources.action.models import (Trade, Confirm, ConfirmRevoke, + Revoke, RevokeDocument, ConfirmDocument) from ereuse_devicehub.resources.user.models import User from ereuse_devicehub.resources.lot.views import delete_from_trade @@ -152,7 +153,7 @@ class ConfirmMixin(): self.schema = schema a = resource_def.schema.load(data) self.validate(a) - if not a['devices'] and not a['documents']: + if not a['devices']: raise ValidationError('Devices not exist.') self.model = self.Model(**a) @@ -267,3 +268,134 @@ class ConfirmRevokeView(ConfirmMixin): dev.reset_owner() trade.lot.devices.difference_update(devices) + + +class ConfirmDocumentMixin(): + """ + Very Important: + ============== + All of this Views than inherit of this class is executed for users + than is not owner of the Trade action. + + The owner of Trade action executed this actions of confirm and revoke from the + lot + + """ + + Model = None + + def __init__(self, data, resource_def, schema): + # import pdb; pdb.set_trace() + self.schema = schema + a = resource_def.schema.load(data) + self.validate(a) + if not a['documents']: + raise ValidationError('Documents not exist.') + self.model = self.Model(**a) + + def post(self): + db.session().final_flush() + ret = self.schema.jsonify(self.model) + ret.status_code = 201 + db.session.commit() + return ret + + +class ConfirmDocumentView(ConfirmDocumentMixin): + """Handler for manager the Confirmation register from post + + request_confirm = { + 'type': 'Confirm', + 'action': trade.id, + 'devices': [device_id] + } + """ + + Model = Confirm + + def validate(self, data): + """If there are one device than have one confirmation, + then remove the list this device of the list of devices of this action + """ + real_devices = [] + for dev in data['devices']: + ac = dev.last_action_trading + if ac.type == Confirm.t and not ac.user == g.user: + real_devices.append(dev) + + data['devices'] = OrderedSet(real_devices) + + # Change the owner for every devices + for dev in data['devices']: + user_to = data['action'].user_to + dev.change_owner(user_to) + + +class RevokeDocumentView(ConfirmDocumentMixin): + """Handler for manager the Revoke register from post + + request_revoke = { + 'type': 'Revoke', + 'action': trade.id, + 'devices': [device_id], + } + + """ + + Model = RevokeDocument + + def __init__(self, data, resource_def, schema): + self.schema = schema + a = resource_def.schema.load(data) + self.validate(a) + + def validate(self, data): + """All devices need to have the status of DoubleConfirmation.""" + + ### check ### + if not data['documents']: + raise ValidationError('Documents not exist.') + + for doc in data['documents']: + if not doc.trading == 'Confirmed': + txt = 'Some of documents do not have enough to confirm for to do a revoke' + ValidationError(txt) + ### End check ### + + +class ConfirmRevokeDocumentView(ConfirmDocumentMixin): + """Handler for manager the Confirmation register from post + + request_confirm_revoke = { + 'type': 'ConfirmRevoke', + 'action': action_revoke.id, + 'devices': [device_id] + } + + """ + + Model = ConfirmRevoke + + def validate(self, data): + """All devices need to have the status of revoke.""" + + if not data['action'].type == 'Revoke': + txt = 'Error: this action is not a revoke action' + ValidationError(txt) + + for dev in data['devices']: + if not dev.trading == 'Revoke': + txt = 'Some of devices do not have revoke to confirm' + ValidationError(txt) + + devices = OrderedSet(data['devices']) + data['devices'] = devices + + # Change the owner for every devices + # data['action'] == 'Revoke' + + trade = data['action'].action + for dev in devices: + dev.reset_owner() + + trade.lot.devices.difference_update(devices) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 61363ed8..d586a102 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -625,11 +625,11 @@ class Computer(Device): It is a subset of the Linux definition of DMI / DMI decode. """ amount = Column(Integer, check_range('amount', min=0, max=100), default=0) - owner_id = db.Column(UUID(as_uuid=True), - db.ForeignKey(User.id), - nullable=False, - default=lambda: g.user.id) - author = db.relationship(User, primaryjoin=owner_id == User.id) + # owner_id = db.Column(UUID(as_uuid=True), + # db.ForeignKey(User.id), + # nullable=False, + # default=lambda: g.user.id) + # author = db.relationship(User, primaryjoin=owner_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=True), diff --git a/ereuse_devicehub/resources/tradedocument/models.py b/ereuse_devicehub/resources/tradedocument/models.py index 5b0939c8..b38e7374 100644 --- a/ereuse_devicehub/resources/tradedocument/models.py +++ b/ereuse_devicehub/resources/tradedocument/models.py @@ -94,8 +94,9 @@ class TradeDocument(Thing): """The trading state, or None if no Trade action has ever been performed to this device. This extract the posibilities for to do""" - confirm = 'Confirm' - to_confirm = 'ToConfirm' + # import pdb; pdb.set_trace() + confirm = 'ConfirmDocument' + to_confirm = 'To Confirm' revoke = 'Revoke' if not self.actions: @@ -105,16 +106,13 @@ class TradeDocument(Thing): actions = list(reversed(actions)) ac = actions[0] - if ac.type == confirm: - return confirm - if ac.type == revoke: return revoke if ac.type == confirm: if ac.user == self.owner: - return confirm - return to_confirm + return to_confirm + return 'Confirmed' def last_action_of(self, *types): """Gets the last action of the given types. diff --git a/ereuse_devicehub/resources/tradedocument/views.py b/ereuse_devicehub/resources/tradedocument/views.py index fa66b481..4222eceb 100644 --- a/ereuse_devicehub/resources/tradedocument/views.py +++ b/ereuse_devicehub/resources/tradedocument/views.py @@ -6,7 +6,7 @@ from teal.resource import View from ereuse_devicehub.db import db from ereuse_devicehub.resources.tradedocument.models import TradeDocument -from ereuse_devicehub.resources.action.models import Confirm, Revoke +from ereuse_devicehub.resources.action.models import ConfirmDocument from ereuse_devicehub.resources.hash_reports import ReportHash @@ -28,7 +28,7 @@ class TradeDocumentView(View): trade = doc.lot.trade if trade: trade.documents.add(doc) - confirm = Confirm(action=trade, + confirm = ConfirmDocument(action=trade, user=g.user, devices=set(), documents={doc})