diff --git a/CHANGELOG.md b/CHANGELOG.md index 42c3a696..3213330a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ml). ## master - [1.0.4-beta] + [1.0.5-beta] ## testing - [1.0.5-beta] + [1.0.6-beta] + +## [1.0.6-beta] ## [1.0.5-beta] - [addend] #124 adding endpoint for extract the internal stats of use diff --git a/ereuse_devicehub/__init__.py b/ereuse_devicehub/__init__.py index 53f0b176..eeacf9dd 100644 --- a/ereuse_devicehub/__init__.py +++ b/ereuse_devicehub/__init__.py @@ -1 +1 @@ -__version__ = "1.0.5-beta" +__version__ = "1.0.6-beta" diff --git a/ereuse_devicehub/migrations/versions/51439cf24be8_change_trade_action.py b/ereuse_devicehub/migrations/versions/51439cf24be8_change_trade_action.py index 41f72d22..b5939e2d 100644 --- a/ereuse_devicehub/migrations/versions/51439cf24be8_change_trade_action.py +++ b/ereuse_devicehub/migrations/versions/51439cf24be8_change_trade_action.py @@ -80,16 +80,6 @@ def upgrade(): schema=f'{get_inv()}' ) - op.create_table('trade_note', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('trade_id', postgresql.UUID(as_uuid=True), nullable=False), - - sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), - sa.ForeignKeyConstraint(['trade_id'], [f'{get_inv()}.trade.id'], ), - sa.PrimaryKeyConstraint('id'), - schema=f'{get_inv()}' - ) - # ## User op.add_column('user', sa.Column('active', sa.Boolean(), default=True, nullable=True), schema='common') @@ -104,7 +94,6 @@ def upgrade(): def downgrade(): op.drop_table('confirm', schema=f'{get_inv()}') - op.drop_table('trade_note', schema=f'{get_inv()}') op.drop_table('trade', schema=f'{get_inv()}') op.create_table('trade', sa.Column('shipping_date', sa.TIMESTAMP(timezone=True), nullable=True, diff --git a/ereuse_devicehub/resources/action/__init__.py b/ereuse_devicehub/resources/action/__init__.py index 83336a74..fdfa15ea 100644 --- a/ereuse_devicehub/resources/action/__init__.py +++ b/ereuse_devicehub/resources/action/__init__.py @@ -264,11 +264,6 @@ class CancelTradeDef(ActionDef): SCHEMA = schemas.CancelTrade -class TradeNoteDef(ActionDef): - VIEW = None - SCHEMA = schemas.TradeNote - - class ToDisposeProductDef(ActionDef): VIEW = None SCHEMA = schemas.ToDisposeProduct diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 8ee742b9..7160abfc 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1433,18 +1433,6 @@ class CancelReservation(Organize): """The act of cancelling a reservation.""" -class TradeNote(JoinedTableMixin, ActionWithMultipleDevices): - """Note add to one trade""" - trade_id = db.Column(UUID(as_uuid=True), - db.ForeignKey('trade.id'), - nullable=False) - trade = db.relationship('Trade', - backref=backref('notes', - uselist=True, - lazy=True), - primaryjoin='TradeNote.trade_id == Trade.id') - - class Confirm(JoinedTableMixin, ActionWithMultipleDevices): """Users confirm the offer and change it to trade""" revoke = Column(Boolean, default=False, nullable=False) diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 9d045794..f0099151 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -457,11 +457,6 @@ class CancelReservation(Organize): __doc__ = m.CancelReservation.__doc__ -class TradeNote(ActionWithMultipleDevices): - __doc__ = m.TradeNote.__doc__ - trade = NestedOn('Trade', only_query='id') - - class Confirm(ActionWithMultipleDevices): __doc__ = m.Confirm.__doc__ revoke = Boolean(required=False, description="""If you want revoke an other confirmation""") diff --git a/ereuse_devicehub/resources/action/views.py b/ereuse_devicehub/resources/action/views.py index 18a26e20..c18c6a08 100644 --- a/ereuse_devicehub/resources/action/views.py +++ b/ereuse_devicehub/resources/action/views.py @@ -8,6 +8,7 @@ from distutils.version import StrictVersion from uuid import UUID from flask import current_app as app, request, g +from flask.wrappers import Response from sqlalchemy.util import OrderedSet from teal.marshmallow import ValidationError from teal.resource import View @@ -225,24 +226,23 @@ class ActionView(View): # defs resource_def = app.resources[json['type']] if json['type'] == Snapshot.t: - tmp_snapshots = app.config['TMP_SNAPSHOTS'] - path_snapshot = save_json(json, tmp_snapshots, g.user.email) - json.pop('debug', None) - a = resource_def.schema.load(json) - response = self.snapshot(a, resource_def) - move_json(tmp_snapshots, path_snapshot, g.user.email) - return response + snapshot = SnapshotView(json, resource_def, self.schema) + return snapshot.post() + 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() - # import pdb; pdb.set_trace() + + if json['type'] == Trade.t: + offer = OfferView(json, resource_def, self.schema) + return offer.post() + a = resource_def.schema.load(json) Model = db.Model._decl_class_registry.data[json['type']]() action = Model(**a) - if json['type'] == Trade.t: - return self.offer(action) db.session.add(action) db.session().final_flush() ret = self.schema.jsonify(action) @@ -255,22 +255,42 @@ class ActionView(View): action = Action.query.filter_by(id=id).one() return self.schema.jsonify(action) - def snapshot(self, snapshot_json: dict, resource_def): - """Performs a Snapshot. + def transfer_ownership(self): + """Perform a InitTransfer action to change author_id of device""" + pass + - See `Snapshot` section in docs for more info. - """ - # Note that if we set the device / components into the snapshot - # model object, when we flush them to the db we will flush - # snapshot, and we want to wait to flush snapshot at the end +class SnapshotView(): + """Performs a Snapshot. - device = snapshot_json.pop('device') # type: Computer + See `Snapshot` section in docs for more info. + """ + # Note that if we set the device / components into the snapshot + # model object, when we flush them to the db we will flush + # snapshot, and we want to wait to flush snapshot at the end + + def __init__(self, snapshot_json: dict, resource_def, schema): + self.schema = schema + self.snapshot_json = snapshot_json + self.resource_def = resource_def + self.tmp_snapshots = app.config['TMP_SNAPSHOTS'] + self.path_snapshot = save_json(snapshot_json, self.tmp_snapshots, g.user.email) + snapshot_json.pop('debug', None) + self.snapshot_json = resource_def.schema.load(snapshot_json) + self.response = self.build() + move_json(self.tmp_snapshots, self.path_snapshot, g.user.email) + + def post(self): + return self.response + + def build(self): + device = self.snapshot_json.pop('device') # type: Computer components = None - if snapshot_json['software'] == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid): - components = snapshot_json.pop('components', None) # type: List[Component] + if self.snapshot_json['software'] == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid): + components = self.snapshot_json.pop('components', None) # type: List[Component] if isinstance(device, Computer) and device.hid: device.add_mac_to_hid(components_snap=components) - snapshot = Snapshot(**snapshot_json) + snapshot = Snapshot(**self.snapshot_json) # Remove new actions from devices so they don't interfere with sync actions_device = set(e for e in device.actions_one) @@ -282,7 +302,7 @@ class ActionView(View): assert not device.actions_one assert all(not c.actions_one for c in components) if components else True - db_device, remove_actions = resource_def.sync.run(device, components) + db_device, remove_actions = self.resource_def.sync.run(device, components) del device # Do not use device anymore snapshot.device = db_device @@ -324,33 +344,37 @@ class ActionView(View): db.session.commit() return ret - def transfer_ownership(self): - """Perform a InitTransfer action to change author_id of device""" - pass - def offer(self, offer: Trade): - self.create_first_confirmation(offer) - self.create_phantom_account(offer) - db.session.add(offer) - self.create_automatic_trade(offer) +class OfferView(): + """Handler for manager the offer/trade action register from post""" + def __init__(self, data, resource_def, schema): + self.schema = schema + a = resource_def.schema.load(data) + self.offer = Trade(**a) + self.create_first_confirmation() + self.create_phantom_account() + db.session.add(self.offer) + self.create_automatic_trade() + + def post(self): db.session().final_flush() - ret = self.schema.jsonify(offer) + ret = self.schema.jsonify(self.offer) ret.status_code = 201 db.session.commit() return ret - def create_first_confirmation(self, offer: Trade) -> None: + def create_first_confirmation(self) -> None: """Do the first confirmation for the user than do the action""" # check than the user than want to do the action is one of the users # involved in the action - assert g.user.id in [offer.user_from_id, offer.user_to_id] + assert g.user.id in [self.offer.user_from_id, self.offer.user_to_id] - confirm = Confirm(user=g.user, action=offer, devices=offer.devices) + confirm = Confirm(user=g.user, action=self.offer, devices=self.offer.devices) db.session.add(confirm) - def create_phantom_account(self, offer) -> None: + def create_phantom_account(self) -> None: """ If exist both users not to do nothing If exist from but not to: @@ -360,50 +384,49 @@ class ActionView(View): The same if exist to but not from """ - if offer.user_from_id and offer.user_to_id: + if self.offer.user_from_id and self.offer.user_to_id: return - if offer.user_from_id and not offer.user_to_id: - assert g.user.id == offer.user_from_id - email = "{}_{}@dhub.com".format(str(offer.user_from_id), offer.code) + if self.offer.user_from_id and not self.offer.user_to_id: + assert g.user.id == self.offer.user_from_id + email = "{}_{}@dhub.com".format(str(self.offer.user_from_id), self.offer.code) users = User.query.filter_by(email=email) if users.first(): user = users.first() - offer.user_to = user + self.offer.user_to = user return user = User(email=email, password='', active=False, phantom=True) db.session.add(user) - offer.user_to = user + self.offer.user_to = user - if not offer.user_from_id and offer.user_to_id: - email = "{}_{}@dhub.com".format(str(offer.user_to_id), offer.code) + if not self.offer.user_from_id and self.offer.user_to_id: + email = "{}_{}@dhub.com".format(str(self.offer.user_to_id), self.offer.code) users = User.query.filter_by(email=email) if users.first(): user = users.first() - offer.user_from = user + self.offer.user_from = user return user = User(email=email, password='', active=False, phantom=True) db.session.add(user) - offer.user_from = user + self.offer.user_from = user - def create_automatic_trade(self, offer: Trade) -> None: + def create_automatic_trade(self) -> None: # not do nothing if it's neccesary confirmation explicity - if offer.confirm: + if self.offer.confirm: return # Change the owner for every devices - for dev in offer.devices: - dev.owner = offer.user_to + for dev in self.offer.devices: + dev.owner = self.offer.user_to if hasattr(dev, 'components'): for c in dev.components: - c.owner = offer.user_to + c.owner = self.offer.user_to # Create a new Confirmation for the user who does not perform the action - user = offer.user_from - if g.user == offer.user_from: - user = offer.user_to - - confirm = Confirm(user=user, action=offer, devices=offer.devices) + user = self.offer.user_from + if g.user == self.offer.user_from: + user = self.offer.user_to + confirm = Confirm(user=user, action=self.offer, devices=self.offer.devices) db.session.add(confirm) diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py index ade50e07..7f057fd3 100644 --- a/ereuse_devicehub/resources/lot/views.py +++ b/ereuse_devicehub/resources/lot/views.py @@ -15,7 +15,6 @@ from ereuse_devicehub.query import things_response from ereuse_devicehub.resources.deliverynote.models import Deliverynote from ereuse_devicehub.resources.device.models import Device, Computer from ereuse_devicehub.resources.lot.models import Lot, Path -from ereuse_devicehub.resources.action.models import TradeNote class LotFormat(Enum): @@ -225,32 +224,11 @@ class LotDeviceView(LotBaseChildrenView): id = ma.fields.List(ma.fields.Integer()) def _post(self, lot: Lot, ids: Set[int]): - if lot.trade: - lot_device_ids = [d.id for d in lot.devices] - new_device_ids = {d for d in ids if not d in lot_device_ids} - devices = set(Device.query.filter(Device.id.in_(new_device_ids)).all()) - txt = 'Adding new device in lot of trade {}'.format(lot.trade.id) - note = TradeNote(description=txt, - devices=devices, - trade=lot.trade) - db.session.add(note) - lot.devices.update(Device.query.filter(Device.id.in_(ids))) - if lot.trade: lot.trade.devices = lot.devices - def _delete(self, lot: Lot, ids: Set[int]): - if lot.trade: - devices = set(Device.query.filter(Device.id.in_(ids)).all()) - txt = 'Removing device from lot of trade {}'.format(lot.trade.id) - note = TradeNote(description=txt, - devices=devices, - trade=lot.trade) - db.session.add(note) - lot.devices.difference_update(Device.query.filter(Device.id.in_(ids))) - if lot.trade: lot.trade.devices = lot.devices diff --git a/tests/test_action.py b/tests/test_action.py index 8f6a128d..4ac7fc0b 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -987,50 +987,6 @@ def test_offer_without_devices(user: UserClient): # no there are transfer of devices -@pytest.mark.mvp -@pytest.mark.usefixtures(conftest.app_context.__name__) -def test_automatic_tradenote(user: UserClient, user2: UserClient): - """Check than there are one note when one device is insert in one trade lot""" - lot, _ = user.post({'name': 'MyLot'}, res=Lot) - request_post = { - 'type': 'Trade', - 'devices': [], - 'userFrom': user.email, - 'userTo': user2.email, - 'price': 10, - 'date': "2020-12-01T02:00:00+00:00", - 'documentID': '1', - 'lot': lot['id'], - 'confirm': True, - } - - user.post(res=models.Action, data=request_post) - trade = models.Trade.query.one() - assert trade.notes == [] - - snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) - device = Device.query.filter_by(id=snapshot['device']['id']).one() - # add one device - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device.id)]) - assert len(trade.notes) == 1 - assert trade.notes[0].devices[0] == device - - # remove one device - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device.id)], - status=200) - - assert len(trade.notes) == 2 - assert trade.notes[0].devices[0] == device - assert trade.notes[0].devices[0] == trade.notes[1].devices[0] - assert trade.devices == OrderedSet() - - @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_price_custom(): @@ -1082,63 +1038,6 @@ def test_erase_physical(): db.session.commit() -@pytest.mark.mvp -@pytest.mark.usefixtures(conftest.app_context.__name__) -def test_endpoint_tradenote(user: UserClient, user2: UserClient): - """Check the normal creation and visualization of one trade note""" - lot, _ = user.post({'name': 'MyLot'}, res=Lot) - request_post = { - 'type': 'Trade', - 'devices': [], - 'userFrom': user.email, - 'userTo': user2.email, - 'price': 10, - 'date': "2020-12-01T02:00:00+00:00", - 'documentID': '1', - 'lot': lot['id'], - 'confirm': True, - } - - user.post(res=models.Action, data=request_post) - trade = models.Trade.query.one() - - snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) - device = Device.query.filter_by(id=snapshot['device']['id']).one() - # add one device - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device.id)]) - - txt = 'Text of Note' - request_post = { - 'type': 'TradeNote', - 'description': txt, - 'devices': [device.id], - 'trade': trade.id, - } - - tradeNote, _ = user.post(res=models.Action, data=request_post) - - assert tradeNote['devices'][0]['id'] == device.id - assert tradeNote['description'] == txt - assert tradeNote['author']['email'] == user.email - - txt2 = 'Text of Note2' - request_post2 = { - 'type': 'TradeNote', - 'description': txt2, - 'devices': [device.id], - 'trade': trade.id, - } - - tradeNote2, _ = user.post(res=models.Action, data=request_post2) - assert tradeNote2['description'] == txt2 - - tradeNote3, _ = user.get(res=models.Action, item=tradeNote2['id']) - assert tradeNote3['id'] == tradeNote2['id'] - - @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_endpoint_confirm(user: UserClient, user2: UserClient):