Merge branch 'testing' into feature/delete-devices
This commit is contained in:
commit
3b41ddb66a
36
CHANGELOG.md
36
CHANGELOG.md
|
@ -14,36 +14,38 @@ ml).
|
||||||
## [1.0.10-beta]
|
## [1.0.10-beta]
|
||||||
- [addend] #170 can delete/deactivate devices.
|
- [addend] #170 can delete/deactivate devices.
|
||||||
- [bugfix] #168 can to do a trade without devices.
|
- [bugfix] #168 can to do a trade without devices.
|
||||||
- [addend] #167 new actions of status devices: use, recycling, refurbish and management.
|
- [added] #167 new actions of status devices: use, recycling, refurbish and management.
|
||||||
|
- [changes] #177 new structure of trade.
|
||||||
|
- [bugfix] #184 clean nested of schemas of lot
|
||||||
|
|
||||||
## [1.0.9-beta]
|
## [1.0.9-beta]
|
||||||
- [addend] #159 external document as proof of erase of disk
|
- [added] #159 external document as proof of erase of disk
|
||||||
- [addend] #162 adding lot for devices unassigned
|
- [added] #162 adding lot for devices unassigned
|
||||||
|
|
||||||
|
|
||||||
## [1.0.8-beta]
|
## [1.0.8-beta]
|
||||||
- [bugfix] #161 fixing DataStorage with bigInteger
|
- [bugfix] #161 fixing DataStorage with bigInteger
|
||||||
|
|
||||||
## [1.0.7-beta]
|
## [1.0.7-beta]
|
||||||
- [addend] #158 support for encrypted snapshots data
|
- [added] #158 support for encrypted snapshots data
|
||||||
- [addend] #135 adding trade system
|
- [added] #135 adding trade system
|
||||||
- [addend] #140 adding endpoint for download the settings for usb workbench
|
- [added] #140 adding endpoint for download the settings for usb workbench
|
||||||
|
|
||||||
## [1.0.6-beta]
|
## [1.0.6-beta]
|
||||||
- [bugfix] #143 biginteger instead of integer in TestDataStorage
|
- [bugfix] #143 biginteger instead of integer in TestDataStorage
|
||||||
|
|
||||||
## [1.0.5-beta]
|
## [1.0.5-beta]
|
||||||
- [addend] #124 adding endpoint for extract the internal stats of use
|
- [added] #124 adding endpoint for extract the internal stats of use
|
||||||
- [addend] #122 system for verify all documents that it's produced from devicehub
|
- [added] #122 system for verify all documents that it's produced from devicehub
|
||||||
- [addend] #127 add one code for every named tag
|
- [added] #127 add one code for every named tag
|
||||||
- [addend] #131 add one code for every device
|
- [added] #131 add one code for every device
|
||||||
- [bugfix] #138 search device with devicehubId
|
- [bugfix] #138 search device with devicehubId
|
||||||
|
|
||||||
## [1.0.4-beta]
|
## [1.0.4-beta]
|
||||||
- [addend] #95 adding endpoint for check the hash of one report
|
- [added] #95 adding endpoint for check the hash of one report
|
||||||
- [addend] #98 adding endpoint for insert a new live
|
- [added] #98 adding endpoint for insert a new live
|
||||||
- [addend] #98 adding endpoint for get all licences in one query
|
- [added] #98 adding endpoint for get all licences in one query
|
||||||
- [addend] #102 adding endpoint for download metrics
|
- [added] #102 adding endpoint for download metrics
|
||||||
- [bugfix] #100 fixing bug of scheme live
|
- [bugfix] #100 fixing bug of scheme live
|
||||||
- [bugfix] #101 fixing bug when 2 users have one device and launch one live
|
- [bugfix] #101 fixing bug when 2 users have one device and launch one live
|
||||||
- [changes] #114 clean blockchain of all models
|
- [changes] #114 clean blockchain of all models
|
||||||
|
@ -52,11 +54,11 @@ ml).
|
||||||
- [remove] #114 remove proof system
|
- [remove] #114 remove proof system
|
||||||
|
|
||||||
## [1.0.3-beta]
|
## [1.0.3-beta]
|
||||||
- [addend] #85 add mac of network adapter to device hid
|
- [added] #85 add mac of network adapter to device hid
|
||||||
- [changed] #94 change form of snapshot manual
|
- [changed] #94 change form of snapshot manual
|
||||||
|
|
||||||
## [1.0.2-beta]
|
## [1.0.2-beta]
|
||||||
- [addend] #87 allocate, deallocate and live actions
|
- [added] #87 allocate, deallocate and live actions
|
||||||
- [fixed] #89 save json on disk only for shapshots
|
- [fixed] #89 save json on disk only for shapshots
|
||||||
- [addend] #83 add owner_id in all kind of device
|
- [added] #83 add owner_id in all kind of device
|
||||||
- [fixed] #91 The most old time allow is 1970-01-01
|
- [fixed] #91 The most old time allow is 1970-01-01
|
||||||
|
|
|
@ -139,7 +139,7 @@ class Dummy:
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot_user['id']),
|
item='{}/devices'.format(lot_user['id']),
|
||||||
query=[('id', pc) for pc in itertools.islice(pcs, 1, 4)])
|
query=[('id', pc) for pc in itertools.islice(pcs, 1, 4)])
|
||||||
assert len(lot['devices'])
|
# assert len(lot['devices'])
|
||||||
|
|
||||||
lot2, _ = user2.post({},
|
lot2, _ = user2.post({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
"""
|
||||||
|
change action_device
|
||||||
|
|
||||||
|
Revision ID: 1bb2b5e0fae7
|
||||||
|
Revises: a0978ac6cf4a
|
||||||
|
Create Date: 2021-11-04 10:32:49.116399
|
||||||
|
|
||||||
|
"""
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from alembic import context
|
||||||
|
from alembic import op
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '1bb2b5e0fae7'
|
||||||
|
down_revision = 'a0978ac6cf4a'
|
||||||
|
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_data():
|
||||||
|
con = op.get_bind()
|
||||||
|
|
||||||
|
values = f"action_id, {get_inv()}.action.created"
|
||||||
|
table = f"{get_inv()}.action_device"
|
||||||
|
joins = f"inner join {get_inv()}.action"
|
||||||
|
on = f"on {get_inv()}.action_device.action_id = {get_inv()}.action.id"
|
||||||
|
sql = f"select {values} from {table} {joins} {on}"
|
||||||
|
|
||||||
|
actions_devs = con.execute(sql)
|
||||||
|
for a in actions_devs:
|
||||||
|
action_id = a.action_id
|
||||||
|
created = a.created
|
||||||
|
sql = f"update {get_inv()}.action_device set created='{created}' where action_id='{action_id}';"
|
||||||
|
con.execute(sql)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('action_device',
|
||||||
|
sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||||
|
nullable=False, comment='When Devicehub created this.'),
|
||||||
|
schema=f'{get_inv()}')
|
||||||
|
|
||||||
|
op.add_column('action_status',
|
||||||
|
sa.Column('trade_id', postgresql.UUID(as_uuid=True), nullable=True),
|
||||||
|
schema=f'{get_inv()}')
|
||||||
|
|
||||||
|
op.create_foreign_key("fk_action_status_trade",
|
||||||
|
"action_status", "trade",
|
||||||
|
["trade_id"], ["id"],
|
||||||
|
ondelete="SET NULL",
|
||||||
|
source_schema=f'{get_inv()}',
|
||||||
|
referent_schema=f'{get_inv()}')
|
||||||
|
|
||||||
|
upgrade_data()
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_constraint("fk_action_status_trade", "action_status", type_="foreignkey", schema=f'{get_inv()}')
|
||||||
|
op.drop_column('action_device', 'created', schema=f'{get_inv()}')
|
||||||
|
op.drop_column('action_status', 'trade_id', schema=f'{get_inv()}')
|
|
@ -0,0 +1,51 @@
|
||||||
|
"""upgrade confirmrevoke
|
||||||
|
|
||||||
|
Revision ID: 968b79fa7756
|
||||||
|
Revises: d22d230d2850
|
||||||
|
Create Date: 2021-11-12 19:18:39.135386
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
from alembic import context
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import sqlalchemy_utils
|
||||||
|
import citext
|
||||||
|
import teal
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '968b79fa7756'
|
||||||
|
down_revision = 'd22d230d2850'
|
||||||
|
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():
|
||||||
|
con = op.get_bind()
|
||||||
|
|
||||||
|
|
||||||
|
confirmsRevokes_sql = f"select * from {get_inv()}.action as action join {get_inv()}.confirm as confirm on action.id=confirm.id where action.type='ConfirmRevoke'"
|
||||||
|
revokes_sql = f"select confirm.id, confirm.action_id from {get_inv()}.action as action join {get_inv()}.confirm as confirm on action.id=confirm.id where action.type='Revoke'"
|
||||||
|
confirmsRevokes = [a for a in con.execute(confirmsRevokes_sql)]
|
||||||
|
revokes = {ac.id: ac.action_id for ac in con.execute(revokes_sql)}
|
||||||
|
|
||||||
|
for ac in confirmsRevokes:
|
||||||
|
ac_id = ac.id
|
||||||
|
revoke_id = ac.action_id
|
||||||
|
trade_id = revokes[revoke_id]
|
||||||
|
sql_action = f"update {get_inv()}.action set type='Revoke' where id='{ac_id}'"
|
||||||
|
sql_confirm = f"update {get_inv()}.confirm set action_id='{trade_id}' where id='{ac_id}'"
|
||||||
|
con.execute(sql_action)
|
||||||
|
con.execute(sql_confirm)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
pass
|
|
@ -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()}')
|
|
@ -285,16 +285,16 @@ class ConfirmDef(ActionDef):
|
||||||
SCHEMA = schemas.Confirm
|
SCHEMA = schemas.Confirm
|
||||||
|
|
||||||
|
|
||||||
class ConfirmRevokeDef(ActionDef):
|
|
||||||
VIEW = None
|
|
||||||
SCHEMA = schemas.ConfirmRevoke
|
|
||||||
|
|
||||||
|
|
||||||
class RevokeDef(ActionDef):
|
class RevokeDef(ActionDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.Revoke
|
SCHEMA = schemas.Revoke
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmRevokeDef(ActionDef):
|
||||||
|
VIEW = None
|
||||||
|
SCHEMA = schemas.ConfirmRevoke
|
||||||
|
|
||||||
|
|
||||||
class TradeDef(ActionDef):
|
class TradeDef(ActionDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.Trade
|
SCHEMA = schemas.Trade
|
||||||
|
|
|
@ -49,6 +49,7 @@ from ereuse_devicehub.resources.enums import AppearanceRange, BatteryHealth, Bio
|
||||||
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
from ereuse_devicehub.resources.tradedocument.models import TradeDocument
|
from ereuse_devicehub.resources.tradedocument.models import TradeDocument
|
||||||
|
from ereuse_devicehub.resources.device.metrics import TradeMetrics
|
||||||
|
|
||||||
|
|
||||||
class JoinedTableMixin:
|
class JoinedTableMixin:
|
||||||
|
@ -303,6 +304,31 @@ class ActionDevice(db.Model):
|
||||||
device_id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
|
device_id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
|
||||||
action_id = Column(UUID(as_uuid=True), ForeignKey(ActionWithMultipleDevices.id),
|
action_id = Column(UUID(as_uuid=True), ForeignKey(ActionWithMultipleDevices.id),
|
||||||
primary_key=True)
|
primary_key=True)
|
||||||
|
device = relationship(Device,
|
||||||
|
backref=backref('actions_device',
|
||||||
|
lazy=True),
|
||||||
|
primaryjoin=Device.id == device_id)
|
||||||
|
action = relationship(Action,
|
||||||
|
backref=backref('actions_device',
|
||||||
|
lazy=True),
|
||||||
|
primaryjoin=Action.id == action_id)
|
||||||
|
created = db.Column(db.TIMESTAMP(timezone=True),
|
||||||
|
nullable=False,
|
||||||
|
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))
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ActionWithMultipleTradeDocuments(ActionWithMultipleDevices):
|
class ActionWithMultipleTradeDocuments(ActionWithMultipleDevices):
|
||||||
|
@ -1359,6 +1385,16 @@ class ActionStatus(JoinedTableMixin, ActionWithMultipleTradeDocuments):
|
||||||
default=lambda: g.user.id)
|
default=lambda: g.user.id)
|
||||||
rol_user = db.relationship(User, primaryjoin=rol_user_id == User.id)
|
rol_user = db.relationship(User, primaryjoin=rol_user_id == User.id)
|
||||||
rol_user_comment = """The user that ."""
|
rol_user_comment = """The user that ."""
|
||||||
|
trade_id = db.Column(UUID(as_uuid=True),
|
||||||
|
db.ForeignKey('trade.id'),
|
||||||
|
nullable=True)
|
||||||
|
trade = db.relationship('Trade',
|
||||||
|
backref=backref('status_changes',
|
||||||
|
uselist=True,
|
||||||
|
lazy=True,
|
||||||
|
order_by=lambda: Action.end_time,
|
||||||
|
collection_class=list),
|
||||||
|
primaryjoin='ActionStatus.trade_id == Trade.id')
|
||||||
|
|
||||||
|
|
||||||
class Recycling(ActionStatus):
|
class Recycling(ActionStatus):
|
||||||
|
@ -1582,11 +1618,11 @@ class Revoke(Confirm):
|
||||||
"""Users can revoke one confirmation of one action trade"""
|
"""Users can revoke one confirmation of one action trade"""
|
||||||
|
|
||||||
|
|
||||||
class ConfirmRevoke(Confirm):
|
# class ConfirmRevoke(Confirm):
|
||||||
"""Users can confirm and accept one action revoke"""
|
# """Users can confirm and accept one action revoke"""
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return '<{0.t} {0.id} accepted by {0.user}>'.format(self)
|
# return '<{0.t} {0.id} accepted by {0.user}>'.format(self)
|
||||||
|
|
||||||
|
|
||||||
class Trade(JoinedTableMixin, ActionWithMultipleTradeDocuments):
|
class Trade(JoinedTableMixin, ActionWithMultipleTradeDocuments):
|
||||||
|
@ -1632,6 +1668,16 @@ class Trade(JoinedTableMixin, ActionWithMultipleTradeDocuments):
|
||||||
cascade=CASCADE_OWN),
|
cascade=CASCADE_OWN),
|
||||||
primaryjoin='Trade.lot_id == Lot.id')
|
primaryjoin='Trade.lot_id == Lot.id')
|
||||||
|
|
||||||
|
def get_metrics(self):
|
||||||
|
"""
|
||||||
|
This method get a list of values for calculate a metrics from a spreadsheet
|
||||||
|
"""
|
||||||
|
metrics = []
|
||||||
|
for doc in self.documents:
|
||||||
|
m = TradeMetrics(document=doc, Trade=self)
|
||||||
|
metrics.extend(m.get_metrics())
|
||||||
|
return metrics
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return '<{0.t} {0.id} executed by {0.author}>'.format(self)
|
return '<{0.t} {0.id} executed by {0.author}>'.format(self)
|
||||||
|
|
||||||
|
|
|
@ -439,17 +439,21 @@ class ActionStatus(Action):
|
||||||
|
|
||||||
@pre_load
|
@pre_load
|
||||||
def put_devices(self, data: dict):
|
def put_devices(self, data: dict):
|
||||||
if not 'devices' in data.keys():
|
if 'devices' not in data.keys():
|
||||||
data['devices'] = []
|
data['devices'] = []
|
||||||
|
|
||||||
@post_load
|
@post_load
|
||||||
def put_rol_user(self, data: dict):
|
def put_rol_user(self, data: dict):
|
||||||
for dev in data['devices']:
|
for dev in data['devices']:
|
||||||
if dev.trading in [None, 'Revoke', 'ConfirmRevoke']:
|
trades = [ac for ac in dev.actions if ac.t == 'Trade']
|
||||||
|
if not trades:
|
||||||
return data
|
return data
|
||||||
trade = [ac for ac in dev.actions if ac.t == 'Trade'][-1]
|
|
||||||
if trade.user_to != g.user:
|
trade = trades[-1]
|
||||||
|
|
||||||
|
if trade.user_from == g.user:
|
||||||
data['rol_user'] = trade.user_to
|
data['rol_user'] = trade.user_to
|
||||||
|
data['trade'] = trade
|
||||||
|
|
||||||
|
|
||||||
class Recycling(ActionStatus):
|
class Recycling(ActionStatus):
|
||||||
|
@ -581,6 +585,10 @@ class Revoke(ActionWithMultipleDevices):
|
||||||
raise ValidationError(txt)
|
raise ValidationError(txt)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmRevoke(Revoke):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ConfirmDocument(ActionWithMultipleDocuments):
|
class ConfirmDocument(ActionWithMultipleDocuments):
|
||||||
__doc__ = m.Confirm.__doc__
|
__doc__ = m.Confirm.__doc__
|
||||||
action = NestedOn('Action', only_query='id')
|
action = NestedOn('Action', only_query='id')
|
||||||
|
@ -635,7 +643,7 @@ class RevokeDocument(ActionWithMultipleDocuments):
|
||||||
|
|
||||||
|
|
||||||
class ConfirmRevokeDocument(ActionWithMultipleDocuments):
|
class ConfirmRevokeDocument(ActionWithMultipleDocuments):
|
||||||
__doc__ = m.ConfirmRevoke.__doc__
|
__doc__ = m.ConfirmRevokeDocument.__doc__
|
||||||
action = NestedOn('Action', only_query='id')
|
action = NestedOn('Action', only_query='id')
|
||||||
|
|
||||||
@validates_schema
|
@validates_schema
|
||||||
|
@ -662,66 +670,6 @@ class ConfirmRevokeDocument(ActionWithMultipleDocuments):
|
||||||
data['action'] = doc.actions[-1]
|
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):
|
class Trade(ActionWithMultipleDevices):
|
||||||
__doc__ = m.Trade.__doc__
|
__doc__ = m.Trade.__doc__
|
||||||
date = DateTime(data_key='date', required=False)
|
date = DateTime(data_key='date', required=False)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from sqlalchemy.util import OrderedSet
|
||||||
from teal.marshmallow import ValidationError
|
from teal.marshmallow import ValidationError
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
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,
|
Revoke, RevokeDocument, ConfirmDocument,
|
||||||
ConfirmRevokeDocument)
|
ConfirmRevokeDocument)
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
|
@ -66,7 +66,7 @@ class TradeView():
|
||||||
|
|
||||||
# check than the user than want to do the action is one of the users
|
# check than the user than want to do the action is one of the users
|
||||||
# involved in the action
|
# involved in the action
|
||||||
if not g.user in [self.trade.user_from, self.trade.user_to]:
|
if g.user not in [self.trade.user_from, self.trade.user_to]:
|
||||||
txt = "You do not participate in this trading"
|
txt = "You do not participate in this trading"
|
||||||
raise ValidationError(txt)
|
raise ValidationError(txt)
|
||||||
|
|
||||||
|
@ -181,15 +181,15 @@ class ConfirmView(ConfirmMixin):
|
||||||
then remove the list this device of the list of devices of this action
|
then remove the list this device of the list of devices of this action
|
||||||
"""
|
"""
|
||||||
real_devices = []
|
real_devices = []
|
||||||
|
trade = data['action']
|
||||||
|
lot = trade.lot
|
||||||
for dev in data['devices']:
|
for dev in data['devices']:
|
||||||
ac = dev.last_action_trading
|
if dev.trading(lot, simple=True) not in ['NeedConfirmation', 'NeedConfirmRevoke']:
|
||||||
if ac.type == Confirm.t and not ac.user == g.user:
|
raise ValidationError('Some devices not possible confirm.')
|
||||||
real_devices.append(dev)
|
|
||||||
|
|
||||||
data['devices'] = OrderedSet(real_devices)
|
|
||||||
|
|
||||||
# Change the owner for every devices
|
# Change the owner for every devices
|
||||||
for dev in data['devices']:
|
for dev in data['devices']:
|
||||||
|
if dev.trading(lot) == 'NeedConfirmation':
|
||||||
user_to = data['action'].user_to
|
user_to = data['action'].user_to
|
||||||
dev.change_owner(user_to)
|
dev.change_owner(user_to)
|
||||||
|
|
||||||
|
@ -215,57 +215,12 @@ class RevokeView(ConfirmMixin):
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
"""All devices need to have the status of DoubleConfirmation."""
|
"""All devices need to have the status of DoubleConfirmation."""
|
||||||
|
|
||||||
### check ###
|
devices = data['devices']
|
||||||
if not data['devices']:
|
if not devices:
|
||||||
raise ValidationError('Devices not exist.')
|
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
|
lot = data['action'].lot
|
||||||
self.model = delete_from_trade(lot, ids)
|
self.model = delete_from_trade(lot, devices)
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
class ConfirmDocumentMixin():
|
class ConfirmDocumentMixin():
|
||||||
|
|
|
@ -15,7 +15,7 @@ from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.query import things_response
|
from ereuse_devicehub.query import things_response
|
||||||
from ereuse_devicehub.resources.action.models import (Action, Snapshot, VisualTest,
|
from ereuse_devicehub.resources.action.models import (Action, Snapshot, VisualTest,
|
||||||
InitTransfer, Live, Allocate, Deallocate,
|
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 import trade as trade_view
|
||||||
from ereuse_devicehub.resources.action.views.snapshot import SnapshotView, save_json, move_json
|
from ereuse_devicehub.resources.action.views.snapshot import SnapshotView, save_json, move_json
|
||||||
from ereuse_devicehub.resources.action.views.documents import ErasedView
|
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)
|
revoke = trade_view.RevokeView(json, resource_def, self.schema)
|
||||||
return revoke.post()
|
return revoke.post()
|
||||||
|
|
||||||
if json['type'] == ConfirmRevoke.t:
|
if json['type'] == 'ConfirmRevoke':
|
||||||
confirm_revoke = trade_view.ConfirmRevokeView(json, resource_def, self.schema)
|
revoke = trade_view.RevokeView(json, resource_def, self.schema)
|
||||||
return confirm_revoke.post()
|
return revoke.post()
|
||||||
|
|
||||||
if json['type'] == 'RevokeDocument':
|
if json['type'] == 'RevokeDocument':
|
||||||
revoke = trade_view.RevokeDocumentView(json, resource_def, self.schema)
|
revoke = trade_view.RevokeDocumentView(json, resource_def, self.schema)
|
||||||
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
import copy
|
||||||
|
|
||||||
|
|
||||||
|
class MetricsMix:
|
||||||
|
"""we want get the data metrics of one device"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# self.actions.sort(key=lambda x: x.created)
|
||||||
|
self.rows = []
|
||||||
|
self.lifetime = 0
|
||||||
|
self.last_trade = None
|
||||||
|
self.action_create_by = 'Receiver'
|
||||||
|
self.status_receiver = ''
|
||||||
|
self.status_supplier = ''
|
||||||
|
self.act = None
|
||||||
|
self.end_users = 0
|
||||||
|
self.final_user_code = ''
|
||||||
|
self.trades = {}
|
||||||
|
|
||||||
|
def get_template_row(self):
|
||||||
|
"""
|
||||||
|
This is a template of a row.
|
||||||
|
"""
|
||||||
|
return {'type': '',
|
||||||
|
'action_type': 'Status',
|
||||||
|
'document_name': '',
|
||||||
|
'status_receiver': self.status_receiver,
|
||||||
|
'status_supplier': self.status_supplier,
|
||||||
|
'status_receiver_created': '',
|
||||||
|
'status_supplier_created': '',
|
||||||
|
'trade_supplier': '',
|
||||||
|
'trade_receiver': self.act.author.email,
|
||||||
|
'trade_confirmed': '',
|
||||||
|
'trade_weight': 0,
|
||||||
|
'action_create_by': self.action_create_by,
|
||||||
|
'devicehubID': self.devicehub_id,
|
||||||
|
'hid': self.hid,
|
||||||
|
'finalUserCode': '',
|
||||||
|
'numEndUsers': 0,
|
||||||
|
'liveCreate': 0,
|
||||||
|
'usageTimeHdd': self.lifetime,
|
||||||
|
'created': self.act.created,
|
||||||
|
'start': '',
|
||||||
|
'usageTimeAllocate': 0}
|
||||||
|
|
||||||
|
def get_metrics(self):
|
||||||
|
"""
|
||||||
|
This method get a list of values for calculate a metrics from a spreadsheet
|
||||||
|
"""
|
||||||
|
return self.rows
|
||||||
|
|
||||||
|
|
||||||
|
class Metrics(MetricsMix):
|
||||||
|
"""we want get the data metrics of one device"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.device = kwargs.pop('device')
|
||||||
|
self.actions = copy.copy(self.device.actions)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.hid = self.device.hid
|
||||||
|
self.devicehub_id = self.device.devicehub_id
|
||||||
|
|
||||||
|
def get_action_status(self):
|
||||||
|
"""
|
||||||
|
Mark the status of one device.
|
||||||
|
If exist one trade before this action, then modify the trade action
|
||||||
|
else, create one new row.
|
||||||
|
"""
|
||||||
|
if self.act.trade not in self.trades:
|
||||||
|
# If not exist one trade, the status is of the Receive
|
||||||
|
self.action_create_by = 'Receiver'
|
||||||
|
self.status_receiver = self.act.type
|
||||||
|
self.status_supplier = ''
|
||||||
|
row = self.get_template_row()
|
||||||
|
row['status_supplier_created'] = ''
|
||||||
|
row['status_receiver_created'] = self.act.created
|
||||||
|
self.rows.append(row)
|
||||||
|
return
|
||||||
|
|
||||||
|
trade = self.trades[self.act.trade]
|
||||||
|
|
||||||
|
if trade['trade_supplier'] == self.act.author.email:
|
||||||
|
trade['status_supplier'] = self.act.type
|
||||||
|
trade['status_supplier_created'] = self.act.created
|
||||||
|
return
|
||||||
|
|
||||||
|
if trade['trade_receiver'] == self.act.author.email:
|
||||||
|
trade['status_receiver'] = self.act.type
|
||||||
|
trade['status_receiver_created'] = self.act.created
|
||||||
|
return
|
||||||
|
|
||||||
|
# necesitamos poder poner un cambio de estado de un trade mas antiguo que last_trade
|
||||||
|
# lo mismo con confirm
|
||||||
|
|
||||||
|
def get_snapshot(self):
|
||||||
|
"""
|
||||||
|
If there are one snapshot get the last lifetime for to do a calcul of time of use.
|
||||||
|
"""
|
||||||
|
lifestimes = self.act.get_last_lifetimes()
|
||||||
|
if lifestimes:
|
||||||
|
self.lifetime = lifestimes[0]['lifetime']
|
||||||
|
|
||||||
|
def get_allocate(self):
|
||||||
|
"""
|
||||||
|
If the action is one Allocate, need modify the row base.
|
||||||
|
"""
|
||||||
|
self.action_create_by = 'Receiver'
|
||||||
|
self.end_users = self.act.end_users
|
||||||
|
self.final_user_code = self.act.final_user_code
|
||||||
|
row = self.get_template_row()
|
||||||
|
row['type'] = 'Allocate'
|
||||||
|
row['trade_supplier'] = ''
|
||||||
|
row['finalUserCode'] = self.final_user_code
|
||||||
|
row['numEndUsers'] = self.end_users
|
||||||
|
row['start'] = self.act.start_time
|
||||||
|
row['usageTimeAllocate'] = self.lifetime
|
||||||
|
self.rows.append(row)
|
||||||
|
|
||||||
|
def get_live(self):
|
||||||
|
"""
|
||||||
|
If the action is one Live, need modify the row base.
|
||||||
|
"""
|
||||||
|
self.action_create_by = 'Receiver'
|
||||||
|
row = self.get_template_row()
|
||||||
|
row['type'] = 'Live'
|
||||||
|
row['finalUserCode'] = self.final_user_code
|
||||||
|
row['numEndUsers'] = self.end_users
|
||||||
|
row['start'] = self.act.start_time
|
||||||
|
row['usageTimeAllocate'] = self.lifetime
|
||||||
|
row['liveCreate'] = self.act.created
|
||||||
|
if self.act.usage_time_hdd:
|
||||||
|
row['usageTimeHdd'] = self.act.usage_time_hdd.total_seconds() / 3600
|
||||||
|
self.rows.append(row)
|
||||||
|
|
||||||
|
def get_deallocate(self):
|
||||||
|
"""
|
||||||
|
If the action is one Dellocate, need modify the row base.
|
||||||
|
"""
|
||||||
|
self.action_create_by = 'Receiver'
|
||||||
|
row = self.get_template_row()
|
||||||
|
row['type'] = 'Deallocate'
|
||||||
|
row['start'] = self.act.start_time
|
||||||
|
self.rows.append(row)
|
||||||
|
|
||||||
|
def get_confirms(self):
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
return self.device.trading(self.act.lot, simple=True)
|
||||||
|
|
||||||
|
def get_trade(self):
|
||||||
|
"""
|
||||||
|
If this action is a trade action modify the base row.
|
||||||
|
"""
|
||||||
|
if self.act.author == self.act.user_from:
|
||||||
|
self.action_create_by = 'Supplier'
|
||||||
|
self.status_receiver = ''
|
||||||
|
|
||||||
|
row = self.get_template_row()
|
||||||
|
self.last_trade = row
|
||||||
|
row['type'] = 'Trade'
|
||||||
|
row['action_type'] = 'Trade'
|
||||||
|
row['trade_supplier'] = self.act.user_from.email
|
||||||
|
row['trade_receiver'] = self.act.user_to.email
|
||||||
|
row['status_receiver'] = self.status_receiver
|
||||||
|
row['status_supplier'] = ''
|
||||||
|
row['trade_confirmed'] = self.get_confirms()
|
||||||
|
self.trades[self.act] = row
|
||||||
|
self.rows.append(row)
|
||||||
|
|
||||||
|
def get_metrics(self):
|
||||||
|
"""
|
||||||
|
This method get a list of values for calculate a metrics from a spreadsheet
|
||||||
|
"""
|
||||||
|
for act in self.actions:
|
||||||
|
self.act = act
|
||||||
|
if act.type in ['Use', 'Refurbish', 'Recycling', 'Management']:
|
||||||
|
self.get_action_status()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if act.type == 'Snapshot':
|
||||||
|
self.get_snapshot()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if act.type == 'Allocate':
|
||||||
|
self.get_allocate()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if act.type == 'Live':
|
||||||
|
self.get_live()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if act.type == 'Deallocate':
|
||||||
|
self.get_deallocate()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if act.type == 'Trade':
|
||||||
|
self.get_trade()
|
||||||
|
continue
|
||||||
|
|
||||||
|
return self.rows
|
||||||
|
|
||||||
|
|
||||||
|
class TradeMetrics(MetricsMix):
|
||||||
|
"""we want get the data metrics of one device"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.document = kwargs.pop('document')
|
||||||
|
self.actions = copy.copy(self.document.actions)
|
||||||
|
self.hid = self.document.file_hash
|
||||||
|
self.devicehub_id = ''
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_metrics(self):
|
||||||
|
self.last_trade = next(x for x in self.actions if x.t == 'Trade')
|
||||||
|
self.act = self.last_trade
|
||||||
|
row = self.get_template_row()
|
||||||
|
|
||||||
|
row['type'] = 'Trade-Document'
|
||||||
|
row['action_type'] = 'Trade-Document'
|
||||||
|
if self.document.weight:
|
||||||
|
row['type'] = 'Trade-Container'
|
||||||
|
row['action_type'] = 'Trade-Container'
|
||||||
|
|
||||||
|
row['document_name'] = self.document.file_name
|
||||||
|
row['trade_supplier'] = self.last_trade.user_from.email
|
||||||
|
row['trade_receiver'] = self.last_trade.user_to.email
|
||||||
|
row['trade_confirmed'] = self.get_confirms()
|
||||||
|
row['status_receiver'] = ''
|
||||||
|
row['status_supplier'] = ''
|
||||||
|
row['trade_weight'] = self.document.weight
|
||||||
|
if self.document.owner == self.last_trade.user_from:
|
||||||
|
row['action_create_by'] = 'Supplier'
|
||||||
|
elif self.document.owner == self.last_trade.user_to:
|
||||||
|
row['action_create_by'] = 'Receiver'
|
||||||
|
|
||||||
|
self.rows.append(row)
|
||||||
|
|
||||||
|
return self.rows
|
||||||
|
|
||||||
|
def get_confirms(self):
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
trade = None
|
||||||
|
confirmations = []
|
||||||
|
confirms = []
|
||||||
|
for ac in self.document.actions:
|
||||||
|
if ac.t == 'Trade':
|
||||||
|
trade = ac
|
||||||
|
elif ac.t == 'ConfirmDocument':
|
||||||
|
confirms.append(ac.author)
|
||||||
|
confirmations.append(ac)
|
||||||
|
elif ac.t in ['RevokeDocument', 'ConfirmDocumentRevoke']:
|
||||||
|
confirmations.append(ac)
|
||||||
|
|
||||||
|
if confirmations and confirmations[-1].t == 'ConfirmDocument':
|
||||||
|
if trade.user_from in confirms and trade.user_to in confirms:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
|
@ -1,17 +1,17 @@
|
||||||
import pathlib
|
import pathlib
|
||||||
import copy
|
import copy
|
||||||
|
import time
|
||||||
from flask import g
|
from flask import g
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from typing import Dict, List, Set
|
from typing import Dict, List, Set
|
||||||
|
from flask_sqlalchemy import event
|
||||||
|
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
from citext import CIText
|
from citext import CIText
|
||||||
from flask_sqlalchemy import event
|
|
||||||
from ereuse_utils.naming import HID_CONVERSION_DOC, Naming
|
from ereuse_utils.naming import HID_CONVERSION_DOC, Naming
|
||||||
from flask import g
|
|
||||||
from more_itertools import unique_everseen
|
from more_itertools import unique_everseen
|
||||||
from sqlalchemy import BigInteger, Boolean, Column, Enum as DBEnum, Float, ForeignKey, Integer, \
|
from sqlalchemy import BigInteger, Boolean, Column, Enum as DBEnum, Float, ForeignKey, Integer, \
|
||||||
Sequence, SmallInteger, Unicode, inspect, text
|
Sequence, SmallInteger, Unicode, inspect, text
|
||||||
|
@ -34,11 +34,12 @@ from ereuse_devicehub.resources.enums import BatteryTechnology, CameraFacing, Co
|
||||||
DataStorageInterface, DisplayTech, PrinterTechnology, RamFormat, RamInterface, Severity, TransferState
|
DataStorageInterface, DisplayTech, PrinterTechnology, RamFormat, RamInterface, Severity, TransferState
|
||||||
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing, listener_reset_field_updated_in_actual_time
|
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing, listener_reset_field_updated_in_actual_time
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
|
from ereuse_devicehub.resources.device.metrics import Metrics
|
||||||
|
|
||||||
|
|
||||||
def create_code(context):
|
def create_code(context):
|
||||||
_id = Device.query.order_by(Device.id.desc()).first() or 1
|
_id = Device.query.order_by(Device.id.desc()).first() or 3
|
||||||
if not _id == 1:
|
if not _id == 3:
|
||||||
_id = _id.id + 1
|
_id = _id.id + 1
|
||||||
return hashcode.encode(_id)
|
return hashcode.encode(_id)
|
||||||
|
|
||||||
|
@ -173,7 +174,16 @@ class Device(Thing):
|
||||||
|
|
||||||
Actions are returned by descending ``created`` time.
|
Actions are returned by descending ``created`` time.
|
||||||
"""
|
"""
|
||||||
return sorted(chain(self.actions_multiple, self.actions_one), key=lambda x: x.created)
|
actions_multiple = copy.copy(self.actions_multiple)
|
||||||
|
actions_one = copy.copy(self.actions_one)
|
||||||
|
|
||||||
|
for ac in actions_multiple:
|
||||||
|
ac.real_created = ac.actions_device[0].created
|
||||||
|
|
||||||
|
for ac in actions_one:
|
||||||
|
ac.real_created = ac.created
|
||||||
|
|
||||||
|
return sorted(chain(actions_multiple, actions_one), key=lambda x: x.real_created)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def problems(self):
|
def problems(self):
|
||||||
|
@ -232,7 +242,7 @@ class Device(Thing):
|
||||||
:return a list of actions:
|
:return a list of actions:
|
||||||
"""
|
"""
|
||||||
hide_actions = ['Price', 'EreusePrice']
|
hide_actions = ['Price', 'EreusePrice']
|
||||||
actions = [ac for ac in self.actions if not ac.t in hide_actions]
|
actions = [ac for ac in self.actions if ac.t not in hide_actions]
|
||||||
actions.reverse()
|
actions.reverse()
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
@ -289,7 +299,7 @@ class Device(Thing):
|
||||||
status_actions = [ac.t for ac in states.Status.actions()]
|
status_actions = [ac.t for ac in states.Status.actions()]
|
||||||
history = []
|
history = []
|
||||||
for ac in self.actions:
|
for ac in self.actions:
|
||||||
if not ac.t in status_actions:
|
if ac.t not in status_actions:
|
||||||
continue
|
continue
|
||||||
if not history:
|
if not history:
|
||||||
history.append(ac)
|
history.append(ac)
|
||||||
|
@ -304,75 +314,90 @@ class Device(Thing):
|
||||||
return history
|
return history
|
||||||
|
|
||||||
@property
|
@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
|
"""The trading state, or None if no Trade action has
|
||||||
ever been performed to this device. This extract the posibilities for to do"""
|
ever been performed to this device. This extract the posibilities for to do.
|
||||||
|
This method is performed for show in the web.
|
||||||
# trade = 'Trade'
|
If you need to do one simple and generic response you can put simple=True for that."""
|
||||||
confirm = 'Confirm'
|
if not hasattr(lot, 'trade'):
|
||||||
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:
|
|
||||||
return
|
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:
|
trade = lot.trade
|
||||||
# can to do revoke_confirmed
|
user_from = trade.user_from
|
||||||
return confirm_revoke
|
user_to = trade.user_to
|
||||||
|
status = 0
|
||||||
|
last_user = None
|
||||||
|
|
||||||
if ac.type == revoke:
|
if not hasattr(trade, 'acceptances'):
|
||||||
if ac.user == g.user:
|
return Status[status]
|
||||||
# can todo revoke_pending
|
|
||||||
return revoke_pending
|
|
||||||
else:
|
|
||||||
# can to do confirm_revoke
|
|
||||||
return revoke
|
|
||||||
|
|
||||||
if ac.type == confirm:
|
for ac in self.actions:
|
||||||
if not first_owner:
|
if ac.t not in ['Confirm', 'Revoke']:
|
||||||
return
|
continue
|
||||||
|
|
||||||
if ac.user == first_owner:
|
if ac.user not in [user_from, user_to]:
|
||||||
if first_owner == g.user:
|
continue
|
||||||
# can to do revoke
|
|
||||||
return confirm
|
if ac.t == 'Confirm' and ac.action == trade:
|
||||||
else:
|
if status in [0, 6]:
|
||||||
# can to do confirm
|
if simple:
|
||||||
return need_confirm
|
status = 2
|
||||||
else:
|
continue
|
||||||
# can to do revoke
|
status = 1
|
||||||
return double_confirm
|
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
|
@property
|
||||||
def revoke(self):
|
def revoke(self):
|
||||||
|
@ -478,15 +503,15 @@ class Device(Thing):
|
||||||
def which_user_put_this_device_in_trace(self):
|
def which_user_put_this_device_in_trace(self):
|
||||||
"""which is the user than put this device in this trade"""
|
"""which is the user than put this device in this trade"""
|
||||||
actions = copy.copy(self.actions)
|
actions = copy.copy(self.actions)
|
||||||
actions.sort(key=lambda x: x.created)
|
|
||||||
actions.reverse()
|
actions.reverse()
|
||||||
last_ac = None
|
|
||||||
# search the automatic Confirm
|
# search the automatic Confirm
|
||||||
for ac in actions:
|
for ac in actions:
|
||||||
if ac.type == 'Trade':
|
if ac.type == 'Trade':
|
||||||
return last_ac.user
|
action_device = [x for x in ac.actions_device if x.device == self][0]
|
||||||
if ac.type == 'Confirm':
|
if action_device.author:
|
||||||
last_ac = ac
|
return action_device.author
|
||||||
|
|
||||||
|
return ac.author
|
||||||
|
|
||||||
def change_owner(self, new_user):
|
def change_owner(self, new_user):
|
||||||
"""util for change the owner one device"""
|
"""util for change the owner one device"""
|
||||||
|
@ -507,52 +532,8 @@ class Device(Thing):
|
||||||
"""
|
"""
|
||||||
This method get a list of values for calculate a metrics from a spreadsheet
|
This method get a list of values for calculate a metrics from a spreadsheet
|
||||||
"""
|
"""
|
||||||
actions = copy.copy(self.actions)
|
metrics = Metrics(device=self)
|
||||||
actions.sort(key=lambda x: x.created)
|
return metrics.get_metrics()
|
||||||
allocates = []
|
|
||||||
lifetime = 0
|
|
||||||
for act in actions:
|
|
||||||
if act.type == 'Snapshot':
|
|
||||||
snapshot = act
|
|
||||||
lifestimes = snapshot.get_last_lifetimes()
|
|
||||||
lifetime = 0
|
|
||||||
if lifestimes:
|
|
||||||
lifetime = lifestimes[0]['lifetime']
|
|
||||||
|
|
||||||
if act.type == 'Allocate':
|
|
||||||
allo = {'type': 'Allocate',
|
|
||||||
'devicehubID': self.devicehub_id,
|
|
||||||
'finalUserCode': act.final_user_code,
|
|
||||||
'numEndUsers': act.end_users,
|
|
||||||
'hid': self.hid,
|
|
||||||
'liveCreate': 0,
|
|
||||||
'usageTimeHdd': 0,
|
|
||||||
'start': act.start_time,
|
|
||||||
'usageTimeAllocate': lifetime}
|
|
||||||
allocates.append(allo)
|
|
||||||
|
|
||||||
if act.type == 'Live':
|
|
||||||
allocate = copy.copy(allo)
|
|
||||||
allocate['type'] = 'Live'
|
|
||||||
allocate['liveCreate'] = act.created
|
|
||||||
allocate['usageTimeHdd'] = 0
|
|
||||||
if act.usage_time_hdd:
|
|
||||||
allocate['usageTimeHdd'] = act.usage_time_hdd.total_seconds()/3600
|
|
||||||
allocates.append(allocate)
|
|
||||||
|
|
||||||
if act.type == 'Deallocate':
|
|
||||||
deallo = {'type': 'Deallocate',
|
|
||||||
'devicehubID': self.devicehub_id,
|
|
||||||
'finalUserCode': '',
|
|
||||||
'numEndUsers': '',
|
|
||||||
'hid': self.hid,
|
|
||||||
'liveCreate': 0,
|
|
||||||
'usageTimeHdd': lifetime,
|
|
||||||
'start': act.start_time,
|
|
||||||
'usageTimeAllocate': 0}
|
|
||||||
allocates.append(deallo)
|
|
||||||
|
|
||||||
return allocates
|
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
return self.id < other.id
|
return self.id < other.id
|
||||||
|
@ -681,7 +662,13 @@ class Computer(Device):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def actions(self) -> list:
|
def actions(self) -> list:
|
||||||
return sorted(chain(super().actions, self.actions_parent))
|
actions = copy.copy(super().actions)
|
||||||
|
actions_parent = copy.copy(self.actions_parent)
|
||||||
|
for ac in actions_parent:
|
||||||
|
ac.real_created = ac.created
|
||||||
|
|
||||||
|
return sorted(chain(actions, actions_parent), key=lambda x: x.real_created)
|
||||||
|
# return sorted(chain(super().actions, self.actions_parent))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ram_size(self) -> int:
|
def ram_size(self) -> int:
|
||||||
|
@ -1212,3 +1199,15 @@ class Manufacturer(db.Model):
|
||||||
|
|
||||||
listener_reset_field_updated_in_actual_time(Device)
|
listener_reset_field_updated_in_actual_time(Device)
|
||||||
|
|
||||||
|
|
||||||
|
def create_code_tag(mapper, connection, device):
|
||||||
|
"""
|
||||||
|
This function create a new tag every time than one device is create.
|
||||||
|
this tag is the same of devicehub_id.
|
||||||
|
"""
|
||||||
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
|
tag = Tag(device_id=device.id, id=device.devicehub_id)
|
||||||
|
db.session.add(tag)
|
||||||
|
|
||||||
|
|
||||||
|
event.listen(Device, 'after_insert', create_code_tag, propagate=True)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from marshmallow import post_load, pre_load, fields as f
|
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 marshmallow.validate import Length, OneOf, Range
|
||||||
from sqlalchemy.util import OrderedSet
|
from sqlalchemy.util import OrderedSet
|
||||||
from stdnum import imei, meid
|
from stdnum import imei, meid
|
||||||
|
@ -40,20 +40,22 @@ class Device(Thing):
|
||||||
width = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment)
|
width = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment)
|
||||||
height = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment)
|
height = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment)
|
||||||
depth = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment)
|
depth = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment)
|
||||||
|
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
||||||
actions = NestedOn('Action', many=True, dump_only=True, description=m.Device.actions.__doc__)
|
actions = NestedOn('Action', many=True, dump_only=True, description=m.Device.actions.__doc__)
|
||||||
|
# TODO TimeOut 2. Comment actions_one and lots if there are time out.
|
||||||
actions_one = NestedOn('Action', many=True, load_only=True, collection_class=OrderedSet)
|
actions_one = NestedOn('Action', many=True, load_only=True, collection_class=OrderedSet)
|
||||||
problems = NestedOn('Action', many=True, dump_only=True, description=m.Device.problems.__doc__)
|
problems = NestedOn('Action', many=True, dump_only=True, description=m.Device.problems.__doc__)
|
||||||
url = URL(dump_only=True, description=m.Device.url.__doc__)
|
url = URL(dump_only=True, description=m.Device.url.__doc__)
|
||||||
|
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
||||||
lots = NestedOn('Lot',
|
lots = NestedOn('Lot',
|
||||||
many=True,
|
many=True,
|
||||||
dump_only=True,
|
dump_only=True,
|
||||||
description='The lots where this device is directly under.')
|
description='The lots where this device is directly under.')
|
||||||
rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__)
|
rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__)
|
||||||
price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__)
|
price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__)
|
||||||
# trading = EnumField(states.Trading, dump_only=True, description=m.Device.trading.__doc__)
|
tradings = Dict(dump_only=True, description='')
|
||||||
trading = SanitizedStr(dump_only=True, description='')
|
|
||||||
physical = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__)
|
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__)
|
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')
|
physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor')
|
||||||
|
@ -99,6 +101,7 @@ class Device(Thing):
|
||||||
|
|
||||||
class Computer(Device):
|
class Computer(Device):
|
||||||
__doc__ = m.Computer.__doc__
|
__doc__ = m.Computer.__doc__
|
||||||
|
# TODO TimeOut 1. Comment components if there are time out.
|
||||||
components = NestedOn('Component',
|
components = NestedOn('Component',
|
||||||
many=True,
|
many=True,
|
||||||
dump_only=True,
|
dump_only=True,
|
||||||
|
@ -129,7 +132,7 @@ class Computer(Device):
|
||||||
description=m.Computer.privacy.__doc__)
|
description=m.Computer.privacy.__doc__)
|
||||||
amount = Integer(validate=f.validate.Range(min=0, max=100),
|
amount = Integer(validate=f.validate.Range(min=0, max=100),
|
||||||
description=m.Computer.amount.__doc__)
|
description=m.Computer.amount.__doc__)
|
||||||
# author_id = NestedOn(s_user.User,only_query='author_id')
|
# author_id = NestedOn(s_user.User, only_query='author_id')
|
||||||
owner_id = UUID(data_key='ownerID')
|
owner_id = UUID(data_key='ownerID')
|
||||||
transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment)
|
transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment)
|
||||||
receiver_id = UUID(data_key='receiverID')
|
receiver_id = UUID(data_key='receiverID')
|
||||||
|
|
|
@ -37,7 +37,6 @@ class Trading(State):
|
||||||
Trade = e.Trade
|
Trade = e.Trade
|
||||||
Confirm = e.Confirm
|
Confirm = e.Confirm
|
||||||
Revoke = e.Revoke
|
Revoke = e.Revoke
|
||||||
ConfirmRevoke = e.ConfirmRevoke
|
|
||||||
Cancelled = e.CancelTrade
|
Cancelled = e.CancelTrade
|
||||||
Sold = e.Sell
|
Sold = e.Sell
|
||||||
Donated = e.Donate
|
Donated = e.Donate
|
||||||
|
|
|
@ -414,6 +414,8 @@ def none2str(string):
|
||||||
return ''
|
return ''
|
||||||
return format(string)
|
return format(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_action(component, action):
|
def get_action(component, action):
|
||||||
""" Filter one action from a component or return None """
|
""" Filter one action from a component or return None """
|
||||||
result = [a for a in component.actions if a.type == action]
|
result = [a for a in component.actions if a.type == action]
|
||||||
|
@ -427,9 +429,21 @@ class ActionRow(OrderedDict):
|
||||||
# General information about allocates, deallocate and lives
|
# General information about allocates, deallocate and lives
|
||||||
self['DHID'] = allocate['devicehubID']
|
self['DHID'] = allocate['devicehubID']
|
||||||
self['Hid'] = allocate['hid']
|
self['Hid'] = allocate['hid']
|
||||||
self['Start'] = allocate['start']
|
self['Document-Name'] = allocate['document_name']
|
||||||
self['FinalUserCode'] = allocate['finalUserCode']
|
self['Action-Type'] = allocate['action_type']
|
||||||
self['NumEndUsers'] = allocate['numEndUsers']
|
self['Action-User-LastOwner-Supplier'] = allocate['trade_supplier']
|
||||||
|
self['Action-User-LastOwner-Receiver'] = allocate['trade_receiver']
|
||||||
|
self['Action-Create-By'] = allocate['action_create_by']
|
||||||
|
self['Trade-Confirmed'] = allocate['trade_confirmed']
|
||||||
|
self['Status-Created-By-Supplier-About-Reciber'] = allocate['status_supplier']
|
||||||
|
self['Status-Receiver'] = allocate['status_receiver']
|
||||||
|
self['Status Supplier – Created Date'] = allocate['status_supplier_created']
|
||||||
|
self['Status Receiver – Created Date'] = allocate['status_receiver_created']
|
||||||
|
self['Trade-Weight'] = allocate['trade_weight']
|
||||||
|
self['Action-Create'] = allocate['created']
|
||||||
|
self['Allocate-Start'] = allocate['start']
|
||||||
|
self['Allocate-User-Code'] = allocate['finalUserCode']
|
||||||
|
self['Allocate-NumUsers'] = allocate['numEndUsers']
|
||||||
self['UsageTimeAllocate'] = allocate['usageTimeAllocate']
|
self['UsageTimeAllocate'] = allocate['usageTimeAllocate']
|
||||||
self['Type'] = allocate['type']
|
self['Type'] = allocate['type']
|
||||||
self['LiveCreate'] = allocate['liveCreate']
|
self['LiveCreate'] = allocate['liveCreate']
|
||||||
|
|
|
@ -3,11 +3,9 @@ import enum
|
||||||
import uuid
|
import uuid
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import pathlib
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Callable, Iterable, Tuple
|
from typing import Callable, Iterable, Tuple
|
||||||
from decouple import config
|
|
||||||
|
|
||||||
import boltons
|
import boltons
|
||||||
import flask
|
import flask
|
||||||
|
@ -32,6 +30,8 @@ from ereuse_devicehub.resources.documents.device_row import (DeviceRow, StockRow
|
||||||
InternalStatsRow)
|
InternalStatsRow)
|
||||||
from ereuse_devicehub.resources.lot import LotView
|
from ereuse_devicehub.resources.lot import LotView
|
||||||
from ereuse_devicehub.resources.lot.models import Lot
|
from ereuse_devicehub.resources.lot.models import Lot
|
||||||
|
from ereuse_devicehub.resources.action.models import Trade
|
||||||
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
from ereuse_devicehub.resources.hash_reports import insert_hash, ReportHash, verify_hash
|
from ereuse_devicehub.resources.hash_reports import insert_hash, ReportHash, verify_hash
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,7 +90,6 @@ class DocumentView(DeviceView):
|
||||||
res = flask.make_response(template)
|
res = flask.make_response(template)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def erasure(query: db.Query):
|
def erasure(query: db.Query):
|
||||||
def erasures():
|
def erasures():
|
||||||
|
@ -151,7 +150,7 @@ class DevicesDocumentView(DeviceView):
|
||||||
class ActionsDocumentView(DeviceView):
|
class ActionsDocumentView(DeviceView):
|
||||||
@cache(datetime.timedelta(minutes=1))
|
@cache(datetime.timedelta(minutes=1))
|
||||||
def find(self, args: dict):
|
def find(self, args: dict):
|
||||||
query = (x for x in self.query(args) if x.owner_id == g.user.id)
|
query = (x for x in self.query(args))
|
||||||
return self.generate_post_csv(query)
|
return self.generate_post_csv(query)
|
||||||
|
|
||||||
def generate_post_csv(self, query):
|
def generate_post_csv(self, query):
|
||||||
|
@ -159,13 +158,26 @@ class ActionsDocumentView(DeviceView):
|
||||||
data = StringIO()
|
data = StringIO()
|
||||||
cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"')
|
cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"')
|
||||||
first = True
|
first = True
|
||||||
|
devs_id = []
|
||||||
for device in query:
|
for device in query:
|
||||||
|
devs_id.append(device.id)
|
||||||
for allocate in device.get_metrics():
|
for allocate in device.get_metrics():
|
||||||
d = ActionRow(allocate)
|
d = ActionRow(allocate)
|
||||||
if first:
|
if first:
|
||||||
cw.writerow(d.keys())
|
cw.writerow(d.keys())
|
||||||
first = False
|
first = False
|
||||||
cw.writerow(d.values())
|
cw.writerow(d.values())
|
||||||
|
query_trade = Trade.query.filter(Trade.devices.any(Device.id.in_(devs_id))).all()
|
||||||
|
|
||||||
|
for trade in query_trade:
|
||||||
|
data_rows = trade.get_metrics()
|
||||||
|
for row in data_rows:
|
||||||
|
d = ActionRow(row)
|
||||||
|
if first:
|
||||||
|
cw.writerow(d.keys())
|
||||||
|
first = False
|
||||||
|
cw.writerow(d.values())
|
||||||
|
|
||||||
bfile = data.getvalue().encode('utf-8')
|
bfile = data.getvalue().encode('utf-8')
|
||||||
output = make_response(bfile)
|
output = make_response(bfile)
|
||||||
insert_hash(bfile)
|
insert_hash(bfile)
|
||||||
|
@ -185,11 +197,11 @@ class LotsDocumentView(LotView):
|
||||||
cw = csv.writer(data)
|
cw = csv.writer(data)
|
||||||
first = True
|
first = True
|
||||||
for lot in query:
|
for lot in query:
|
||||||
l = LotRow(lot)
|
_lot = LotRow(lot)
|
||||||
if first:
|
if first:
|
||||||
cw.writerow(l.keys())
|
cw.writerow(_lot.keys())
|
||||||
first = False
|
first = False
|
||||||
cw.writerow(l.values())
|
cw.writerow(_lot.values())
|
||||||
bfile = data.getvalue().encode('utf-8')
|
bfile = data.getvalue().encode('utf-8')
|
||||||
output = make_response(bfile)
|
output = make_response(bfile)
|
||||||
insert_hash(bfile)
|
insert_hash(bfile)
|
||||||
|
@ -275,7 +287,7 @@ class StampsView(View):
|
||||||
ok = '100% coincidence. The attached file contains data 100% existing in \
|
ok = '100% coincidence. The attached file contains data 100% existing in \
|
||||||
to our backend'
|
to our backend'
|
||||||
result = ('Bad', bad)
|
result = ('Bad', bad)
|
||||||
mime = ['text/csv', 'application/pdf', 'text/plain','text/markdown',
|
mime = ['text/csv', 'application/pdf', 'text/plain', 'text/markdown',
|
||||||
'image/jpeg', 'image/png', 'text/html',
|
'image/jpeg', 'image/png', 'text/html',
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
'application/vnd.oasis.opendocument.spreadsheet',
|
'application/vnd.oasis.opendocument.spreadsheet',
|
||||||
|
@ -304,9 +316,9 @@ class InternalStatsView(DeviceView):
|
||||||
create = '{}-{}'.format(ac.created.year, ac.created.month)
|
create = '{}-{}'.format(ac.created.year, ac.created.month)
|
||||||
user = ac.author.email
|
user = ac.author.email
|
||||||
|
|
||||||
if not user in d:
|
if user not in d:
|
||||||
d[user] = {}
|
d[user] = {}
|
||||||
if not create in d[user]:
|
if create not in d[user]:
|
||||||
d[user][create] = []
|
d[user][create] = []
|
||||||
d[user][create].append(ac)
|
d[user][create].append(ac)
|
||||||
|
|
||||||
|
@ -434,4 +446,3 @@ class DocumentDef(Resource):
|
||||||
auth=app.auth)
|
auth=app.auth)
|
||||||
wbconf_view = app.auth.requires_auth(wbconf_view)
|
wbconf_view = app.auth.requires_auth(wbconf_view)
|
||||||
self.add_url_rule('/wbconf/<string:wbtype>', view_func=wbconf_view, methods=get)
|
self.add_url_rule('/wbconf/<string:wbtype>', view_func=wbconf_view, methods=get)
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,32 @@ from ereuse_devicehub.marshmallow import NestedOn
|
||||||
from ereuse_devicehub.resources.deliverynote import schemas as s_deliverynote
|
from ereuse_devicehub.resources.deliverynote import schemas as s_deliverynote
|
||||||
from ereuse_devicehub.resources.device import schemas as s_device
|
from ereuse_devicehub.resources.device import schemas as s_device
|
||||||
from ereuse_devicehub.resources.action import schemas as s_action
|
from ereuse_devicehub.resources.action import schemas as s_action
|
||||||
from ereuse_devicehub.resources.tradedocument import schemas as s_document
|
|
||||||
from ereuse_devicehub.resources.enums import TransferState
|
from ereuse_devicehub.resources.enums import TransferState
|
||||||
from ereuse_devicehub.resources.lot import models as m
|
from ereuse_devicehub.resources.lot import models as m
|
||||||
from ereuse_devicehub.resources.models import STR_SIZE
|
from ereuse_devicehub.resources.models import STR_SIZE
|
||||||
from ereuse_devicehub.resources.schemas import Thing
|
from ereuse_devicehub.resources.schemas import Thing
|
||||||
|
|
||||||
|
|
||||||
class Lot(Thing):
|
TRADE_VALUES = (
|
||||||
|
'id',
|
||||||
|
'user_from.email',
|
||||||
|
'user_to.email',
|
||||||
|
'user_from.id',
|
||||||
|
'user_to.id',
|
||||||
|
'user_to.code',
|
||||||
|
'user_from.code'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTS_VALUES = (
|
||||||
|
'id',
|
||||||
|
'file_name',
|
||||||
|
'total_weight',
|
||||||
|
'trading'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Old_Lot(Thing):
|
||||||
id = f.UUID(dump_only=True)
|
id = f.UUID(dump_only=True)
|
||||||
name = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True)
|
name = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True)
|
||||||
description = SanitizedStr(description=m.Lot.description.comment)
|
description = SanitizedStr(description=m.Lot.description.comment)
|
||||||
|
@ -29,4 +47,11 @@ class Lot(Thing):
|
||||||
receiver_address = SanitizedStr(validate=f.validate.Length(max=42))
|
receiver_address = SanitizedStr(validate=f.validate.Length(max=42))
|
||||||
deliverynote = NestedOn(s_deliverynote.Deliverynote, dump_only=True)
|
deliverynote = NestedOn(s_deliverynote.Deliverynote, dump_only=True)
|
||||||
documents = NestedOn('TradeDocument', many=True, dump_only=True)
|
documents = NestedOn('TradeDocument', many=True, dump_only=True)
|
||||||
trade = NestedOn(s_action.Trade, dump_only=True)
|
|
||||||
|
|
||||||
|
class Lot(Thing):
|
||||||
|
id = f.UUID(dump_only=True)
|
||||||
|
name = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True)
|
||||||
|
description = SanitizedStr(description=m.Lot.description.comment)
|
||||||
|
trade = f.Nested(s_action.Trade, dump_only=True, only=TRADE_VALUES)
|
||||||
|
documents = f.Nested('TradeDocument', many=True, dump_only=True, only=DOCUMENTS_VALUES)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
from sqlalchemy.util import OrderedSet
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, List, Set, Union
|
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.db import db
|
||||||
from ereuse_devicehub.query import things_response
|
from ereuse_devicehub.query import things_response
|
||||||
from ereuse_devicehub.resources.device.models import Device, Computer
|
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
|
from ereuse_devicehub.resources.lot.models import Lot, Path
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,7 +231,7 @@ class LotDeviceView(LotBaseChildrenView):
|
||||||
return
|
return
|
||||||
|
|
||||||
devices = set(Device.query.filter(Device.id.in_(ids)).filter(
|
devices = set(Device.query.filter(Device.id.in_(ids)).filter(
|
||||||
Device.owner==g.user))
|
Device.owner == g.user))
|
||||||
|
|
||||||
lot.devices.update(devices)
|
lot.devices.update(devices)
|
||||||
|
|
||||||
|
@ -246,7 +247,8 @@ class LotDeviceView(LotBaseChildrenView):
|
||||||
return
|
return
|
||||||
|
|
||||||
if lot.trade:
|
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:
|
if not g.user == lot.owner:
|
||||||
txt = 'This is not your lot'
|
txt = 'This is not your lot'
|
||||||
|
@ -258,49 +260,45 @@ class LotDeviceView(LotBaseChildrenView):
|
||||||
lot.devices.difference_update(devices)
|
lot.devices.difference_update(devices)
|
||||||
|
|
||||||
|
|
||||||
def delete_from_trade(lot: Lot, ids: Set[int]):
|
def delete_from_trade(lot: Lot, devices: List):
|
||||||
users = [lot.trade.user_from.id, lot.trade.user_to.id]
|
users = [lot.trade.user_from, lot.trade.user_to]
|
||||||
if not g.user.id in users:
|
if g.user not in users:
|
||||||
# theoretically this case is impossible
|
# theoretically this case is impossible
|
||||||
txt = 'This is not your trade'
|
txt = 'This is not your trade'
|
||||||
raise ma.ValidationError(txt)
|
raise ma.ValidationError(txt)
|
||||||
|
|
||||||
devices = set(Device.query.filter(Device.id.in_(ids)).filter(
|
# we need lock the action revoke for devices than travel for futures trades
|
||||||
Device.owner_id.in_(users)))
|
|
||||||
|
|
||||||
# Now we need to know which devices we need extract of the lot
|
|
||||||
without_confirms = set() # set of devs without confirms of user2
|
|
||||||
|
|
||||||
# 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:
|
for dev in devices:
|
||||||
# if have only one confirmation
|
if dev.owner not in users:
|
||||||
# then can be revoked and deleted of the lot
|
txt = 'This is not your device'
|
||||||
# Confirm of dev.trading mean that there are only one confirmation
|
raise ma.ValidationError(txt)
|
||||||
# and the first user than put this device in trade is the actual g.user
|
|
||||||
if dev.trading == 'Confirm':
|
drop_of_lot = []
|
||||||
without_confirms.add(dev)
|
without_confirms = []
|
||||||
|
for dev in devices:
|
||||||
|
if dev.trading(lot) in ['NeedConfirmation', 'Confirm', 'NeedConfirmRevoke']:
|
||||||
|
drop_of_lot.append(dev)
|
||||||
dev.reset_owner()
|
dev.reset_owner()
|
||||||
|
|
||||||
# we need to mark one revoke for every devs
|
if not lot.trade.confirm:
|
||||||
revoke = Revoke(action=lot.trade, user=g.user, devices=devices)
|
drop_of_lot.append(dev)
|
||||||
|
without_confirms.append(dev)
|
||||||
|
dev.reset_owner()
|
||||||
|
|
||||||
|
revoke = Revoke(action=lot.trade, user=g.user, devices=set(devices))
|
||||||
db.session.add(revoke)
|
db.session.add(revoke)
|
||||||
|
|
||||||
if not lot.trade.confirm:
|
|
||||||
# if the trade is with phantom account
|
|
||||||
without_confirms = devices
|
|
||||||
|
|
||||||
if without_confirms:
|
if without_confirms:
|
||||||
confirm_revoke = ConfirmRevoke(
|
phantom = lot.trade.user_to
|
||||||
action=revoke,
|
if lot.trade.user_to == g.user:
|
||||||
user=g.user,
|
phantom = lot.trade.user_from
|
||||||
devices=without_confirms
|
|
||||||
|
phantom_revoke = Revoke(
|
||||||
|
action=lot.trade,
|
||||||
|
user=phantom,
|
||||||
|
devices=set(without_confirms)
|
||||||
)
|
)
|
||||||
db.session.add(confirm_revoke)
|
db.session.add(phantom_revoke)
|
||||||
|
|
||||||
lot.devices.difference_update(without_confirms)
|
|
||||||
lot.trade.devices = lot.devices
|
|
||||||
|
|
||||||
|
lot.devices.difference_update(OrderedSet(drop_of_lot))
|
||||||
return revoke
|
return revoke
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -328,7 +328,7 @@ def test_outgoinlot_status_actions(action_model: models.Action, user: UserClient
|
||||||
|
|
||||||
assert device['actions'][-1]['id'] == action['id']
|
assert device['actions'][-1]['id'] == action['id']
|
||||||
assert action['author']['id'] == user.user['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
|
@pytest.mark.mvp
|
||||||
|
@ -1396,6 +1396,7 @@ def test_confirm_revoke(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
user.post(res=models.Action, data=request_post)
|
user.post(res=models.Action, data=request_post)
|
||||||
trade = models.Trade.query.one()
|
trade = models.Trade.query.one()
|
||||||
|
device = trade.devices[0]
|
||||||
|
|
||||||
request_confirm = {
|
request_confirm = {
|
||||||
'type': 'Confirm',
|
'type': 'Confirm',
|
||||||
|
@ -1416,9 +1417,10 @@ def test_confirm_revoke(user: UserClient, user2: UserClient):
|
||||||
# Normal revoke
|
# Normal revoke
|
||||||
user2.post(res=models.Action, data=request_revoke)
|
user2.post(res=models.Action, data=request_revoke)
|
||||||
|
|
||||||
# You can not to do one confirmation next of one revoke
|
# You can to do one confirmation next of one revoke
|
||||||
user2.post(res=models.Action, data=request_confirm, status=422)
|
user2.post(res=models.Action, data=request_confirm)
|
||||||
assert len(trade.acceptances) == 3
|
assert len(trade.acceptances) == 4
|
||||||
|
assert device.trading(trade.lot) == "TradeConfirmed"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -1516,9 +1518,6 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient):
|
||||||
'type': 'Confirm',
|
'type': 'Confirm',
|
||||||
'action': trade.id,
|
'action': trade.id,
|
||||||
'devices': [
|
'devices': [
|
||||||
snap1['device']['id'],
|
|
||||||
snap2['device']['id'],
|
|
||||||
snap3['device']['id'],
|
|
||||||
snap4['device']['id'],
|
snap4['device']['id'],
|
||||||
snap5['device']['id'],
|
snap5['device']['id'],
|
||||||
snap6['device']['id'],
|
snap6['device']['id'],
|
||||||
|
@ -1554,31 +1553,28 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# the SCRAP confirms the revoke action
|
# the SCRAP confirms the revoke action
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device_10.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [
|
'devices': [
|
||||||
snap10['device']['id']
|
snap10['device']['id']
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
user2.post(res=models.Action, data=request_confirm_revoke)
|
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 device_10.actions[-2].t == 'Revoke'
|
||||||
# assert len(trade.lot.devices) == len(trade.devices) == 9
|
# assert len(trade.lot.devices) == len(trade.devices) == 9
|
||||||
# assert not device_10 in trade.devices
|
# assert not device_10 in trade.devices
|
||||||
|
|
||||||
# check validation error
|
# check validation error
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device_10.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [
|
'devices': [
|
||||||
snap9['device']['id']
|
snap9['device']['id']
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
user2.post(res=models.Action, data=request_confirm_revoke, status=422)
|
|
||||||
|
|
||||||
|
|
||||||
# The manager add again device_10
|
# The manager add again device_10
|
||||||
# assert len(trade.devices) == 9
|
# assert len(trade.devices) == 9
|
||||||
lot, _ = user.post({},
|
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[-1].user == trade.user_from
|
||||||
assert device_10.actions[-2].t == 'Confirm'
|
assert device_10.actions[-2].t == 'Confirm'
|
||||||
assert device_10.actions[-2].user == trade.user_to
|
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
|
# assert len(device_10.actions) == 13
|
||||||
|
|
||||||
|
|
||||||
|
@ -1772,31 +1768,23 @@ def test_trade_case1(user: UserClient, user2: UserClient):
|
||||||
user.post(res=models.Action, data=request_post)
|
user.post(res=models.Action, data=request_post)
|
||||||
trade = models.Trade.query.one()
|
trade = models.Trade.query.one()
|
||||||
|
|
||||||
lot, _ = user.post({},
|
lot = trade.lot
|
||||||
|
device = trade.devices[0]
|
||||||
|
|
||||||
|
assert device.actions[-2].t == 'Trade'
|
||||||
|
assert device.actions[-1].t == 'Confirm'
|
||||||
|
assert device.actions[-1].user == trade.user_to
|
||||||
|
|
||||||
|
user.delete({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot['id']),
|
item='{}/devices'.format(lot.id),
|
||||||
query=devices[-1:])
|
query=devices[:-1], status=200)
|
||||||
|
|
||||||
device1, device2 = trade.devices
|
assert device not in trade.lot.devices
|
||||||
|
assert device.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
assert device1.actions[-2].t == 'Trade'
|
assert device.actions[-2].t == 'Confirm'
|
||||||
assert device1.actions[-1].t == 'Confirm'
|
assert device.actions[-1].t == 'Revoke'
|
||||||
assert device1.actions[-1].user == trade.user_to
|
assert device.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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -1855,12 +1843,13 @@ def test_trade_case2(user: UserClient, user2: UserClient):
|
||||||
# Normal revoke
|
# Normal revoke
|
||||||
user.post(res=models.Action, data=request_revoke)
|
user.post(res=models.Action, data=request_revoke)
|
||||||
|
|
||||||
assert device1.actions[-2].t == 'Revoke'
|
assert device1.actions[-2].t == 'Confirm'
|
||||||
assert device1.actions[-1].t == 'ConfirmRevoke'
|
assert device1.actions[-1].t == 'Revoke'
|
||||||
assert device1.actions[-1].user == trade.user_to
|
assert device1.actions[-1].user == trade.user_to
|
||||||
assert device2.actions[-2].t == 'Revoke'
|
assert device2.actions[-2].t == 'Confirm'
|
||||||
assert device2.actions[-1].t == 'ConfirmRevoke'
|
assert device2.actions[-1].t == 'Revoke'
|
||||||
assert device2.actions[-1].user == trade.user_to
|
assert device2.actions[-1].user == trade.user_to
|
||||||
|
assert device1.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -1868,7 +1857,6 @@ def test_trade_case2(user: UserClient, user2: UserClient):
|
||||||
def test_trade_case3(user: UserClient, user2: UserClient):
|
def test_trade_case3(user: UserClient, user2: UserClient):
|
||||||
# the pRp (manatest_usecase_confirmationger) creates a temporary lot
|
# the pRp (manatest_usecase_confirmationger) creates a temporary lot
|
||||||
lot, _ = user.post({'name': 'MyLot'}, res=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)
|
snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
|
||||||
snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot)
|
snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot)
|
||||||
|
|
||||||
|
@ -1915,9 +1903,10 @@ def test_trade_case3(user: UserClient, user2: UserClient):
|
||||||
item='{}/devices'.format(lot['id']),
|
item='{}/devices'.format(lot['id']),
|
||||||
query=devices[-1:], status=200)
|
query=devices[-1:], status=200)
|
||||||
|
|
||||||
assert device2.actions[-2].t == 'Revoke'
|
assert device2.actions[-2].t == 'Confirm'
|
||||||
assert device2.actions[-1].t == 'ConfirmRevoke'
|
assert device2.actions[-1].t == 'Revoke'
|
||||||
assert device2.actions[-1].user == trade.user_from
|
assert device2.actions[-1].user == trade.user_from
|
||||||
|
assert device2.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -1979,9 +1968,10 @@ def test_trade_case4(user: UserClient, user2: UserClient):
|
||||||
assert device1.actions[-2].t == 'Trade'
|
assert device1.actions[-2].t == 'Trade'
|
||||||
assert device1.actions[-1].t == 'Confirm'
|
assert device1.actions[-1].t == 'Confirm'
|
||||||
assert device1.actions[-1].user == trade.user_to
|
assert device1.actions[-1].user == trade.user_to
|
||||||
assert device2.actions[-2].t == 'Revoke'
|
assert device2.actions[-2].t == 'Confirm'
|
||||||
assert device2.actions[-1].t == 'ConfirmRevoke'
|
assert device2.actions[-1].t == 'Revoke'
|
||||||
assert device2.actions[-1].user == trade.user_from
|
assert device2.actions[-1].user == trade.user_from
|
||||||
|
assert device2.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -2036,8 +2026,8 @@ def test_trade_case5(user: UserClient, user2: UserClient):
|
||||||
assert device2.actions[-1].user == trade.user_from
|
assert device2.actions[-1].user == trade.user_from
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device2.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device2.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)
|
user.post(res=models.Action, data=request_confirm_revoke)
|
||||||
|
|
||||||
assert device2.actions[-2].t == '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.actions[-1].user == trade.user_to
|
||||||
|
assert device2.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -2106,8 +2097,8 @@ def test_trade_case6(user: UserClient, user2: UserClient):
|
||||||
assert device2.actions[-1].user == trade.user_to
|
assert device2.actions[-1].user == trade.user_to
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device2.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device2.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)
|
user2.post(res=models.Action, data=request_confirm_revoke)
|
||||||
|
|
||||||
assert device2.actions[-2].t == '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.actions[-1].user == trade.user_from
|
||||||
|
assert device2.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -2158,6 +2150,7 @@ def test_trade_case7(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# Normal revoke
|
# Normal revoke
|
||||||
user2.post(res=models.Action, data=request_confirm)
|
user2.post(res=models.Action, data=request_confirm)
|
||||||
|
assert device.trading(trade.lot) == 'TradeConfirmed'
|
||||||
|
|
||||||
lot, _ = user.delete({},
|
lot, _ = user.delete({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
|
@ -2165,14 +2158,14 @@ def test_trade_case7(user: UserClient, user2: UserClient):
|
||||||
query=devices, status=200)
|
query=devices, status=200)
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device.id],
|
'devices': [device.id],
|
||||||
}
|
}
|
||||||
|
|
||||||
user2.post(res=models.Action, data=request_confirm_revoke)
|
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[-1].user == trade.user_from
|
||||||
assert device.actions[-2].t == 'Revoke'
|
assert device.actions[-2].t == 'Revoke'
|
||||||
assert device.actions[-2].user == trade.user_to
|
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[-4].user == trade.user_to
|
||||||
assert device.actions[-5].t == 'Trade'
|
assert device.actions[-5].t == 'Trade'
|
||||||
assert device.actions[-5].author == trade.user_to
|
assert device.actions[-5].author == trade.user_to
|
||||||
|
assert device.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -2223,6 +2217,7 @@ def test_trade_case8(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# Normal revoke
|
# Normal revoke
|
||||||
user2.post(res=models.Action, data=request_confirm)
|
user2.post(res=models.Action, data=request_confirm)
|
||||||
|
assert device.trading(trade.lot) == 'TradeConfirmed'
|
||||||
|
|
||||||
request_revoke = {
|
request_revoke = {
|
||||||
'type': 'Revoke',
|
'type': 'Revoke',
|
||||||
|
@ -2234,14 +2229,14 @@ def test_trade_case8(user: UserClient, user2: UserClient):
|
||||||
user.post(res=models.Action, data=request_revoke)
|
user.post(res=models.Action, data=request_revoke)
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device.id],
|
'devices': [device.id],
|
||||||
}
|
}
|
||||||
|
|
||||||
user2.post(res=models.Action, data=request_confirm_revoke)
|
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[-1].user == trade.user_from
|
||||||
assert device.actions[-2].t == 'Revoke'
|
assert device.actions[-2].t == 'Revoke'
|
||||||
assert device.actions[-2].user == trade.user_to
|
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[-4].user == trade.user_to
|
||||||
assert device.actions[-5].t == 'Trade'
|
assert device.actions[-5].t == 'Trade'
|
||||||
assert device.actions[-5].author == trade.user_to
|
assert device.actions[-5].author == trade.user_to
|
||||||
|
assert device.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -2303,6 +2299,7 @@ def test_trade_case9(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# Normal revoke
|
# Normal revoke
|
||||||
user.post(res=models.Action, data=request_confirm)
|
user.post(res=models.Action, data=request_confirm)
|
||||||
|
assert device.trading(trade.lot) == 'TradeConfirmed'
|
||||||
|
|
||||||
assert device.owner == trade.user_to
|
assert device.owner == trade.user_to
|
||||||
|
|
||||||
|
@ -2312,8 +2309,8 @@ def test_trade_case9(user: UserClient, user2: UserClient):
|
||||||
query=devices[-1:], status=200)
|
query=devices[-1:], status=200)
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device.id],
|
'devices': [device.id],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2321,7 +2318,7 @@ def test_trade_case9(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
assert device.owner == trade.user_from
|
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[-1].user == trade.user_to
|
||||||
assert device.actions[-2].t == 'Revoke'
|
assert device.actions[-2].t == 'Revoke'
|
||||||
assert device.actions[-2].user == trade.user_from
|
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[-4].user == trade.user_from
|
||||||
assert device.actions[-5].t == 'Trade'
|
assert device.actions[-5].t == 'Trade'
|
||||||
assert device.actions[-5].author == trade.user_to
|
assert device.actions[-5].author == trade.user_to
|
||||||
|
assert device.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -2374,6 +2372,7 @@ def test_trade_case10(user: UserClient, user2: UserClient):
|
||||||
device1, device = trade.devices
|
device1, device = trade.devices
|
||||||
|
|
||||||
assert device.owner == trade.user_from
|
assert device.owner == trade.user_from
|
||||||
|
# assert device.trading(trade.lot) == 'Confirm'
|
||||||
|
|
||||||
request_confirm = {
|
request_confirm = {
|
||||||
'type': 'Confirm',
|
'type': 'Confirm',
|
||||||
|
@ -2383,6 +2382,7 @@ def test_trade_case10(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# Normal confirm
|
# Normal confirm
|
||||||
user.post(res=models.Action, data=request_confirm)
|
user.post(res=models.Action, data=request_confirm)
|
||||||
|
# assert device.trading(trade.lot) == 'TradeConfirmed'
|
||||||
|
|
||||||
assert device.owner == trade.user_to
|
assert device.owner == trade.user_to
|
||||||
|
|
||||||
|
@ -2394,18 +2394,18 @@ def test_trade_case10(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# Normal revoke
|
# Normal revoke
|
||||||
user2.post(res=models.Action, data=request_revoke)
|
user2.post(res=models.Action, data=request_revoke)
|
||||||
|
assert device.trading(trade.lot) == 'Revoke'
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device.id],
|
'devices': [device.id],
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=models.Action, data=request_confirm_revoke)
|
user.post(res=models.Action, data=request_confirm_revoke)
|
||||||
|
|
||||||
assert device.owner == trade.user_from
|
assert device.owner == trade.user_from
|
||||||
|
assert device.actions[-1].t == 'Revoke'
|
||||||
assert device.actions[-1].t == 'ConfirmRevoke'
|
|
||||||
assert device.actions[-1].user == trade.user_to
|
assert device.actions[-1].user == trade.user_to
|
||||||
assert device.actions[-2].t == 'Revoke'
|
assert device.actions[-2].t == 'Revoke'
|
||||||
assert device.actions[-2].user == trade.user_from
|
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[-4].user == trade.user_from
|
||||||
assert device.actions[-5].t == 'Trade'
|
assert device.actions[-5].t == 'Trade'
|
||||||
assert device.actions[-5].author == trade.user_to
|
assert device.actions[-5].author == trade.user_to
|
||||||
|
assert device.trading(trade.lot) == 'RevokeConfirmed'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -2451,6 +2452,7 @@ def test_trade_case11(user: UserClient, user2: UserClient):
|
||||||
trade = models.Trade.query.one()
|
trade = models.Trade.query.one()
|
||||||
|
|
||||||
device1, device = trade.devices
|
device1, device = trade.devices
|
||||||
|
assert device.trading(trade.lot) == 'Confirm'
|
||||||
|
|
||||||
request_confirm = {
|
request_confirm = {
|
||||||
'type': 'Confirm',
|
'type': 'Confirm',
|
||||||
|
@ -2459,21 +2461,24 @@ def test_trade_case11(user: UserClient, user2: UserClient):
|
||||||
}
|
}
|
||||||
|
|
||||||
user2.post(res=models.Action, data=request_confirm)
|
user2.post(res=models.Action, data=request_confirm)
|
||||||
|
assert device.trading(trade.lot) == 'TradeConfirmed'
|
||||||
|
|
||||||
lot, _ = user2.delete({},
|
lot, _ = user2.delete({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot['id']),
|
item='{}/devices'.format(lot['id']),
|
||||||
query=devices[-1:], status=200)
|
query=devices[-1:], status=200)
|
||||||
|
assert device.trading(trade.lot) == 'Revoke'
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device.id],
|
'devices': [device.id],
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=models.Action, data=request_confirm_revoke)
|
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[-1].user == trade.user_to
|
||||||
assert device.actions[-2].t == 'Revoke'
|
assert device.actions[-2].t == 'Revoke'
|
||||||
assert device.actions[-2].user == trade.user_from
|
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()
|
trade = models.Trade.query.one()
|
||||||
|
|
||||||
device1, device = trade.devices
|
device1, device = trade.devices
|
||||||
|
assert device.trading(trade.lot) == 'Confirm'
|
||||||
|
|
||||||
# Normal confirm
|
# Normal confirm
|
||||||
request_confirm = {
|
request_confirm = {
|
||||||
|
@ -2528,6 +2534,7 @@ def test_trade_case12(user: UserClient, user2: UserClient):
|
||||||
}
|
}
|
||||||
|
|
||||||
user2.post(res=models.Action, data=request_confirm)
|
user2.post(res=models.Action, data=request_confirm)
|
||||||
|
assert device.trading(trade.lot) == 'TradeConfirmed'
|
||||||
|
|
||||||
request_revoke = {
|
request_revoke = {
|
||||||
'type': 'Revoke',
|
'type': 'Revoke',
|
||||||
|
@ -2537,16 +2544,18 @@ def test_trade_case12(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# Normal revoke
|
# Normal revoke
|
||||||
user2.post(res=models.Action, data=request_revoke)
|
user2.post(res=models.Action, data=request_revoke)
|
||||||
|
assert device.trading(trade.lot) == 'Revoke'
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device.id],
|
'devices': [device.id],
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=models.Action, data=request_confirm_revoke)
|
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[-1].user == trade.user_to
|
||||||
assert device.actions[-2].t == 'Revoke'
|
assert device.actions[-2].t == 'Revoke'
|
||||||
assert device.actions[-2].user == trade.user_from
|
assert device.actions[-2].user == trade.user_from
|
||||||
|
@ -2597,6 +2606,8 @@ def test_trade_case13(user: UserClient, user2: UserClient):
|
||||||
query=devices[-1:])
|
query=devices[-1:])
|
||||||
|
|
||||||
device1, device = trade.devices
|
device1, device = trade.devices
|
||||||
|
assert device1.trading(trade.lot) == 'NeedConfirmation'
|
||||||
|
assert device.trading(trade.lot) == 'Confirm'
|
||||||
|
|
||||||
request_confirm = {
|
request_confirm = {
|
||||||
'type': 'Confirm',
|
'type': 'Confirm',
|
||||||
|
@ -2605,21 +2616,26 @@ def test_trade_case13(user: UserClient, user2: UserClient):
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=models.Action, data=request_confirm)
|
user.post(res=models.Action, data=request_confirm)
|
||||||
|
assert device1.trading(trade.lot) == 'Confirm'
|
||||||
|
assert device.trading(trade.lot) == 'TradeConfirmed'
|
||||||
|
|
||||||
lot, _ = user.delete({},
|
lot, _ = user.delete({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot['id']),
|
item='{}/devices'.format(lot['id']),
|
||||||
query=devices[-1:], status=200)
|
query=devices[-1:], status=200)
|
||||||
|
assert device1.trading(trade.lot) == 'Confirm'
|
||||||
|
assert device.trading(trade.lot) == 'Revoke'
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device.id],
|
'devices': [device.id],
|
||||||
}
|
}
|
||||||
|
|
||||||
user2.post(res=models.Action, data=request_confirm_revoke)
|
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[-1].user == trade.user_from
|
||||||
assert device.actions[-2].t == 'Revoke'
|
assert device.actions[-2].t == 'Revoke'
|
||||||
assert device.actions[-2].user == trade.user_to
|
assert device.actions[-2].user == trade.user_to
|
||||||
|
@ -2670,6 +2686,8 @@ def test_trade_case14(user: UserClient, user2: UserClient):
|
||||||
query=devices[-1:])
|
query=devices[-1:])
|
||||||
|
|
||||||
device1, device = trade.devices
|
device1, device = trade.devices
|
||||||
|
assert device1.trading(trade.lot) == 'NeedConfirmation'
|
||||||
|
assert device.trading(trade.lot) == 'Confirm'
|
||||||
|
|
||||||
# Normal confirm
|
# Normal confirm
|
||||||
request_confirm = {
|
request_confirm = {
|
||||||
|
@ -2679,6 +2697,7 @@ def test_trade_case14(user: UserClient, user2: UserClient):
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=models.Action, data=request_confirm)
|
user.post(res=models.Action, data=request_confirm)
|
||||||
|
assert device.trading(trade.lot) == 'TradeConfirmed'
|
||||||
|
|
||||||
request_revoke = {
|
request_revoke = {
|
||||||
'type': 'Revoke',
|
'type': 'Revoke',
|
||||||
|
@ -2688,16 +2707,18 @@ def test_trade_case14(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# Normal revoke
|
# Normal revoke
|
||||||
user.post(res=models.Action, data=request_revoke)
|
user.post(res=models.Action, data=request_revoke)
|
||||||
|
assert device.trading(trade.lot) == 'Revoke'
|
||||||
|
|
||||||
request_confirm_revoke = {
|
request_confirm_revoke = {
|
||||||
'type': 'ConfirmRevoke',
|
'type': 'Revoke',
|
||||||
'action': device.actions[-1].id,
|
'action': trade.id,
|
||||||
'devices': [device.id],
|
'devices': [device.id],
|
||||||
}
|
}
|
||||||
|
|
||||||
user2.post(res=models.Action, data=request_confirm_revoke)
|
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[-1].user == trade.user_from
|
||||||
assert device.actions[-2].t == 'Revoke'
|
assert device.actions[-2].t == 'Revoke'
|
||||||
assert device.actions[-2].user == trade.user_to
|
assert device.actions[-2].user == trade.user_to
|
||||||
|
|
|
@ -309,9 +309,10 @@ def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
|
||||||
# they are not the same tags though
|
# they are not the same tags though
|
||||||
# tag is a transient obj and db_tag the one from the db
|
# tag is a transient obj and db_tag the one from the db
|
||||||
# they have the same pk though
|
# they have the same pk though
|
||||||
assert tag != db_tag, 'They are not the same tags though'
|
|
||||||
assert db_tag.id == tag.id
|
|
||||||
assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
|
assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
|
||||||
|
assert tag != db_tag, 'They are not the same tags though'
|
||||||
|
for tag in pc.tags:
|
||||||
|
assert tag.id in ['foo', pc.devicehub_id]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -346,8 +347,9 @@ def test_sync_execute_register_tag_linked_same_device():
|
||||||
pc.tags.add(Tag(id='foo'))
|
pc.tags.add(Tag(id='foo'))
|
||||||
db_pc = Sync().execute_register(pc)
|
db_pc = Sync().execute_register(pc)
|
||||||
assert db_pc.id == orig_pc.id
|
assert db_pc.id == orig_pc.id
|
||||||
assert len(db_pc.tags) == 1
|
assert len(db_pc.tags) == 2
|
||||||
assert next(iter(db_pc.tags)).id == 'foo'
|
for tag in db_pc.tags:
|
||||||
|
assert tag.id in ['foo', db_pc.devicehub_id]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -399,6 +401,7 @@ def test_sync_execute_register_mismatch_between_tags_and_hid():
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_get_device(user: UserClient):
|
def test_get_device(user: UserClient):
|
||||||
"""Checks GETting a d.Desktop with its components."""
|
"""Checks GETting a d.Desktop with its components."""
|
||||||
|
g.user = User.query.one()
|
||||||
pc = d.Desktop(model='p1mo',
|
pc = d.Desktop(model='p1mo',
|
||||||
manufacturer='p1ma',
|
manufacturer='p1ma',
|
||||||
serial_number='p1s',
|
serial_number='p1s',
|
||||||
|
@ -437,6 +440,7 @@ def test_get_device(user: UserClient):
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_get_devices(app: Devicehub, user: UserClient):
|
def test_get_devices(app: Devicehub, user: UserClient):
|
||||||
"""Checks GETting multiple devices."""
|
"""Checks GETting multiple devices."""
|
||||||
|
g.user = User.query.one()
|
||||||
pc = d.Desktop(model='p1mo',
|
pc = d.Desktop(model='p1mo',
|
||||||
manufacturer='p1ma',
|
manufacturer='p1ma',
|
||||||
serial_number='p1s',
|
serial_number='p1s',
|
||||||
|
@ -502,7 +506,8 @@ def test_get_devices_permissions(app: Devicehub, user: UserClient, user2: UserCl
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
def test_get_devices_unassigned(app: Devicehub, user: UserClient):
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_get_devices_unassigned(user: UserClient):
|
||||||
"""Checks GETting multiple devices."""
|
"""Checks GETting multiple devices."""
|
||||||
|
|
||||||
user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
||||||
|
@ -525,7 +530,8 @@ def test_get_devices_unassigned(app: Devicehub, user: UserClient):
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(my_lot['id']),
|
item='{}/devices'.format(my_lot['id']),
|
||||||
query=[('id', device_id)])
|
query=[('id', device_id)])
|
||||||
assert lot['devices'][0]['id'] == device_id, 'Lot contains device'
|
lot = Lot.query.filter_by(id=lot['id']).one()
|
||||||
|
assert next(iter(lot.devices)).id == device_id
|
||||||
|
|
||||||
url = '/devices/?filter={"type":["Computer"]}&unassign=0'
|
url = '/devices/?filter={"type":["Computer"]}&unassign=0'
|
||||||
|
|
||||||
|
@ -604,6 +610,7 @@ def test_device_public(user: UserClient, client: Client):
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_computer_accessory_model(user: UserClient):
|
def test_computer_accessory_model(user: UserClient):
|
||||||
|
g.user = User.query.one()
|
||||||
sai = d.SAI(owner_id=user.user['id'])
|
sai = d.SAI(owner_id=user.user['id'])
|
||||||
db.session.add(sai)
|
db.session.add(sai)
|
||||||
keyboard = d.Keyboard(layout=Layouts.ES, owner_id=user.user['id'])
|
keyboard = d.Keyboard(layout=Layouts.ES, owner_id=user.user['id'])
|
||||||
|
@ -616,6 +623,7 @@ def test_computer_accessory_model(user: UserClient):
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_networking_model(user: UserClient):
|
def test_networking_model(user: UserClient):
|
||||||
|
g.user = User.query.one()
|
||||||
router = d.Router(speed=1000, wireless=True, owner_id=user.user['id'])
|
router = d.Router(speed=1000, wireless=True, owner_id=user.user['id'])
|
||||||
db.session.add(router)
|
db.session.add(router)
|
||||||
switch = d.Switch(speed=1000, wireless=False, owner_id=user.user['id'])
|
switch = d.Switch(speed=1000, wireless=False, owner_id=user.user['id'])
|
||||||
|
|
|
@ -183,7 +183,7 @@ def test_device_query(user: UserClient):
|
||||||
pc = next(d for d in i['items'] if d['type'] == 'Desktop')
|
pc = next(d for d in i['items'] if d['type'] == 'Desktop')
|
||||||
assert len(pc['actions']) == 4
|
assert len(pc['actions']) == 4
|
||||||
assert len(pc['components']) == 3
|
assert len(pc['components']) == 3
|
||||||
assert not pc['tags']
|
assert pc['tags'][0]['id'] == pc['devicehubID']
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
|
|
@ -251,13 +251,13 @@ def test_check_insert_hash(app: Devicehub, user: UserClient, client: Client):
|
||||||
assert ReportHash.query.filter_by(hash3=hash3).count() == 1
|
assert ReportHash.query.filter_by(hash3=hash3).count() == 1
|
||||||
result, status = client.get(res=documents.DocumentDef.t, item='check/', query=[('hash', hash3)])
|
result, status = client.get(res=documents.DocumentDef.t, item='check/', query=[('hash', hash3)])
|
||||||
assert status.status_code == 200
|
assert status.status_code == 200
|
||||||
assert result == True
|
assert result
|
||||||
|
|
||||||
ff = open('/tmp/test.csv', 'w')
|
ff = open('/tmp/test.csv', 'w')
|
||||||
ff.write(csv_str)
|
ff.write(csv_str)
|
||||||
ff.close()
|
ff.close()
|
||||||
|
|
||||||
a= open('/tmp/test.csv').read()
|
a = open('/tmp/test.csv').read()
|
||||||
assert hash3 == hashlib.sha3_256(a.encode('utf-8')).hexdigest()
|
assert hash3 == hashlib.sha3_256(a.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
@ -268,10 +268,7 @@ def test_export_extended(app: Devicehub, user: UserClient):
|
||||||
snapshot2, _ = user.post(file('complete.export.snapshot'), res=Snapshot, status=201)
|
snapshot2, _ = user.post(file('complete.export.snapshot'), res=Snapshot, status=201)
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
# Create a pc with a tag
|
# Create a pc with a tag
|
||||||
tag = Tag(id='foo', owner_id=user.user['id'])
|
|
||||||
# pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
|
||||||
pc = d.Device.query.filter_by(id=snapshot1['device']['id']).first()
|
pc = d.Device.query.filter_by(id=snapshot1['device']['id']).first()
|
||||||
pc.tags.add(tag)
|
|
||||||
db.session.add(pc)
|
db.session.add(pc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
|
@ -313,7 +313,6 @@ def test_post_get_lot(user: UserClient):
|
||||||
assert l['name'] == 'Foo'
|
assert l['name'] == 'Foo'
|
||||||
l, _ = user.get(res=Lot, item=l['id'])
|
l, _ = user.get(res=Lot, item=l['id'])
|
||||||
assert l['name'] == 'Foo'
|
assert l['name'] == 'Foo'
|
||||||
assert not l['children']
|
|
||||||
|
|
||||||
|
|
||||||
def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
|
def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
|
||||||
|
@ -355,12 +354,13 @@ def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
def test_lot_post_add_remove_device_view(app: Devicehub, user: UserClient):
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_lot_post_add_remove_device_view(user: UserClient):
|
||||||
"""Tests adding a device to a lot using POST and
|
"""Tests adding a device to a lot using POST and
|
||||||
removing it with DELETE.
|
removing it with DELETE.
|
||||||
"""
|
"""
|
||||||
# todo check with components
|
# todo check with components
|
||||||
with app.app_context():
|
g.user = User.query.one()
|
||||||
device = Desktop(serial_number='foo',
|
device = Desktop(serial_number='foo',
|
||||||
model='bar',
|
model='bar',
|
||||||
manufacturer='foobar',
|
manufacturer='foobar',
|
||||||
|
@ -375,25 +375,29 @@ def test_lot_post_add_remove_device_view(app: Devicehub, user: UserClient):
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(parent['id']),
|
item='{}/devices'.format(parent['id']),
|
||||||
query=[('id', device_id)])
|
query=[('id', device_id)])
|
||||||
assert lot['devices'][0]['id'] == device_id, 'Lot contains device'
|
lot = Lot.query.filter_by(id=lot['id']).one()
|
||||||
device, _ = user.get(res=Device, item=devicehub_id)
|
assert list(lot.devices)[0].id == device_id, 'Lot contains device'
|
||||||
assert len(device['lots']) == 1
|
device = Device.query.filter_by(devicehub_id=devicehub_id).one()
|
||||||
assert device['lots'][0]['id'] == lot['id'], 'Device is inside lot'
|
assert len(device.lots) == 1
|
||||||
|
# assert device['lots'][0]['id'] == lot['id'], 'Device is inside lot'
|
||||||
|
assert list(device.lots)[0].id == lot.id, 'Device is inside lot'
|
||||||
|
|
||||||
# Remove the device
|
# Remove the device
|
||||||
lot, _ = user.delete(res=Lot,
|
user.delete(res=Lot,
|
||||||
item='{}/devices'.format(parent['id']),
|
item='{}/devices'.format(parent['id']),
|
||||||
query=[('id', device_id)],
|
query=[('id', device_id)],
|
||||||
status=200)
|
status=200)
|
||||||
assert not len(lot['devices'])
|
assert not len(lot.devices)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_lot_error_add_device_from_other_user(user: UserClient):
|
def test_lot_error_add_device_from_other_user(user: UserClient):
|
||||||
|
# TODO
|
||||||
"""Tests adding a device to a lot using POST and
|
"""Tests adding a device to a lot using POST and
|
||||||
removing it with DELETE.
|
removing it with DELETE.
|
||||||
"""
|
"""
|
||||||
|
g.user = User.query.one()
|
||||||
user2 = User(email='baz@baz.cxm', password='baz')
|
user2 = User(email='baz@baz.cxm', password='baz')
|
||||||
user2.individuals.add(Person(name='Tommy'))
|
user2.individuals.add(Person(name='Tommy'))
|
||||||
db.session.add(user2)
|
db.session.add(user2)
|
||||||
|
@ -413,8 +417,9 @@ def test_lot_error_add_device_from_other_user(user: UserClient):
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(parent['id']),
|
item='{}/devices'.format(parent['id']),
|
||||||
query=[('id', device_id)])
|
query=[('id', device_id)])
|
||||||
assert lot['devices'] == [], 'Lot contains device'
|
lot = Lot.query.filter_by(id=lot['id']).one()
|
||||||
assert len(lot['devices']) == 0
|
assert list(lot.devices) == [], 'Lot contains device'
|
||||||
|
assert len(lot.devices) == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
|
|
@ -2,6 +2,9 @@ import pytest
|
||||||
|
|
||||||
from ereuse_devicehub.client import UserClient
|
from ereuse_devicehub.client import UserClient
|
||||||
from ereuse_devicehub.resources.action import models as ma
|
from ereuse_devicehub.resources.action import models as ma
|
||||||
|
from ereuse_devicehub.resources.documents import documents
|
||||||
|
from ereuse_devicehub.resources.lot.models import Lot
|
||||||
|
from ereuse_devicehub.resources.tradedocument.models import TradeDocument
|
||||||
from tests import conftest
|
from tests import conftest
|
||||||
from tests.conftest import file, yaml2json, json_encode
|
from tests.conftest import file, yaml2json, json_encode
|
||||||
|
|
||||||
|
@ -20,8 +23,7 @@ def test_simple_metrics(user: UserClient):
|
||||||
"finalUserCode": "abcdefjhi",
|
"finalUserCode": "abcdefjhi",
|
||||||
"devices": [device_id], "description": "aaa",
|
"devices": [device_id], "description": "aaa",
|
||||||
"startTime": "2020-11-01T02:00:00+00:00",
|
"startTime": "2020-11-01T02:00:00+00:00",
|
||||||
"endTime": "2020-12-01T02:00:00+00:00"
|
"endTime": "2020-12-01T02:00:00+00:00"}
|
||||||
}
|
|
||||||
|
|
||||||
# Create Allocate
|
# Create Allocate
|
||||||
user.post(res=ma.Allocate, data=post_request)
|
user.post(res=ma.Allocate, data=post_request)
|
||||||
|
@ -65,8 +67,7 @@ def test_second_hdd_metrics(user: UserClient):
|
||||||
"finalUserCode": "abcdefjhi",
|
"finalUserCode": "abcdefjhi",
|
||||||
"devices": [device_id], "description": "aaa",
|
"devices": [device_id], "description": "aaa",
|
||||||
"startTime": "2020-11-01T02:00:00+00:00",
|
"startTime": "2020-11-01T02:00:00+00:00",
|
||||||
"endTime": "2020-12-01T02:00:00+00:00"
|
"endTime": "2020-12-01T02:00:00+00:00"}
|
||||||
}
|
|
||||||
|
|
||||||
# Create Allocate
|
# Create Allocate
|
||||||
user.post(res=ma.Allocate, data=post_request)
|
user.post(res=ma.Allocate, data=post_request)
|
||||||
|
@ -109,8 +110,7 @@ def test_metrics_with_live_null(user: UserClient):
|
||||||
"finalUserCode": "abcdefjhi",
|
"finalUserCode": "abcdefjhi",
|
||||||
"devices": [device_id], "description": "aaa",
|
"devices": [device_id], "description": "aaa",
|
||||||
"startTime": "2020-11-01T02:00:00+00:00",
|
"startTime": "2020-11-01T02:00:00+00:00",
|
||||||
"endTime": "2020-12-01T02:00:00+00:00"
|
"endTime": "2020-12-01T02:00:00+00:00"}
|
||||||
}
|
|
||||||
|
|
||||||
# Create Allocate
|
# Create Allocate
|
||||||
user.post(res=ma.Allocate, data=post_request)
|
user.post(res=ma.Allocate, data=post_request)
|
||||||
|
@ -120,3 +120,240 @@ def test_metrics_with_live_null(user: UserClient):
|
||||||
res, _ = user.get("/metrics/")
|
res, _ = user.get("/metrics/")
|
||||||
assert res == metrics
|
assert res == metrics
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_metrics_action_status(user: UserClient, user2: UserClient):
|
||||||
|
""" Checks one standard query of metrics."""
|
||||||
|
# Insert computer
|
||||||
|
lenovo = yaml2json('desktop-9644w8n-lenovo-0169622.snapshot')
|
||||||
|
snap, _ = user.post(json_encode(lenovo), res=ma.Snapshot)
|
||||||
|
action = {'type': ma.Use.t, 'devices': [snap['device']['id']]}
|
||||||
|
action_use, _ = user.post(action, res=ma.Action)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='actions/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
head = 'DHID;Hid;Document-Name;Action-Type;Action-User-LastOwner-Supplier;Action-User-LastOwner-Receiver;Action-Create-By;Trade-Confirmed;Status-Created-By-Supplier-About-Reciber;Status-Receiver;Status Supplier – Created Date;Status Receiver – Created Date;Trade-Weight;Action-Create;Allocate-Start;Allocate-User-Code;Allocate-NumUsers;UsageTimeAllocate;Type;LiveCreate;UsageTimeHdd\n'
|
||||||
|
body = 'O48N2;desktop-lenovo-9644w8n-0169622-00:1a:6b:5e:7f:10;;Status;;foo@foo.com;Receiver;;;Use;;'
|
||||||
|
assert head in csv_str
|
||||||
|
assert body in csv_str
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_complet_metrics_with_trade(user: UserClient, user2: UserClient):
|
||||||
|
""" Checks one standard query of metrics in a trade enviroment."""
|
||||||
|
# Insert computer
|
||||||
|
lenovo = yaml2json('desktop-9644w8n-lenovo-0169622.snapshot')
|
||||||
|
acer = yaml2json('acer.happy.battery.snapshot')
|
||||||
|
snap1, _ = user.post(json_encode(lenovo), res=ma.Snapshot)
|
||||||
|
snap2, _ = user.post(json_encode(acer), res=ma.Snapshot)
|
||||||
|
lot, _ = user.post({'name': 'MyLot'}, res=Lot)
|
||||||
|
devices = [('id', snap1['device']['id']),
|
||||||
|
('id', snap2['device']['id'])]
|
||||||
|
lot, _ = user.post({},
|
||||||
|
res=Lot,
|
||||||
|
item='{}/devices'.format(lot['id']),
|
||||||
|
query=devices)
|
||||||
|
|
||||||
|
action = {'type': ma.Refurbish.t, 'devices': [snap1['device']['id']]}
|
||||||
|
user.post(action, res=ma.Action)
|
||||||
|
|
||||||
|
request_post = {
|
||||||
|
'type': 'Trade',
|
||||||
|
'devices': [snap1['device']['id'], snap2['device']['id']],
|
||||||
|
'userFromEmail': user.email,
|
||||||
|
'userToEmail': user2.email,
|
||||||
|
'price': 10,
|
||||||
|
'date': "2020-12-01T02:00:00+00:00",
|
||||||
|
'lot': lot['id'],
|
||||||
|
'confirms': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
user.post(res=ma.Action, data=request_post)
|
||||||
|
|
||||||
|
action = {'type': ma.Use.t, 'devices': [snap1['device']['id']]}
|
||||||
|
action_use, _ = user.post(action, res=ma.Action)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='actions/',
|
||||||
|
accept='text/csv',
|
||||||
|
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;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;NeedConfirmation;;;;;0;'
|
||||||
|
body2_acer = ';;0;0;Trade;0;4692.0\n'
|
||||||
|
|
||||||
|
assert body1_lenovo in csv_str
|
||||||
|
assert body2_lenovo in csv_str
|
||||||
|
assert body1_acer in csv_str
|
||||||
|
assert body2_acer in csv_str
|
||||||
|
|
||||||
|
# User2 mark this device as Refurbish
|
||||||
|
action = {'type': ma.Use.t, 'devices': [snap1['device']['id']]}
|
||||||
|
action_use2, _ = user2.post(action, res=ma.Action)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='actions/',
|
||||||
|
accept='text/csv',
|
||||||
|
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;NeedConfirmation;Use;Use;'
|
||||||
|
body2_lenovo = ';;0;0;Trade;0;0\n'
|
||||||
|
body2_acer = ';;0;0;Trade;0;4692.0\n'
|
||||||
|
|
||||||
|
assert body1_lenovo in csv_str
|
||||||
|
assert body2_lenovo in csv_str
|
||||||
|
assert body2_acer in csv_str
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_metrics_action_status_for_containers(user: UserClient, user2: UserClient):
|
||||||
|
""" Checks one standard query of metrics for a container."""
|
||||||
|
# Insert computer
|
||||||
|
lenovo = yaml2json('desktop-9644w8n-lenovo-0169622.snapshot')
|
||||||
|
snap, _ = user.post(json_encode(lenovo), res=ma.Snapshot)
|
||||||
|
lot, _ = user.post({'name': 'MyLot'}, res=Lot)
|
||||||
|
devices = [('id', snap['device']['id'])]
|
||||||
|
lot, _ = user.post({},
|
||||||
|
res=Lot,
|
||||||
|
item='{}/devices'.format(lot['id']),
|
||||||
|
query=devices)
|
||||||
|
request_post = {
|
||||||
|
'type': 'Trade',
|
||||||
|
'devices': [snap['device']['id']],
|
||||||
|
'userFromEmail': user.email,
|
||||||
|
'userToEmail': user2.email,
|
||||||
|
'price': 10,
|
||||||
|
'date': "2020-12-01T02:00:00+00:00",
|
||||||
|
'lot': lot['id'],
|
||||||
|
'confirms': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
user.post(res=ma.Action, data=request_post)
|
||||||
|
|
||||||
|
request_post = {
|
||||||
|
'filename': 'test.pdf',
|
||||||
|
'hash': 'bbbbbbbb',
|
||||||
|
'url': 'http://www.ereuse.org/',
|
||||||
|
'weight': 150,
|
||||||
|
'lot': lot['id']
|
||||||
|
}
|
||||||
|
tradedocument, _ = user.post(res=TradeDocument, data=request_post)
|
||||||
|
action = {'type': ma.Recycling.t, 'devices': [], 'documents': [tradedocument['id']]}
|
||||||
|
action, _ = user.post(action, res=ma.Action)
|
||||||
|
trade = TradeDocument.query.one()
|
||||||
|
|
||||||
|
assert str(trade.actions[-1].id) == action['id']
|
||||||
|
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='actions/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
|
||||||
|
body1 = ';bbbbbbbb;test.pdf;Trade-Container;foo@foo.com;foo2@foo.com;Supplier;False;;;;;150.0;'
|
||||||
|
body2 = ';;0;0;Trade-Container;0;0'
|
||||||
|
assert len(csv_str.split('\n')) == 4
|
||||||
|
assert body1 in csv_str.split('\n')[-2]
|
||||||
|
assert body2 in csv_str.split('\n')[-2]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_visual_metrics_for_old_owners(user: UserClient, user2: UserClient):
|
||||||
|
""" Checks if one old owner can see the metrics in a trade enviroment."""
|
||||||
|
# Insert computer
|
||||||
|
lenovo = yaml2json('desktop-9644w8n-lenovo-0169622.snapshot')
|
||||||
|
snap1, _ = user.post(json_encode(lenovo), res=ma.Snapshot)
|
||||||
|
lot, _ = user.post({'name': 'MyLot'}, res=Lot)
|
||||||
|
devices = [('id', snap1['device']['id'])]
|
||||||
|
lot, _ = user.post({},
|
||||||
|
res=Lot,
|
||||||
|
item='{}/devices'.format(lot['id']),
|
||||||
|
query=devices)
|
||||||
|
request_post = {
|
||||||
|
'type': 'Trade',
|
||||||
|
'devices': [snap1['device']['id']],
|
||||||
|
'userFromEmail': user.email,
|
||||||
|
'userToEmail': user2.email,
|
||||||
|
'price': 10,
|
||||||
|
'date': "2020-12-01T02:00:00+00:00",
|
||||||
|
'lot': lot['id'],
|
||||||
|
'confirms': True,
|
||||||
|
}
|
||||||
|
trade, _ = user.post(res=ma.Action, data=request_post)
|
||||||
|
|
||||||
|
request_confirm = {
|
||||||
|
'type': 'Confirm',
|
||||||
|
'action': trade['id'],
|
||||||
|
'devices': [snap1['device']['id']]
|
||||||
|
}
|
||||||
|
user2.post(res=ma.Action, data=request_confirm)
|
||||||
|
|
||||||
|
action = {'type': ma.Refurbish.t, 'devices': [snap1['device']['id']]}
|
||||||
|
action_use, _ = user.post(action, res=ma.Action)
|
||||||
|
csv_supplier, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='actions/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
csv_receiver, _ = user2.get(res=documents.DocumentDef.t,
|
||||||
|
item='actions/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
body = ';;0;0;Trade;0;0\n'
|
||||||
|
|
||||||
|
assert body in csv_receiver
|
||||||
|
assert body in csv_supplier
|
||||||
|
assert csv_receiver == csv_supplier
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_bug_trade_confirmed(user: UserClient, user2: UserClient):
|
||||||
|
"""When the receiber do a Trade, then the confirmation is wrong."""
|
||||||
|
lenovo = yaml2json('desktop-9644w8n-lenovo-0169622.snapshot')
|
||||||
|
snap1, _ = user.post(json_encode(lenovo), res=ma.Snapshot)
|
||||||
|
lot, _ = user.post({'name': 'MyLot'}, res=Lot)
|
||||||
|
devices = [('id', snap1['device']['id'])]
|
||||||
|
lot, _ = user.post({},
|
||||||
|
res=Lot,
|
||||||
|
item='{}/devices'.format(lot['id']),
|
||||||
|
query=devices)
|
||||||
|
request_post = {
|
||||||
|
'type': 'Trade',
|
||||||
|
'devices': [snap1['device']['id']],
|
||||||
|
'userFromEmail': user2.email,
|
||||||
|
'userToEmail': user.email,
|
||||||
|
'price': 10,
|
||||||
|
'date': "2020-12-01T02:00:00+00:00",
|
||||||
|
'lot': lot['id'],
|
||||||
|
'confirms': True,
|
||||||
|
}
|
||||||
|
trade, _ = user.post(res=ma.Action, data=request_post)
|
||||||
|
|
||||||
|
csv_not_confirmed, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='actions/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
request_confirm = {
|
||||||
|
'type': 'Confirm',
|
||||||
|
'action': trade['id'],
|
||||||
|
'devices': [snap1['device']['id']]
|
||||||
|
}
|
||||||
|
user2.post(res=ma.Action, data=request_confirm)
|
||||||
|
csv_confirmed, _ = user2.get(res=documents.DocumentDef.t,
|
||||||
|
item='actions/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -37,7 +37,6 @@ from tests import conftest
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures('auth_app_context')
|
@pytest.mark.usefixtures('auth_app_context')
|
||||||
# cayop
|
|
||||||
def test_snapshot_model():
|
def test_snapshot_model():
|
||||||
"""Tests creating a Snapshot with its relationships ensuring correct
|
"""Tests creating a Snapshot with its relationships ensuring correct
|
||||||
DB mapping.
|
DB mapping.
|
||||||
|
@ -318,7 +317,7 @@ def test_snapshot_tag_inner_tag(user: UserClient, tag_id: str, app: Devicehub):
|
||||||
snapshot_and_check(user, b,
|
snapshot_and_check(user, b,
|
||||||
action_types=(RateComputer.t, BenchmarkProcessor.t, VisualTest.t))
|
action_types=(RateComputer.t, BenchmarkProcessor.t, VisualTest.t))
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
tag = Tag.query.one() # type: Tag
|
tag = Tag.query.all()[0] # type: Tag
|
||||||
assert tag.device_id == 3, 'Tag should be linked to the first device'
|
assert tag.device_id == 3, 'Tag should be linked to the first device'
|
||||||
|
|
||||||
|
|
||||||
|
@ -358,7 +357,7 @@ def test_snapshot_different_properties_same_tags(user: UserClient, tag_id: str):
|
||||||
def test_snapshot_upload_twice_uuid_error(user: UserClient):
|
def test_snapshot_upload_twice_uuid_error(user: UserClient):
|
||||||
pc1 = file('basic.snapshot')
|
pc1 = file('basic.snapshot')
|
||||||
user.post(pc1, res=Snapshot)
|
user.post(pc1, res=Snapshot)
|
||||||
user.post(pc1, res=Snapshot, status=UniqueViolation)
|
user.post(pc1, res=Snapshot, status=400)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
|
|
@ -2,6 +2,7 @@ import pathlib
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import requests_mock
|
import requests_mock
|
||||||
|
from flask import g
|
||||||
from boltons.urlutils import URL
|
from boltons.urlutils import URL
|
||||||
from ereuse_utils.session import DevicehubClient
|
from ereuse_utils.session import DevicehubClient
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
|
@ -11,6 +12,7 @@ from teal.marshmallow import ValidationError
|
||||||
from ereuse_devicehub.client import UserClient, Client
|
from ereuse_devicehub.client import UserClient, Client
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.devicehub import Devicehub
|
from ereuse_devicehub.devicehub import Devicehub
|
||||||
|
from ereuse_devicehub.resources.user.models import User
|
||||||
from ereuse_devicehub.resources.action.models import Snapshot
|
from ereuse_devicehub.resources.action.models import Snapshot
|
||||||
from ereuse_devicehub.resources.agent.models import Organization
|
from ereuse_devicehub.resources.agent.models import Organization
|
||||||
from ereuse_devicehub.resources.device.models import Desktop, Device
|
from ereuse_devicehub.resources.device.models import Desktop, Device
|
||||||
|
@ -19,7 +21,7 @@ from ereuse_devicehub.resources.tag import Tag
|
||||||
from ereuse_devicehub.resources.tag.view import CannotCreateETag, LinkedToAnotherDevice, \
|
from ereuse_devicehub.resources.tag.view import CannotCreateETag, LinkedToAnotherDevice, \
|
||||||
TagNotLinked
|
TagNotLinked
|
||||||
from tests import conftest
|
from tests import conftest
|
||||||
from tests.conftest import file, yaml2json, json_encode
|
from tests.conftest import yaml2json, json_encode
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -41,6 +43,7 @@ def test_create_tag(user: UserClient):
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_create_tag_with_device(user: UserClient):
|
def test_create_tag_with_device(user: UserClient):
|
||||||
"""Creates a tag specifying linked with one device."""
|
"""Creates a tag specifying linked with one device."""
|
||||||
|
g.user = User.query.one()
|
||||||
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
||||||
db.session.add(pc)
|
db.session.add(pc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -60,13 +63,14 @@ def test_create_tag_with_device(user: UserClient):
|
||||||
def test_delete_tags(user: UserClient, client: Client):
|
def test_delete_tags(user: UserClient, client: Client):
|
||||||
"""Delete a named tag."""
|
"""Delete a named tag."""
|
||||||
# Delete Tag Named
|
# Delete Tag Named
|
||||||
|
g.user = User.query.one()
|
||||||
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
||||||
db.session.add(pc)
|
db.session.add(pc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
tag = Tag(id='bar', owner_id=user.user['id'], device_id=pc.id)
|
tag = Tag(id='bar', owner_id=user.user['id'], device_id=pc.id)
|
||||||
db.session.add(tag)
|
db.session.add(tag)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
tag = Tag.query.one()
|
tag = Tag.query.all()[-1]
|
||||||
assert tag.id == 'bar'
|
assert tag.id == 'bar'
|
||||||
# Is not possible delete one tag linked to one device
|
# Is not possible delete one tag linked to one device
|
||||||
res, _ = user.delete(res=Tag, item=tag.id, status=422)
|
res, _ = user.delete(res=Tag, item=tag.id, status=422)
|
||||||
|
@ -88,12 +92,12 @@ def test_delete_tags(user: UserClient, client: Client):
|
||||||
tag = Tag(id='bar-1', org=org, provider=URL('http://foo.bar'), owner_id=user.user['id'])
|
tag = Tag(id='bar-1', org=org, provider=URL('http://foo.bar'), owner_id=user.user['id'])
|
||||||
db.session.add(tag)
|
db.session.add(tag)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
tag = Tag.query.one()
|
tag = Tag.query.all()[-1]
|
||||||
assert tag.id == 'bar-1'
|
assert tag.id == 'bar-1'
|
||||||
res, _ = user.delete(res=Tag, item=tag.id, status=422)
|
res, _ = user.delete(res=Tag, item=tag.id, status=422)
|
||||||
msg = 'This tag {} is unnamed tag. It is imposible delete.'.format(tag.id)
|
msg = 'This tag {} is unnamed tag. It is imposible delete.'.format(tag.id)
|
||||||
assert msg in res['message']
|
assert msg in res['message']
|
||||||
tag = Tag.query.one()
|
tag = Tag.query.all()[-1]
|
||||||
assert tag.id == 'bar-1'
|
assert tag.id == 'bar-1'
|
||||||
|
|
||||||
|
|
||||||
|
@ -182,6 +186,7 @@ def test_tag_get_device_from_tag_endpoint(app: Devicehub, user: UserClient):
|
||||||
"""Checks getting a linked device from a tag endpoint"""
|
"""Checks getting a linked device from a tag endpoint"""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
# Create a pc with a tag
|
# Create a pc with a tag
|
||||||
|
g.user = User.query.one()
|
||||||
tag = Tag(id='foo-bar', owner_id=user.user['id'])
|
tag = Tag(id='foo-bar', owner_id=user.user['id'])
|
||||||
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
||||||
pc.tags.add(tag)
|
pc.tags.add(tag)
|
||||||
|
@ -213,6 +218,7 @@ def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: Us
|
||||||
system should not return any of both (to be deterministic) so
|
system should not return any of both (to be deterministic) so
|
||||||
it should raise an exception.
|
it should raise an exception.
|
||||||
"""
|
"""
|
||||||
|
g.user = User.query.all()[0]
|
||||||
db.session.add(Tag(id='foo', secondary='bar', owner_id=user.user['id']))
|
db.session.add(Tag(id='foo', secondary='bar', owner_id=user.user['id']))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -276,6 +282,7 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
|
||||||
Checks search has the term.
|
Checks search has the term.
|
||||||
"""
|
"""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
g.user = User.query.one()
|
||||||
db.session.add(Tag('foo-bar', secondary='foo-sec', owner_id=user.user['id']))
|
db.session.add(Tag('foo-bar', secondary='foo-sec', owner_id=user.user['id']))
|
||||||
desktop = Desktop(serial_number='foo', chassis=ComputerChassis.AllInOne, owner_id=user.user['id'])
|
desktop = Desktop(serial_number='foo', chassis=ComputerChassis.AllInOne, owner_id=user.user['id'])
|
||||||
db.session.add(desktop)
|
db.session.add(desktop)
|
||||||
|
@ -284,7 +291,7 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
|
||||||
devicehub_id = desktop.devicehub_id
|
devicehub_id = desktop.devicehub_id
|
||||||
user.put({}, res=Tag, item='foo-bar/device/{}'.format(desktop_id), status=204)
|
user.put({}, res=Tag, item='foo-bar/device/{}'.format(desktop_id), status=204)
|
||||||
device, _ = user.get(res=Device, item=devicehub_id)
|
device, _ = user.get(res=Device, item=devicehub_id)
|
||||||
assert device['tags'][0]['id'] == 'foo-bar'
|
assert 'foo-bar' in [x['id'] for x in device['tags']]
|
||||||
|
|
||||||
# Device already linked
|
# Device already linked
|
||||||
# Just returns an OK to conform to PUT as anything changes
|
# Just returns an OK to conform to PUT as anything changes
|
||||||
|
@ -323,8 +330,8 @@ def test_tag_secondary_workbench_link_find(user: UserClient):
|
||||||
s['device']['tags'] = [{'id': 'foo', 'secondary': 'bar', 'type': 'Tag'}]
|
s['device']['tags'] = [{'id': 'foo', 'secondary': 'bar', 'type': 'Tag'}]
|
||||||
snapshot, _ = user.post(json_encode(s), res=Snapshot)
|
snapshot, _ = user.post(json_encode(s), res=Snapshot)
|
||||||
device, _ = user.get(res=Device, item=snapshot['device']['devicehubID'])
|
device, _ = user.get(res=Device, item=snapshot['device']['devicehubID'])
|
||||||
assert device['tags'][0]['id'] == 'foo'
|
assert 'foo' in [x['id'] for x in device['tags']]
|
||||||
assert device['tags'][0]['secondary'] == 'bar'
|
assert 'bar' in [x.get('secondary') for x in device['tags']]
|
||||||
|
|
||||||
r, _ = user.get(res=Device, query=[('search', 'foo'), ('filter', {'type': ['Computer']})])
|
r, _ = user.get(res=Device, query=[('search', 'foo'), ('filter', {'type': ['Computer']})])
|
||||||
assert len(r['items']) == 1
|
assert len(r['items']) == 1
|
||||||
|
@ -412,6 +419,7 @@ def test_get_tag_permissions(app: Devicehub, user: UserClient, user2: UserClient
|
||||||
"""Creates a tag specifying a custom organization."""
|
"""Creates a tag specifying a custom organization."""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
# Create a pc with a tag
|
# Create a pc with a tag
|
||||||
|
g.user = User.query.all()[0]
|
||||||
tag = Tag(id='foo-bar', owner_id=user.user['id'])
|
tag = Tag(id='foo-bar', owner_id=user.user['id'])
|
||||||
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
|
||||||
pc.tags.add(tag)
|
pc.tags.add(tag)
|
||||||
|
@ -424,5 +432,5 @@ def test_get_tag_permissions(app: Devicehub, user: UserClient, user2: UserClient
|
||||||
computer2, res2 = user2.get(url, None)
|
computer2, res2 = user2.get(url, None)
|
||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
assert res2.status_code == 200
|
assert res2.status_code == 200
|
||||||
assert len(computer['items']) == 1
|
assert len(computer['items']) == 2
|
||||||
assert len(computer2['items']) == 0
|
assert len(computer2['items']) == 0
|
||||||
|
|
|
@ -66,8 +66,8 @@ def test_workbench_server_condensed(user: UserClient):
|
||||||
assert device['rate']['rating'] == 1
|
assert device['rate']['rating'] == 1
|
||||||
assert device['rate']['type'] == RateComputer.t
|
assert device['rate']['type'] == RateComputer.t
|
||||||
# TODO JN why haven't same order in actions on each execution?
|
# 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 device['tags'][0]['id'] == 'tag1'
|
assert 'tag1' in [x['id'] for x in device['tags']]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Functionality not yet developed.')
|
@pytest.mark.xfail(reason='Functionality not yet developed.')
|
||||||
|
@ -184,7 +184,7 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||||
assert pc['serialNumber'] == 'b8oaas048286'
|
assert pc['serialNumber'] == 'b8oaas048286'
|
||||||
assert pc['manufacturer'] == 'asustek computer inc.'
|
assert pc['manufacturer'] == 'asustek computer inc.'
|
||||||
assert pc['hid'] == 'laptop-asustek_computer_inc-1001pxd-b8oaas048286-14:da:e9:42:f6:7c'
|
assert pc['hid'] == 'laptop-asustek_computer_inc-1001pxd-b8oaas048286-14:da:e9:42:f6:7c'
|
||||||
assert pc['tags'] == []
|
assert len(pc['tags']) == 1
|
||||||
assert pc['networkSpeeds'] == [100, 0], 'Although it has WiFi we do not know the speed'
|
assert pc['networkSpeeds'] == [100, 0], 'Although it has WiFi we do not know the speed'
|
||||||
assert pc['rate']
|
assert pc['rate']
|
||||||
rate = pc['rate']
|
rate = pc['rate']
|
||||||
|
|
Reference in New Issue