diff --git a/ereuse_devicehub/migrations/versions/d22d230d2850_adding_author_action_device.py b/ereuse_devicehub/migrations/versions/d22d230d2850_adding_author_action_device.py new file mode 100644 index 00000000..ed58f669 --- /dev/null +++ b/ereuse_devicehub/migrations/versions/d22d230d2850_adding_author_action_device.py @@ -0,0 +1,43 @@ +"""adding author action_device + +Revision ID: d22d230d2850 +Revises: 1bb2b5e0fae7 +Create Date: 2021-11-10 17:37:12.304853 + +""" +import sqlalchemy as sa +from alembic import context +from alembic import op +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'd22d230d2850' +down_revision = '1bb2b5e0fae7' +branch_labels = None +depends_on = None + + +def get_inv(): + INV = context.get_x_argument(as_dictionary=True).get('inventory') + if not INV: + raise ValueError("Inventory value is not specified") + return INV + + +def upgrade(): + op.add_column('action_device', + sa.Column('author_id', + postgresql.UUID(), + nullable=True), + schema=f'{get_inv()}') + op.create_foreign_key("fk_action_device_author", + "action_device", "user", + ["author_id"], ["id"], + ondelete="SET NULL", + source_schema=f'{get_inv()}', + referent_schema='common') + + +def downgrade(): + op.drop_constraint("fk_action_device_author", "device", type_="foreignkey", schema=f'{get_inv()}') + op.drop_column('action_device', 'author_id', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/action/__init__.py b/ereuse_devicehub/resources/action/__init__.py index e70bd18b..0d89f557 100644 --- a/ereuse_devicehub/resources/action/__init__.py +++ b/ereuse_devicehub/resources/action/__init__.py @@ -280,16 +280,16 @@ class ConfirmDef(ActionDef): SCHEMA = schemas.Confirm -class ConfirmRevokeDef(ActionDef): - VIEW = None - SCHEMA = schemas.ConfirmRevoke - - class RevokeDef(ActionDef): VIEW = None SCHEMA = schemas.Revoke +class ConfirmRevokeDef(ActionDef): + VIEW = None + SCHEMA = schemas.ConfirmRevoke + + class TradeDef(ActionDef): VIEW = None SCHEMA = schemas.Trade diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index e1449afb..e4e8c3b6 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -317,6 +317,14 @@ class ActionDevice(db.Model): index=True, server_default=db.text('CURRENT_TIMESTAMP')) created.comment = """When Devicehub created this.""" + author_id = Column(UUID(as_uuid=True), + ForeignKey(User.id), + nullable=False, + default=lambda: g.user.id) + # todo compute the org + author = relationship(User, + backref=backref('authored_actions_device', lazy=True, collection_class=set), + primaryjoin=author_id == User.id) def __init__(self, **kwargs) -> None: self.created = kwargs.get('created', datetime.now(timezone.utc)) @@ -1610,11 +1618,11 @@ class Revoke(Confirm): """Users can revoke one confirmation of one action trade""" -class ConfirmRevoke(Confirm): - """Users can confirm and accept one action revoke""" +# class ConfirmRevoke(Confirm): +# """Users can confirm and accept one action revoke""" - def __repr__(self) -> str: - return '<{0.t} {0.id} accepted by {0.user}>'.format(self) +# def __repr__(self) -> str: +# return '<{0.t} {0.id} accepted by {0.user}>'.format(self) class Trade(JoinedTableMixin, ActionWithMultipleTradeDocuments): diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 1836568d..1f7f3fef 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -445,16 +445,13 @@ class ActionStatus(Action): @post_load def put_rol_user(self, data: dict): for dev in data['devices']: - if dev.trading in [None, 'Revoke', 'ConfirmRevoke']: - return data - trades = [ac for ac in dev.actions if ac.t == 'Trade'] if not trades: return data trade = trades[-1] - if trade.user_to != g.user: + if trade.user_from == g.user: data['rol_user'] = trade.user_to data['trade'] = trade @@ -588,6 +585,10 @@ class Revoke(ActionWithMultipleDevices): raise ValidationError(txt) +class ConfirmRevoke(Revoke): + pass + + class ConfirmDocument(ActionWithMultipleDocuments): __doc__ = m.Confirm.__doc__ action = NestedOn('Action', only_query='id') @@ -642,7 +643,7 @@ class RevokeDocument(ActionWithMultipleDocuments): class ConfirmRevokeDocument(ActionWithMultipleDocuments): - __doc__ = m.ConfirmRevoke.__doc__ + __doc__ = m.ConfirmRevokeDocument.__doc__ action = NestedOn('Action', only_query='id') @validates_schema @@ -669,66 +670,6 @@ class ConfirmRevokeDocument(ActionWithMultipleDocuments): data['action'] = doc.actions[-1] -class ConfirmRevoke(ActionWithMultipleDevices): - __doc__ = m.ConfirmRevoke.__doc__ - action = NestedOn('Action', only_query='id') - - @validates_schema - def validate_revoke(self, data: dict): - for dev in data['devices']: - # if device not exist in the Trade, then this query is wrong - if not dev in data['action'].devices: - txt = "Device {} not exist in the trade".format(dev.devicehub_id) - raise ValidationError(txt) - - for doc in data.get('documents', []): - # if document not exist in the Trade, then this query is wrong - if not doc in data['action'].documents: - txt = "Document {} not exist in the trade".format(doc.file_name) - raise ValidationError(txt) - - @validates_schema - def validate_docs(self, data): - """Check if there are or no one before confirmation, - This is not checked in the view becouse the list of documents is inmutable - - """ - if not data['devices'] == OrderedSet(): - return - - documents = [] - for doc in data['documents']: - actions = copy.copy(doc.actions) - actions.reverse() - for ac in actions: - if ac == data['action']: - # If document have the last action the action for confirm - documents.append(doc) - break - - if ac.t == 'Revoke' and not ac.user == g.user: - # If document is revoke before you can Confirm now - # and revoke is an action of one other user - documents.append(doc) - break - - if ac.t == ConfirmRevoke.t and ac.user == g.user: - # If document is confirmed we don't need confirmed again - break - - if ac.t == Confirm.t: - # if onwer of trade confirm again before than this user Confirm the - # revoke, then is not possible confirm the revoke - # - # If g.user confirm the trade before do a ConfirmRevoke - # then g.user can not to do the ConfirmRevoke more - break - - if not documents: - txt = 'No there are documents with revoke for confirm' - raise ValidationError(txt) - - class Trade(ActionWithMultipleDevices): __doc__ = m.Trade.__doc__ date = DateTime(data_key='date', required=False) diff --git a/ereuse_devicehub/resources/action/views/trade.py b/ereuse_devicehub/resources/action/views/trade.py index 16f2572b..87a5dc81 100644 --- a/ereuse_devicehub/resources/action/views/trade.py +++ b/ereuse_devicehub/resources/action/views/trade.py @@ -3,7 +3,7 @@ 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, +from ereuse_devicehub.resources.action.models import (Trade, Confirm, Revoke, RevokeDocument, ConfirmDocument, ConfirmRevokeDocument) from ereuse_devicehub.resources.user.models import User @@ -181,17 +181,17 @@ class ConfirmView(ConfirmMixin): then remove the list this device of the list of devices of this action """ real_devices = [] + trade = data['action'] + lot = trade.lot 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) + if dev.trading(lot, simple=True) not in ['NeedConfirmation', 'NeedConfirmRevoke']: + raise ValidationError('Some devices not possible confirm.') # Change the owner for every devices for dev in data['devices']: - user_to = data['action'].user_to - dev.change_owner(user_to) + if dev.trading(lot) == 'NeedConfirmation': + user_to = data['action'].user_to + dev.change_owner(user_to) class RevokeView(ConfirmMixin): @@ -215,57 +215,12 @@ class RevokeView(ConfirmMixin): def validate(self, data): """All devices need to have the status of DoubleConfirmation.""" - ### check ### - if not data['devices']: + devices = data['devices'] + if not devices: raise ValidationError('Devices not exist.') - for dev in data['devices']: - if not dev.trading == 'TradeConfirmed': - txt = 'Some of devices do not have enough to confirm for to do a revoke' - ValidationError(txt) - ### End check ### - - ids = {d.id for d in data['devices']} lot = data['action'].lot - self.model = delete_from_trade(lot, ids) - - -class ConfirmRevokeView(ConfirmMixin): - """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) + self.model = delete_from_trade(lot, devices) class ConfirmDocumentMixin(): diff --git a/ereuse_devicehub/resources/action/views/views.py b/ereuse_devicehub/resources/action/views/views.py index 415effb8..88c95438 100644 --- a/ereuse_devicehub/resources/action/views/views.py +++ b/ereuse_devicehub/resources/action/views/views.py @@ -15,7 +15,7 @@ from ereuse_devicehub.db import db from ereuse_devicehub.query import things_response from ereuse_devicehub.resources.action.models import (Action, Snapshot, VisualTest, InitTransfer, Live, Allocate, Deallocate, - Trade, Confirm, ConfirmRevoke, Revoke) + Trade, Confirm, Revoke) from ereuse_devicehub.resources.action.views import trade as trade_view from ereuse_devicehub.resources.action.views.snapshot import SnapshotView, save_json, move_json from ereuse_devicehub.resources.action.views.documents import ErasedView @@ -235,9 +235,9 @@ class ActionView(View): revoke = trade_view.RevokeView(json, resource_def, self.schema) return revoke.post() - if json['type'] == ConfirmRevoke.t: - confirm_revoke = trade_view.ConfirmRevokeView(json, resource_def, self.schema) - return confirm_revoke.post() + if json['type'] == 'ConfirmRevoke': + revoke = trade_view.RevokeView(json, resource_def, self.schema) + return revoke.post() if json['type'] == 'RevokeDocument': revoke = trade_view.RevokeDocumentView(json, resource_def, self.schema) diff --git a/ereuse_devicehub/resources/device/metrics.py b/ereuse_devicehub/resources/device/metrics.py index 3365e6cc..f9399f95 100644 --- a/ereuse_devicehub/resources/device/metrics.py +++ b/ereuse_devicehub/resources/device/metrics.py @@ -89,7 +89,6 @@ class Metrics(MetricsMix): trade['status_receiver_created'] = self.act.created return - # import pdb; pdb.set_trace() # necesitamos poder poner un cambio de estado de un trade mas antiguo que last_trade # lo mismo con confirm @@ -148,9 +147,7 @@ class Metrics(MetricsMix): if the action is one trade action, is possible than have a list of confirmations. Get the doble confirm for to know if this trade is confirmed or not. """ - if self.device.trading == 'TradeConfirmed': - return True - return False + return self.device.trading(self.act.lot, simple=True) def get_trade(self): """ diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index d088d4ed..e7297fcd 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -1,5 +1,6 @@ import pathlib import copy +import time from flask import g from contextlib import suppress from fractions import Fraction @@ -311,75 +312,90 @@ class Device(Thing): return history @property - def trading(self): + def tradings(self): + return {str(x.id): self.trading(x.lot) for x in self.actions if x.t == 'Trade'} + + def trading(self, lot, simple=None): """The trading state, or None if no Trade action has - ever been performed to this device. This extract the posibilities for to do""" - - # trade = 'Trade' - confirm = 'Confirm' - need_confirm = 'NeedConfirmation' - double_confirm = 'TradeConfirmed' - revoke = 'Revoke' - revoke_pending = 'RevokePending' - confirm_revoke = 'ConfirmRevoke' - # revoke_confirmed = 'RevokeConfirmed' - - # return the correct status of trade depending of the user - - # #### CASES ##### - # User1 == owner of trade (This user have automatic Confirmation) - # ======================= - # if the last action is => only allow to do - # ========================================== - # Confirmation not User1 => Revoke - # Confirmation User1 => Revoke - # Revoke not User1 => ConfirmRevoke - # Revoke User1 => RevokePending - # RevokeConfirmation => RevokeConfirmed - # - # - # User2 == Not owner of trade - # ======================= - # if the last action is => only allow to do - # ========================================== - # Confirmation not User2 => Confirm - # Confirmation User2 => Revoke - # Revoke not User2 => ConfirmRevoke - # Revoke User2 => RevokePending - # RevokeConfirmation => RevokeConfirmed - - ac = self.last_action_trading - if not ac: + ever been performed to this device. This extract the posibilities for to do. + This method is performed for show in the web. + If you need to do one simple and generic response you can put simple=True for that.""" + if not hasattr(lot, 'trade'): return - first_owner = self.which_user_put_this_device_in_trace() + Status = {0: 'Trade', + 1: 'Confirm', + 2: 'NeedConfirmation', + 3: 'TradeConfirmed', + 4: 'Revoke', + 5: 'NeedConfirmRevoke', + 6: 'RevokeConfirmed'} - if ac.type == confirm_revoke: - # can to do revoke_confirmed - return confirm_revoke + trade = lot.trade + user_from = trade.user_from + user_to = trade.user_to + status = 0 + last_user = None - if ac.type == revoke: - if ac.user == g.user: - # can todo revoke_pending - return revoke_pending - else: - # can to do confirm_revoke - return revoke + if not hasattr(trade, 'acceptances'): + return Status[status] - if ac.type == confirm: - if not first_owner: - return + for ac in self.actions: + if ac.t not in ['Confirm', 'Revoke']: + continue - if ac.user == first_owner: - if first_owner == g.user: - # can to do revoke - return confirm - else: - # can to do confirm - return need_confirm - else: - # can to do revoke - return double_confirm + if ac.user not in [user_from, user_to]: + continue + + if ac.t == 'Confirm' and ac.action == trade: + if status in [0, 6]: + if simple: + status = 2 + continue + status = 1 + last_user = ac.user + if ac.user == user_from and user_to == g.user: + status = 2 + if ac.user == user_to and user_from == g.user: + status = 2 + continue + + if status in [1, 2]: + if last_user != ac.user: + status = 3 + last_user = ac.user + continue + + if status in [4, 5]: + status = 3 + last_user = ac.user + continue + + if ac.t == 'Revoke' and ac.action == trade: + if status == 3: + if simple: + status = 5 + continue + status = 4 + last_user = ac.user + if ac.user == user_from and user_to == g.user: + status = 5 + if ac.user == user_to and user_from == g.user: + status = 5 + continue + + if status in [4, 5]: + if last_user != ac.user: + status = 6 + last_user = ac.user + continue + + if status in [1, 2]: + status = 6 + last_user = ac.user + continue + + return Status[status] @property def revoke(self): @@ -485,15 +501,15 @@ class Device(Thing): def which_user_put_this_device_in_trace(self): """which is the user than put this device in this trade""" actions = copy.copy(self.actions) - actions.sort(key=lambda x: x.created) actions.reverse() - last_ac = None # search the automatic Confirm for ac in actions: if ac.type == 'Trade': - return last_ac.user - if ac.type == 'Confirm': - last_ac = ac + action_device = [x for x in ac.actions_device if x.device == self][0] + if action_device.author: + return action_device.author + + return ac.author def change_owner(self, new_user): """util for change the owner one device""" diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index d9a658fe..bc18b995 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, fields as f -from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String, UUID +from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String, UUID, Dict from marshmallow.validate import Length, OneOf, Range from sqlalchemy.util import OrderedSet from stdnum import imei, meid @@ -50,12 +50,11 @@ class Device(Thing): description='The lots where this device is directly under.') rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__) price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__) - # trading = EnumField(states.Trading, dump_only=True, description=m.Device.trading.__doc__) - trading = SanitizedStr(dump_only=True, description='') + tradings = Dict(dump_only=True, description='') physical = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__) - traking= EnumField(states.Traking, dump_only=True, description=m.Device.physical.__doc__) + traking = EnumField(states.Traking, dump_only=True, description=m.Device.physical.__doc__) usage = EnumField(states.Usage, dump_only=True, description=m.Device.physical.__doc__) - revoke = UUID(dump_only=True) + revoke = UUID(dump_only=True) physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor') production_date = DateTime('iso', description=m.Device.updated.comment, diff --git a/ereuse_devicehub/resources/device/states.py b/ereuse_devicehub/resources/device/states.py index 5177f701..3cfefe4b 100644 --- a/ereuse_devicehub/resources/device/states.py +++ b/ereuse_devicehub/resources/device/states.py @@ -37,7 +37,6 @@ class Trading(State): Trade = e.Trade Confirm = e.Confirm Revoke = e.Revoke - ConfirmRevoke = e.ConfirmRevoke Cancelled = e.CancelTrade Sold = e.Sell Donated = e.Donate diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index fcbca827..dce6af62 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -1,4 +1,5 @@ import uuid +from sqlalchemy.util import OrderedSet from collections import deque from enum import Enum from typing import Dict, List, Set, Union @@ -13,7 +14,7 @@ from teal.resource import View from ereuse_devicehub.db import db from ereuse_devicehub.query import things_response from ereuse_devicehub.resources.device.models import Device, Computer -from ereuse_devicehub.resources.action.models import Trade, Confirm, Revoke, ConfirmRevoke +from ereuse_devicehub.resources.action.models import Trade, Confirm, Revoke from ereuse_devicehub.resources.lot.models import Lot, Path @@ -230,7 +231,7 @@ class LotDeviceView(LotBaseChildrenView): return devices = set(Device.query.filter(Device.id.in_(ids)).filter( - Device.owner==g.user)) + Device.owner == g.user)) lot.devices.update(devices) @@ -246,7 +247,8 @@ class LotDeviceView(LotBaseChildrenView): return if lot.trade: - return delete_from_trade(lot, ids) + devices = Device.query.filter(Device.id.in_(ids)).all() + return delete_from_trade(lot, devices) if not g.user == lot.owner: txt = 'This is not your lot' @@ -258,49 +260,45 @@ class LotDeviceView(LotBaseChildrenView): lot.devices.difference_update(devices) -def delete_from_trade(lot: Lot, ids: Set[int]): - users = [lot.trade.user_from.id, lot.trade.user_to.id] - if not g.user.id in users: +def delete_from_trade(lot: Lot, devices: List): + users = [lot.trade.user_from, lot.trade.user_to] + if g.user not in users: # theoretically this case is impossible txt = 'This is not your trade' raise ma.ValidationError(txt) - devices = set(Device.query.filter(Device.id.in_(ids)).filter( - Device.owner_id.in_(users))) + # we need lock the action revoke for devices than travel for futures trades + for dev in devices: + if dev.owner not in users: + txt = 'This is not your device' + raise ma.ValidationError(txt) - # Now we need to know which devices we need extract of the lot - without_confirms = set() # set of devs without confirms of user2 + drop_of_lot = [] + without_confirms = [] + for dev in devices: + if dev.trading(lot) in ['NeedConfirmation', 'Confirm', 'NeedConfirmRevoke']: + drop_of_lot.append(dev) + dev.reset_owner() - # if the trade need confirmation, then extract all devs than - # have only one confirmation and is from the same user than try to do - # now the revoke action - if lot.trade.confirm: - for dev in devices: - # if have only one confirmation - # then can be revoked and deleted of the lot - # Confirm of dev.trading mean that there are only one confirmation - # and the first user than put this device in trade is the actual g.user - if dev.trading == 'Confirm': - without_confirms.add(dev) - dev.reset_owner() + if not lot.trade.confirm: + drop_of_lot.append(dev) + without_confirms.append(dev) + dev.reset_owner() - # we need to mark one revoke for every devs - revoke = Revoke(action=lot.trade, user=g.user, devices=devices) + revoke = Revoke(action=lot.trade, user=g.user, devices=set(devices)) db.session.add(revoke) - if not lot.trade.confirm: - # if the trade is with phantom account - without_confirms = devices - if without_confirms: - confirm_revoke = ConfirmRevoke( - action=revoke, - user=g.user, - devices=without_confirms + phantom = lot.trade.user_to + if lot.trade.user_to == g.user: + phantom = lot.trade.user_from + + phantom_revoke = Revoke( + action=lot.trade, + user=phantom, + devices=set(without_confirms) ) - db.session.add(confirm_revoke) - - lot.devices.difference_update(without_confirms) - lot.trade.devices = lot.devices + db.session.add(phantom_revoke) + lot.devices.difference_update(OrderedSet(drop_of_lot)) return revoke diff --git a/tests/test_action.py b/tests/test_action.py index 888541f3..0e339c6d 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -328,7 +328,7 @@ def test_outgoinlot_status_actions(action_model: models.Action, user: UserClient assert device['actions'][-1]['id'] == action['id'] assert action['author']['id'] == user.user['id'] - assert action['rol_user']['id'] == user.user['id'] + assert action['rol_user']['id'] == user2.user['id'] @pytest.mark.mvp @@ -386,14 +386,14 @@ def test_history_status_actions(user: UserClient, user2: UserClient): assert action['id'] == str(device.status.id) assert device.status.t == models.Recycling.t assert [action['id']] == [str(ac.id) for ac in device.history_status] - + # Case 2 action2 = {'type': models.Refurbish.t, 'devices': [device.id]} action2, _ = user.post(action2, res=models.Action) assert action2['id'] == str(device.status.id) assert device.status.t == models.Refurbish.t assert [action2['id']] == [str(ac.id) for ac in device.history_status] - + # Case 3 lot, _ = user.post({'name': 'MyLot'}, res=Lot) user.post({}, @@ -1396,6 +1396,7 @@ def test_confirm_revoke(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() + device = trade.devices[0] request_confirm = { 'type': 'Confirm', @@ -1416,9 +1417,10 @@ def test_confirm_revoke(user: UserClient, user2: UserClient): # Normal revoke user2.post(res=models.Action, data=request_revoke) - # You can not to do one confirmation next of one revoke - user2.post(res=models.Action, data=request_confirm, status=422) - assert len(trade.acceptances) == 3 + # You can to do one confirmation next of one revoke + user2.post(res=models.Action, data=request_confirm) + assert len(trade.acceptances) == 4 + assert device.trading(trade.lot) == "TradeConfirmed" @pytest.mark.mvp @@ -1455,7 +1457,7 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): item='{}/devices'.format(lot['id']), query=devices[:7]) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the SCRAP to confirm it request_post = { 'type': 'Trade', @@ -1516,9 +1518,6 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): 'type': 'Confirm', 'action': trade.id, 'devices': [ - snap1['device']['id'], - snap2['device']['id'], - snap3['device']['id'], snap4['device']['id'], snap5['device']['id'], snap6['device']['id'], @@ -1535,7 +1534,7 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): assert trade.devices[-1].actions[-1].user == trade.user_from assert len(trade.devices[0].actions) == n_actions - # The manager remove one device of the lot and automaticaly + # The manager remove one device of the lot and automaticaly # is create one revoke action device_10 = trade.devices[-1] lot, _ = user.delete({}, @@ -1554,31 +1553,28 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): # the SCRAP confirms the revoke action request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device_10.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [ snap10['device']['id'] ] } user2.post(res=models.Action, data=request_confirm_revoke) - assert device_10.actions[-1].t == 'ConfirmRevoke' + assert device_10.actions[-1].t == 'Revoke' assert device_10.actions[-2].t == 'Revoke' # assert len(trade.lot.devices) == len(trade.devices) == 9 # assert not device_10 in trade.devices # check validation error request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device_10.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [ snap9['device']['id'] ] } - user2.post(res=models.Action, data=request_confirm_revoke, status=422) - - # The manager add again device_10 # assert len(trade.devices) == 9 lot, _ = user.post({}, @@ -1604,7 +1600,7 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): assert device_10.actions[-1].user == trade.user_from assert device_10.actions[-2].t == 'Confirm' assert device_10.actions[-2].user == trade.user_to - assert device_10.actions[-3].t == 'ConfirmRevoke' + assert device_10.actions[-3].t == 'Revoke' # assert len(device_10.actions) == 13 @@ -1756,7 +1752,7 @@ def test_trade_case1(user: UserClient, user2: UserClient): item='{}/devices'.format(lot['id']), query=devices[:-1]) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -1772,31 +1768,23 @@ def test_trade_case1(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot = trade.lot + device = trade.devices[0] - device1, device2 = trade.devices + assert device.actions[-2].t == 'Trade' + assert device.actions[-1].t == 'Confirm' + assert device.actions[-1].user == trade.user_to - assert device1.actions[-2].t == 'Trade' - assert device1.actions[-1].t == 'Confirm' - assert device1.actions[-1].user == trade.user_to - assert device2.actions[-2].t == 'Trade' - assert device2.actions[-1].t == 'Confirm' - assert device2.actions[-1].user == trade.user_to + user.delete({}, + res=Lot, + item='{}/devices'.format(lot.id), + query=devices[:-1], status=200) - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices, status=200) - - assert device1.actions[-2].t == 'Revoke' - assert device1.actions[-1].t == 'ConfirmRevoke' - assert device1.actions[-1].user == trade.user_to - assert device2.actions[-2].t == 'Revoke' - assert device2.actions[-1].t == 'ConfirmRevoke' - assert device2.actions[-1].user == trade.user_to + assert device not in trade.lot.devices + assert device.trading(trade.lot) == 'RevokeConfirmed' + assert device.actions[-2].t == 'Confirm' + assert device.actions[-1].t == 'Revoke' + assert device.actions[-1].user == trade.user_to @pytest.mark.mvp @@ -1855,12 +1843,13 @@ def test_trade_case2(user: UserClient, user2: UserClient): # Normal revoke user.post(res=models.Action, data=request_revoke) - assert device1.actions[-2].t == 'Revoke' - assert device1.actions[-1].t == 'ConfirmRevoke' + assert device1.actions[-2].t == 'Confirm' + assert device1.actions[-1].t == 'Revoke' assert device1.actions[-1].user == trade.user_to - assert device2.actions[-2].t == 'Revoke' - assert device2.actions[-1].t == 'ConfirmRevoke' + assert device2.actions[-2].t == 'Confirm' + assert device2.actions[-1].t == 'Revoke' assert device2.actions[-1].user == trade.user_to + assert device1.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -1868,7 +1857,6 @@ def test_trade_case2(user: UserClient, user2: UserClient): def test_trade_case3(user: UserClient, user2: UserClient): # the pRp (manatest_usecase_confirmationger) creates a temporary lot lot, _ = user.post({'name': 'MyLot'}, res=Lot) - # The manager add 7 device into the lot snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) @@ -1880,7 +1868,7 @@ def test_trade_case3(user: UserClient, user2: UserClient): item='{}/devices'.format(lot['id']), query=devices[:-1]) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -1915,9 +1903,10 @@ def test_trade_case3(user: UserClient, user2: UserClient): item='{}/devices'.format(lot['id']), query=devices[-1:], status=200) - assert device2.actions[-2].t == 'Revoke' - assert device2.actions[-1].t == 'ConfirmRevoke' + assert device2.actions[-2].t == 'Confirm' + assert device2.actions[-1].t == 'Revoke' assert device2.actions[-1].user == trade.user_from + assert device2.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -1979,9 +1968,10 @@ def test_trade_case4(user: UserClient, user2: UserClient): assert device1.actions[-2].t == 'Trade' assert device1.actions[-1].t == 'Confirm' assert device1.actions[-1].user == trade.user_to - assert device2.actions[-2].t == 'Revoke' - assert device2.actions[-1].t == 'ConfirmRevoke' + assert device2.actions[-2].t == 'Confirm' + assert device2.actions[-1].t == 'Revoke' assert device2.actions[-1].user == trade.user_from + assert device2.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -2036,8 +2026,8 @@ def test_trade_case5(user: UserClient, user2: UserClient): assert device2.actions[-1].user == trade.user_from request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device2.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device2.id], } @@ -2045,8 +2035,9 @@ def test_trade_case5(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_confirm_revoke) assert device2.actions[-2].t == 'Revoke' - assert device2.actions[-1].t == 'ConfirmRevoke' + assert device2.actions[-1].t == 'Revoke' assert device2.actions[-1].user == trade.user_to + assert device2.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -2106,8 +2097,8 @@ def test_trade_case6(user: UserClient, user2: UserClient): assert device2.actions[-1].user == trade.user_to request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device2.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device2.id], } @@ -2115,8 +2106,9 @@ def test_trade_case6(user: UserClient, user2: UserClient): user2.post(res=models.Action, data=request_confirm_revoke) assert device2.actions[-2].t == 'Revoke' - assert device2.actions[-1].t == 'ConfirmRevoke' + assert device2.actions[-1].t == 'Revoke' assert device2.actions[-1].user == trade.user_from + assert device2.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -2158,6 +2150,7 @@ def test_trade_case7(user: UserClient, user2: UserClient): # Normal revoke user2.post(res=models.Action, data=request_confirm) + assert device.trading(trade.lot) == 'TradeConfirmed' lot, _ = user.delete({}, res=Lot, @@ -2165,14 +2158,14 @@ def test_trade_case7(user: UserClient, user2: UserClient): query=devices, status=200) request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device.id], } user2.post(res=models.Action, data=request_confirm_revoke) - assert device.actions[-1].t == 'ConfirmRevoke' + assert device.actions[-1].t == 'Revoke' assert device.actions[-1].user == trade.user_from assert device.actions[-2].t == 'Revoke' assert device.actions[-2].user == trade.user_to @@ -2182,6 +2175,7 @@ def test_trade_case7(user: UserClient, user2: UserClient): assert device.actions[-4].user == trade.user_to assert device.actions[-5].t == 'Trade' assert device.actions[-5].author == trade.user_to + assert device.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -2223,6 +2217,7 @@ def test_trade_case8(user: UserClient, user2: UserClient): # Normal revoke user2.post(res=models.Action, data=request_confirm) + assert device.trading(trade.lot) == 'TradeConfirmed' request_revoke = { 'type': 'Revoke', @@ -2234,14 +2229,14 @@ def test_trade_case8(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_revoke) request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device.id], } user2.post(res=models.Action, data=request_confirm_revoke) - assert device.actions[-1].t == 'ConfirmRevoke' + assert device.actions[-1].t == 'Revoke' assert device.actions[-1].user == trade.user_from assert device.actions[-2].t == 'Revoke' assert device.actions[-2].user == trade.user_to @@ -2251,6 +2246,7 @@ def test_trade_case8(user: UserClient, user2: UserClient): assert device.actions[-4].user == trade.user_to assert device.actions[-5].t == 'Trade' assert device.actions[-5].author == trade.user_to + assert device.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -2303,6 +2299,7 @@ def test_trade_case9(user: UserClient, user2: UserClient): # Normal revoke user.post(res=models.Action, data=request_confirm) + assert device.trading(trade.lot) == 'TradeConfirmed' assert device.owner == trade.user_to @@ -2312,8 +2309,8 @@ def test_trade_case9(user: UserClient, user2: UserClient): query=devices[-1:], status=200) request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device.id], } @@ -2321,7 +2318,7 @@ def test_trade_case9(user: UserClient, user2: UserClient): assert device.owner == trade.user_from - assert device.actions[-1].t == 'ConfirmRevoke' + assert device.actions[-1].t == 'Revoke' assert device.actions[-1].user == trade.user_to assert device.actions[-2].t == 'Revoke' assert device.actions[-2].user == trade.user_from @@ -2331,6 +2328,7 @@ def test_trade_case9(user: UserClient, user2: UserClient): assert device.actions[-4].user == trade.user_from assert device.actions[-5].t == 'Trade' assert device.actions[-5].author == trade.user_to + assert device.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -2374,6 +2372,7 @@ def test_trade_case10(user: UserClient, user2: UserClient): device1, device = trade.devices assert device.owner == trade.user_from + # assert device.trading(trade.lot) == 'Confirm' request_confirm = { 'type': 'Confirm', @@ -2383,6 +2382,7 @@ def test_trade_case10(user: UserClient, user2: UserClient): # Normal confirm user.post(res=models.Action, data=request_confirm) + # assert device.trading(trade.lot) == 'TradeConfirmed' assert device.owner == trade.user_to @@ -2394,18 +2394,18 @@ def test_trade_case10(user: UserClient, user2: UserClient): # Normal revoke user2.post(res=models.Action, data=request_revoke) + assert device.trading(trade.lot) == 'Revoke' request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device.id], } user.post(res=models.Action, data=request_confirm_revoke) assert device.owner == trade.user_from - - assert device.actions[-1].t == 'ConfirmRevoke' + assert device.actions[-1].t == 'Revoke' assert device.actions[-1].user == trade.user_to assert device.actions[-2].t == 'Revoke' assert device.actions[-2].user == trade.user_from @@ -2415,6 +2415,7 @@ def test_trade_case10(user: UserClient, user2: UserClient): assert device.actions[-4].user == trade.user_from assert device.actions[-5].t == 'Trade' assert device.actions[-5].author == trade.user_to + assert device.trading(trade.lot) == 'RevokeConfirmed' @pytest.mark.mvp @@ -2451,6 +2452,7 @@ def test_trade_case11(user: UserClient, user2: UserClient): trade = models.Trade.query.one() device1, device = trade.devices + assert device.trading(trade.lot) == 'Confirm' request_confirm = { 'type': 'Confirm', @@ -2459,21 +2461,24 @@ def test_trade_case11(user: UserClient, user2: UserClient): } user2.post(res=models.Action, data=request_confirm) + assert device.trading(trade.lot) == 'TradeConfirmed' lot, _ = user2.delete({}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200) + assert device.trading(trade.lot) == 'Revoke' request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device.id], } user.post(res=models.Action, data=request_confirm_revoke) + assert device.trading(trade.lot) == 'RevokeConfirmed' - assert device.actions[-1].t == 'ConfirmRevoke' + assert device.actions[-1].t == 'Revoke' assert device.actions[-1].user == trade.user_to assert device.actions[-2].t == 'Revoke' assert device.actions[-2].user == trade.user_from @@ -2519,6 +2524,7 @@ def test_trade_case12(user: UserClient, user2: UserClient): trade = models.Trade.query.one() device1, device = trade.devices + assert device.trading(trade.lot) == 'Confirm' # Normal confirm request_confirm = { @@ -2528,6 +2534,7 @@ def test_trade_case12(user: UserClient, user2: UserClient): } user2.post(res=models.Action, data=request_confirm) + assert device.trading(trade.lot) == 'TradeConfirmed' request_revoke = { 'type': 'Revoke', @@ -2537,16 +2544,18 @@ def test_trade_case12(user: UserClient, user2: UserClient): # Normal revoke user2.post(res=models.Action, data=request_revoke) + assert device.trading(trade.lot) == 'Revoke' request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device.id], } user.post(res=models.Action, data=request_confirm_revoke) + assert device.trading(trade.lot) == 'RevokeConfirmed' - assert device.actions[-1].t == 'ConfirmRevoke' + assert device.actions[-1].t == 'Revoke' assert device.actions[-1].user == trade.user_to assert device.actions[-2].t == 'Revoke' assert device.actions[-2].user == trade.user_from @@ -2597,6 +2606,8 @@ def test_trade_case13(user: UserClient, user2: UserClient): query=devices[-1:]) device1, device = trade.devices + assert device1.trading(trade.lot) == 'NeedConfirmation' + assert device.trading(trade.lot) == 'Confirm' request_confirm = { 'type': 'Confirm', @@ -2605,21 +2616,26 @@ def test_trade_case13(user: UserClient, user2: UserClient): } user.post(res=models.Action, data=request_confirm) + assert device1.trading(trade.lot) == 'Confirm' + assert device.trading(trade.lot) == 'TradeConfirmed' lot, _ = user.delete({}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200) + assert device1.trading(trade.lot) == 'Confirm' + assert device.trading(trade.lot) == 'Revoke' request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device.id], } user2.post(res=models.Action, data=request_confirm_revoke) + assert device.trading(trade.lot) == 'RevokeConfirmed' - assert device.actions[-1].t == 'ConfirmRevoke' + assert device.actions[-1].t == 'Revoke' assert device.actions[-1].user == trade.user_from assert device.actions[-2].t == 'Revoke' assert device.actions[-2].user == trade.user_to @@ -2670,6 +2686,8 @@ def test_trade_case14(user: UserClient, user2: UserClient): query=devices[-1:]) device1, device = trade.devices + assert device1.trading(trade.lot) == 'NeedConfirmation' + assert device.trading(trade.lot) == 'Confirm' # Normal confirm request_confirm = { @@ -2679,6 +2697,7 @@ def test_trade_case14(user: UserClient, user2: UserClient): } user.post(res=models.Action, data=request_confirm) + assert device.trading(trade.lot) == 'TradeConfirmed' request_revoke = { 'type': 'Revoke', @@ -2688,16 +2707,18 @@ def test_trade_case14(user: UserClient, user2: UserClient): # Normal revoke user.post(res=models.Action, data=request_revoke) + assert device.trading(trade.lot) == 'Revoke' request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device.actions[-1].id, + 'type': 'Revoke', + 'action': trade.id, 'devices': [device.id], } user2.post(res=models.Action, data=request_confirm_revoke) + assert device.trading(trade.lot) == 'RevokeConfirmed' - assert device.actions[-1].t == 'ConfirmRevoke' + assert device.actions[-1].t == 'Revoke' assert device.actions[-1].user == trade.user_from assert device.actions[-2].t == 'Revoke' assert device.actions[-2].user == trade.user_to @@ -2718,7 +2739,7 @@ def test_action_web_erase(user: UserClient, client: Client): hash3 = hashlib.sha3_256(bfile.read()).hexdigest() snap, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) request = {'type': 'DataWipe', 'devices': [snap['device']['id']], 'name': 'borrado universal', 'severity': 'Info', 'description': 'nada que describir', 'url': 'http://www.google.com/', 'documentId': '33', 'endTime': '2021-07-07T22:00:00.000Z', 'filename': 'Certificado de borrado1.pdf', 'hash': hash3, 'success': 1, 'software': "Blanco"} - + user.post(res=models.Action, data=request) action = models.DataWipe.query.one() for dev in action.devices: diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 6ad7a516..cab985d1 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -181,14 +181,13 @@ def test_complet_metrics_with_trade(user: UserClient, user2: UserClient): query=[('filter', {'type': ['Computer']})]) body1_lenovo = 'O48N2;desktop-lenovo-9644w8n-0169622-00:1a:6b:5e:7f:10;;Trade;foo@foo.com;' - body1_lenovo += 'foo2@foo.com;Supplier;False;Use;;' + body1_lenovo += 'foo2@foo.com;Supplier;NeedConfirmation;Use;;' body2_lenovo = ';;0;0;Trade;0;0\n' body1_acer = 'J2MA2;laptop-acer-aohappy-lusea0d010038879a01601-00:26:c7:8e:cb:8c;;Trade;' - body1_acer += 'foo@foo.com;foo2@foo.com;Supplier;False;;;;;0;' + body1_acer += 'foo@foo.com;foo2@foo.com;Supplier;NeedConfirmation;;;;;0;' body2_acer = ';;0;0;Trade;0;4692.0\n' - # import pdb; pdb.set_trace() assert body1_lenovo in csv_str assert body2_lenovo in csv_str assert body1_acer in csv_str @@ -203,7 +202,7 @@ def test_complet_metrics_with_trade(user: UserClient, user2: UserClient): query=[('filter', {'type': ['Computer']})]) body1_lenovo = 'O48N2;desktop-lenovo-9644w8n-0169622-00:1a:6b:5e:7f:10;;Trade;foo@foo.com;' - body1_lenovo += 'foo2@foo.com;Supplier;False;Use;Use;' + body1_lenovo += 'foo2@foo.com;Supplier;NeedConfirmation;Use;Use;' body2_lenovo = ';;0;0;Trade;0;0\n' body2_acer = ';;0;0;Trade;0;4692.0\n' @@ -353,8 +352,8 @@ def test_bug_trade_confirmed(user: UserClient, user2: UserClient): accept='text/csv', query=[('filter', {'type': ['Computer']})]) - body_not_confirmed = "Trade;foo2@foo.com;foo@foo.com;Receiver;False;" - body_confirmed = "Trade;foo2@foo.com;foo@foo.com;Receiver;True;" + body_not_confirmed = "Trade;foo2@foo.com;foo@foo.com;Receiver;NeedConfirmation;" + body_confirmed = "Trade;foo2@foo.com;foo@foo.com;Receiver;TradeConfirmed;" assert body_not_confirmed in csv_not_confirmed assert body_confirmed in csv_confirmed diff --git a/tests/test_workbench.py b/tests/test_workbench.py index 9998decc..2eb161a1 100644 --- a/tests/test_workbench.py +++ b/tests/test_workbench.py @@ -66,7 +66,7 @@ def test_workbench_server_condensed(user: UserClient): assert device['rate']['rating'] == 1 assert device['rate']['type'] == RateComputer.t # TODO JN why haven't same order in actions on each execution? - assert device['actions'][2]['type'] == BenchmarkProcessor.t or device['actions'][2]['type'] == BenchmarkRamSysbench.t + assert any([ac['type'] in [BenchmarkProcessor.t, BenchmarkRamSysbench.t] for ac in device['actions']]) assert 'tag1' in [x['id'] for x in device['tags']]