Merge branch 'testing' into feature/delete-devices

This commit is contained in:
Cayo Puigdefabregas 2021-11-19 12:07:14 +01:00
commit 3b41ddb66a
29 changed files with 1194 additions and 493 deletions

View File

@ -14,36 +14,38 @@ ml).
## [1.0.10-beta]
- [addend] #170 can delete/deactivate 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]
- [addend] #159 external document as proof of erase of disk
- [addend] #162 adding lot for devices unassigned
- [added] #159 external document as proof of erase of disk
- [added] #162 adding lot for devices unassigned
## [1.0.8-beta]
- [bugfix] #161 fixing DataStorage with bigInteger
## [1.0.7-beta]
- [addend] #158 support for encrypted snapshots data
- [addend] #135 adding trade system
- [addend] #140 adding endpoint for download the settings for usb workbench
- [added] #158 support for encrypted snapshots data
- [added] #135 adding trade system
- [added] #140 adding endpoint for download the settings for usb workbench
## [1.0.6-beta]
- [bugfix] #143 biginteger instead of integer in TestDataStorage
## [1.0.5-beta]
- [addend] #124 adding endpoint for extract the internal stats of use
- [addend] #122 system for verify all documents that it's produced from devicehub
- [addend] #127 add one code for every named tag
- [addend] #131 add one code for every device
- [added] #124 adding endpoint for extract the internal stats of use
- [added] #122 system for verify all documents that it's produced from devicehub
- [added] #127 add one code for every named tag
- [added] #131 add one code for every device
- [bugfix] #138 search device with devicehubId
## [1.0.4-beta]
- [addend] #95 adding endpoint for check the hash of one report
- [addend] #98 adding endpoint for insert a new live
- [addend] #98 adding endpoint for get all licences in one query
- [addend] #102 adding endpoint for download metrics
- [added] #95 adding endpoint for check the hash of one report
- [added] #98 adding endpoint for insert a new live
- [added] #98 adding endpoint for get all licences in one query
- [added] #102 adding endpoint for download metrics
- [bugfix] #100 fixing bug of scheme live
- [bugfix] #101 fixing bug when 2 users have one device and launch one live
- [changes] #114 clean blockchain of all models
@ -52,11 +54,11 @@ ml).
- [remove] #114 remove proof system
## [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
## [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
- [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

View File

@ -139,7 +139,7 @@ class Dummy:
res=Lot,
item='{}/devices'.format(lot_user['id']),
query=[('id', pc) for pc in itertools.islice(pcs, 1, 4)])
assert len(lot['devices'])
# assert len(lot['devices'])
lot2, _ = user2.post({},
res=Lot,

View File

@ -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()}')

View File

@ -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

View File

@ -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()}')

View File

@ -285,16 +285,16 @@ class ConfirmDef(ActionDef):
SCHEMA = schemas.Confirm
class ConfirmRevokeDef(ActionDef):
VIEW = None
SCHEMA = schemas.ConfirmRevoke
class RevokeDef(ActionDef):
VIEW = None
SCHEMA = schemas.Revoke
class ConfirmRevokeDef(ActionDef):
VIEW = None
SCHEMA = schemas.ConfirmRevoke
class TradeDef(ActionDef):
VIEW = None
SCHEMA = schemas.Trade

View File

@ -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.user.models import User
from ereuse_devicehub.resources.tradedocument.models import TradeDocument
from ereuse_devicehub.resources.device.metrics import TradeMetrics
class JoinedTableMixin:
@ -303,6 +304,31 @@ class ActionDevice(db.Model):
device_id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
action_id = Column(UUID(as_uuid=True), ForeignKey(ActionWithMultipleDevices.id),
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):
@ -1359,6 +1385,16 @@ class ActionStatus(JoinedTableMixin, ActionWithMultipleTradeDocuments):
default=lambda: g.user.id)
rol_user = db.relationship(User, primaryjoin=rol_user_id == User.id)
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):
@ -1582,11 +1618,11 @@ class Revoke(Confirm):
"""Users can revoke one confirmation of one action trade"""
class ConfirmRevoke(Confirm):
"""Users can confirm and accept one action revoke"""
# class ConfirmRevoke(Confirm):
# """Users can confirm and accept one action revoke"""
def __repr__(self) -> str:
return '<{0.t} {0.id} accepted by {0.user}>'.format(self)
# def __repr__(self) -> str:
# return '<{0.t} {0.id} accepted by {0.user}>'.format(self)
class Trade(JoinedTableMixin, ActionWithMultipleTradeDocuments):
@ -1632,6 +1668,16 @@ class Trade(JoinedTableMixin, ActionWithMultipleTradeDocuments):
cascade=CASCADE_OWN),
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:
return '<{0.t} {0.id} executed by {0.author}>'.format(self)

View File

@ -439,17 +439,21 @@ class ActionStatus(Action):
@pre_load
def put_devices(self, data: dict):
if not 'devices' in data.keys():
if 'devices' not in data.keys():
data['devices'] = []
@post_load
def put_rol_user(self, data: dict):
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
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['trade'] = trade
class Recycling(ActionStatus):
@ -581,6 +585,10 @@ class Revoke(ActionWithMultipleDevices):
raise ValidationError(txt)
class ConfirmRevoke(Revoke):
pass
class ConfirmDocument(ActionWithMultipleDocuments):
__doc__ = m.Confirm.__doc__
action = NestedOn('Action', only_query='id')
@ -635,7 +643,7 @@ class RevokeDocument(ActionWithMultipleDocuments):
class ConfirmRevokeDocument(ActionWithMultipleDocuments):
__doc__ = m.ConfirmRevoke.__doc__
__doc__ = m.ConfirmRevokeDocument.__doc__
action = NestedOn('Action', only_query='id')
@validates_schema
@ -662,66 +670,6 @@ class ConfirmRevokeDocument(ActionWithMultipleDocuments):
data['action'] = doc.actions[-1]
class ConfirmRevoke(ActionWithMultipleDevices):
__doc__ = m.ConfirmRevoke.__doc__
action = NestedOn('Action', only_query='id')
@validates_schema
def validate_revoke(self, data: dict):
for dev in data['devices']:
# if device not exist in the Trade, then this query is wrong
if not dev in data['action'].devices:
txt = "Device {} not exist in the trade".format(dev.devicehub_id)
raise ValidationError(txt)
for doc in data.get('documents', []):
# if document not exist in the Trade, then this query is wrong
if not doc in data['action'].documents:
txt = "Document {} not exist in the trade".format(doc.file_name)
raise ValidationError(txt)
@validates_schema
def validate_docs(self, data):
"""Check if there are or no one before confirmation,
This is not checked in the view becouse the list of documents is inmutable
"""
if not data['devices'] == OrderedSet():
return
documents = []
for doc in data['documents']:
actions = copy.copy(doc.actions)
actions.reverse()
for ac in actions:
if ac == data['action']:
# If document have the last action the action for confirm
documents.append(doc)
break
if ac.t == 'Revoke' and not ac.user == g.user:
# If document is revoke before you can Confirm now
# and revoke is an action of one other user
documents.append(doc)
break
if ac.t == ConfirmRevoke.t and ac.user == g.user:
# If document is confirmed we don't need confirmed again
break
if ac.t == Confirm.t:
# if onwer of trade confirm again before than this user Confirm the
# revoke, then is not possible confirm the revoke
#
# If g.user confirm the trade before do a ConfirmRevoke
# then g.user can not to do the ConfirmRevoke more
break
if not documents:
txt = 'No there are documents with revoke for confirm'
raise ValidationError(txt)
class Trade(ActionWithMultipleDevices):
__doc__ = m.Trade.__doc__
date = DateTime(data_key='date', required=False)

View File

@ -3,7 +3,7 @@ from sqlalchemy.util import OrderedSet
from teal.marshmallow import ValidationError
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.action.models import (Trade, Confirm, ConfirmRevoke,
from ereuse_devicehub.resources.action.models import (Trade, Confirm,
Revoke, RevokeDocument, ConfirmDocument,
ConfirmRevokeDocument)
from ereuse_devicehub.resources.user.models import User
@ -66,7 +66,7 @@ class TradeView():
# check than the user than want to do the action is one of the users
# 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"
raise ValidationError(txt)
@ -181,17 +181,17 @@ class ConfirmView(ConfirmMixin):
then remove the list this device of the list of devices of this action
"""
real_devices = []
trade = data['action']
lot = trade.lot
for dev in data['devices']:
ac = dev.last_action_trading
if ac.type == Confirm.t and not ac.user == g.user:
real_devices.append(dev)
data['devices'] = OrderedSet(real_devices)
if dev.trading(lot, simple=True) not in ['NeedConfirmation', 'NeedConfirmRevoke']:
raise ValidationError('Some devices not possible confirm.')
# Change the owner for every devices
for dev in data['devices']:
user_to = data['action'].user_to
dev.change_owner(user_to)
if dev.trading(lot) == 'NeedConfirmation':
user_to = data['action'].user_to
dev.change_owner(user_to)
class RevokeView(ConfirmMixin):
@ -215,57 +215,12 @@ class RevokeView(ConfirmMixin):
def validate(self, data):
"""All devices need to have the status of DoubleConfirmation."""
### check ###
if not data['devices']:
devices = data['devices']
if not devices:
raise ValidationError('Devices not exist.')
for dev in data['devices']:
if not dev.trading == 'TradeConfirmed':
txt = 'Some of devices do not have enough to confirm for to do a revoke'
ValidationError(txt)
### End check ###
ids = {d.id for d in data['devices']}
lot = data['action'].lot
self.model = delete_from_trade(lot, ids)
class ConfirmRevokeView(ConfirmMixin):
"""Handler for manager the Confirmation register from post
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': action_revoke.id,
'devices': [device_id]
}
"""
Model = ConfirmRevoke
def validate(self, data):
"""All devices need to have the status of revoke."""
if not data['action'].type == 'Revoke':
txt = 'Error: this action is not a revoke action'
ValidationError(txt)
for dev in data['devices']:
if not dev.trading == 'Revoke':
txt = 'Some of devices do not have revoke to confirm'
ValidationError(txt)
devices = OrderedSet(data['devices'])
data['devices'] = devices
# Change the owner for every devices
# data['action'] == 'Revoke'
trade = data['action'].action
for dev in devices:
dev.reset_owner()
trade.lot.devices.difference_update(devices)
self.model = delete_from_trade(lot, devices)
class ConfirmDocumentMixin():

View File

@ -15,7 +15,7 @@ from ereuse_devicehub.db import db
from ereuse_devicehub.query import things_response
from ereuse_devicehub.resources.action.models import (Action, Snapshot, VisualTest,
InitTransfer, Live, Allocate, Deallocate,
Trade, Confirm, ConfirmRevoke, Revoke)
Trade, Confirm, Revoke)
from ereuse_devicehub.resources.action.views import trade as trade_view
from ereuse_devicehub.resources.action.views.snapshot import SnapshotView, save_json, move_json
from ereuse_devicehub.resources.action.views.documents import ErasedView
@ -235,9 +235,9 @@ class ActionView(View):
revoke = trade_view.RevokeView(json, resource_def, self.schema)
return revoke.post()
if json['type'] == ConfirmRevoke.t:
confirm_revoke = trade_view.ConfirmRevokeView(json, resource_def, self.schema)
return confirm_revoke.post()
if json['type'] == 'ConfirmRevoke':
revoke = trade_view.RevokeView(json, resource_def, self.schema)
return revoke.post()
if json['type'] == 'RevokeDocument':
revoke = trade_view.RevokeDocumentView(json, resource_def, self.schema)

View File

@ -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

View File

@ -1,17 +1,17 @@
import pathlib
import copy
import time
from flask import g
from contextlib import suppress
from fractions import Fraction
from itertools import chain
from operator import attrgetter
from typing import Dict, List, Set
from flask_sqlalchemy import event
from boltons import urlutils
from citext import CIText
from flask_sqlalchemy import event
from ereuse_utils.naming import HID_CONVERSION_DOC, Naming
from flask import g
from more_itertools import unique_everseen
from sqlalchemy import BigInteger, Boolean, Column, Enum as DBEnum, Float, ForeignKey, Integer, \
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
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.device.metrics import Metrics
def create_code(context):
_id = Device.query.order_by(Device.id.desc()).first() or 1
if not _id == 1:
_id = Device.query.order_by(Device.id.desc()).first() or 3
if not _id == 3:
_id = _id.id + 1
return hashcode.encode(_id)
@ -173,7 +174,16 @@ class Device(Thing):
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
def problems(self):
@ -232,7 +242,7 @@ class Device(Thing):
:return a list of actions:
"""
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()
return actions
@ -289,7 +299,7 @@ class Device(Thing):
status_actions = [ac.t for ac in states.Status.actions()]
history = []
for ac in self.actions:
if not ac.t in status_actions:
if ac.t not in status_actions:
continue
if not history:
history.append(ac)
@ -304,75 +314,90 @@ class Device(Thing):
return history
@property
def trading(self):
def tradings(self):
return {str(x.id): self.trading(x.lot) for x in self.actions if x.t == 'Trade'}
def trading(self, lot, simple=None):
"""The trading state, or None if no Trade action has
ever been performed to this device. This extract the posibilities for to do"""
# trade = 'Trade'
confirm = 'Confirm'
need_confirm = 'NeedConfirmation'
double_confirm = 'TradeConfirmed'
revoke = 'Revoke'
revoke_pending = 'RevokePending'
confirm_revoke = 'ConfirmRevoke'
# revoke_confirmed = 'RevokeConfirmed'
# return the correct status of trade depending of the user
##### CASES #####
## User1 == owner of trade (This user have automatic Confirmation)
## =======================
## if the last action is => only allow to do
## ==========================================
## Confirmation not User1 => Revoke
## Confirmation User1 => Revoke
## Revoke not User1 => ConfirmRevoke
## Revoke User1 => RevokePending
## RevokeConfirmation => RevokeConfirmed
##
##
## User2 == Not owner of trade
## =======================
## if the last action is => only allow to do
## ==========================================
## Confirmation not User2 => Confirm
## Confirmation User2 => Revoke
## Revoke not User2 => ConfirmRevoke
## Revoke User2 => RevokePending
## RevokeConfirmation => RevokeConfirmed
ac = self.last_action_trading
if not ac:
ever been performed to this device. This extract the posibilities for to do.
This method is performed for show in the web.
If you need to do one simple and generic response you can put simple=True for that."""
if not hasattr(lot, 'trade'):
return
first_owner = self.which_user_put_this_device_in_trace()
Status = {0: 'Trade',
1: 'Confirm',
2: 'NeedConfirmation',
3: 'TradeConfirmed',
4: 'Revoke',
5: 'NeedConfirmRevoke',
6: 'RevokeConfirmed'}
if ac.type == confirm_revoke:
# can to do revoke_confirmed
return confirm_revoke
trade = lot.trade
user_from = trade.user_from
user_to = trade.user_to
status = 0
last_user = None
if ac.type == revoke:
if ac.user == g.user:
# can todo revoke_pending
return revoke_pending
else:
# can to do confirm_revoke
return revoke
if not hasattr(trade, 'acceptances'):
return Status[status]
if ac.type == confirm:
if not first_owner:
return
for ac in self.actions:
if ac.t not in ['Confirm', 'Revoke']:
continue
if ac.user == first_owner:
if first_owner == g.user:
# can to do revoke
return confirm
else:
# can to do confirm
return need_confirm
else:
# can to do revoke
return double_confirm
if ac.user not in [user_from, user_to]:
continue
if ac.t == 'Confirm' and ac.action == trade:
if status in [0, 6]:
if simple:
status = 2
continue
status = 1
last_user = ac.user
if ac.user == user_from and user_to == g.user:
status = 2
if ac.user == user_to and user_from == g.user:
status = 2
continue
if status in [1, 2]:
if last_user != ac.user:
status = 3
last_user = ac.user
continue
if status in [4, 5]:
status = 3
last_user = ac.user
continue
if ac.t == 'Revoke' and ac.action == trade:
if status == 3:
if simple:
status = 5
continue
status = 4
last_user = ac.user
if ac.user == user_from and user_to == g.user:
status = 5
if ac.user == user_to and user_from == g.user:
status = 5
continue
if status in [4, 5]:
if last_user != ac.user:
status = 6
last_user = ac.user
continue
if status in [1, 2]:
status = 6
last_user = ac.user
continue
return Status[status]
@property
def revoke(self):
@ -428,8 +453,8 @@ class Device(Thing):
# TODO @cayop uncomment this lines for link the possessor with the device
# from ereuse_devicehub.resources.action.models import Receive
# with suppress(LookupError):
# action = self.last_action_of(Receive)
# return action.agent_to
# action = self.last_action_of(Receive)
# return action.agent_to
@property
def working(self):
@ -478,15 +503,15 @@ class Device(Thing):
def which_user_put_this_device_in_trace(self):
"""which is the user than put this device in this trade"""
actions = copy.copy(self.actions)
actions.sort(key=lambda x: x.created)
actions.reverse()
last_ac = None
# search the automatic Confirm
for ac in actions:
if ac.type == 'Trade':
return last_ac.user
if ac.type == 'Confirm':
last_ac = ac
action_device = [x for x in ac.actions_device if x.device == self][0]
if action_device.author:
return action_device.author
return ac.author
def change_owner(self, new_user):
"""util for change the owner one device"""
@ -507,52 +532,8 @@ class Device(Thing):
"""
This method get a list of values for calculate a metrics from a spreadsheet
"""
actions = copy.copy(self.actions)
actions.sort(key=lambda x: x.created)
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
metrics = Metrics(device=self)
return metrics.get_metrics()
def __lt__(self, other):
return self.id < other.id
@ -681,7 +662,13 @@ class Computer(Device):
@property
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
def ram_size(self) -> int:
@ -1212,3 +1199,15 @@ class Manufacturer(db.Model):
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)

View File

@ -1,7 +1,7 @@
import datetime
from marshmallow import post_load, pre_load, fields as f
from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String, UUID
from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String, UUID, Dict
from marshmallow.validate import Length, OneOf, Range
from sqlalchemy.util import OrderedSet
from stdnum import imei, meid
@ -40,22 +40,24 @@ class Device(Thing):
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)
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__)
# 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)
problems = NestedOn('Action', many=True, dump_only=True, description=m.Device.problems.__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',
many=True,
dump_only=True,
description='The lots where this device is directly under.')
rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__)
price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__)
# trading = EnumField(states.Trading, dump_only=True, description=m.Device.trading.__doc__)
trading = SanitizedStr(dump_only=True, description='')
tradings = Dict(dump_only=True, description='')
physical = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__)
traking= EnumField(states.Traking, dump_only=True, description=m.Device.physical.__doc__)
traking = EnumField(states.Traking, dump_only=True, description=m.Device.physical.__doc__)
usage = EnumField(states.Usage, dump_only=True, description=m.Device.physical.__doc__)
revoke = UUID(dump_only=True)
revoke = UUID(dump_only=True)
physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor')
production_date = DateTime('iso',
description=m.Device.updated.comment,
@ -99,6 +101,7 @@ class Device(Thing):
class Computer(Device):
__doc__ = m.Computer.__doc__
# TODO TimeOut 1. Comment components if there are time out.
components = NestedOn('Component',
many=True,
dump_only=True,
@ -129,7 +132,7 @@ class Computer(Device):
description=m.Computer.privacy.__doc__)
amount = Integer(validate=f.validate.Range(min=0, max=100),
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')
transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment)
receiver_id = UUID(data_key='receiverID')

View File

@ -37,7 +37,6 @@ class Trading(State):
Trade = e.Trade
Confirm = e.Confirm
Revoke = e.Revoke
ConfirmRevoke = e.ConfirmRevoke
Cancelled = e.CancelTrade
Sold = e.Sell
Donated = e.Donate

View File

@ -414,6 +414,8 @@ def none2str(string):
return ''
return format(string)
def get_action(component, action):
""" Filter one action from a component or return None """
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
self['DHID'] = allocate['devicehubID']
self['Hid'] = allocate['hid']
self['Start'] = allocate['start']
self['FinalUserCode'] = allocate['finalUserCode']
self['NumEndUsers'] = allocate['numEndUsers']
self['Document-Name'] = allocate['document_name']
self['Action-Type'] = allocate['action_type']
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['Type'] = allocate['type']
self['LiveCreate'] = allocate['liveCreate']

View File

@ -3,11 +3,9 @@ import enum
import uuid
import time
import datetime
import pathlib
from collections import OrderedDict
from io import StringIO
from typing import Callable, Iterable, Tuple
from decouple import config
import boltons
import flask
@ -32,6 +30,8 @@ from ereuse_devicehub.resources.documents.device_row import (DeviceRow, StockRow
InternalStatsRow)
from ereuse_devicehub.resources.lot import LotView
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
@ -90,7 +90,6 @@ class DocumentView(DeviceView):
res = flask.make_response(template)
return res
@staticmethod
def erasure(query: db.Query):
def erasures():
@ -151,7 +150,7 @@ class DevicesDocumentView(DeviceView):
class ActionsDocumentView(DeviceView):
@cache(datetime.timedelta(minutes=1))
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)
def generate_post_csv(self, query):
@ -159,13 +158,26 @@ class ActionsDocumentView(DeviceView):
data = StringIO()
cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"')
first = True
devs_id = []
for device in query:
devs_id.append(device.id)
for allocate in device.get_metrics():
d = ActionRow(allocate)
if first:
cw.writerow(d.keys())
first = False
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')
output = make_response(bfile)
insert_hash(bfile)
@ -185,11 +197,11 @@ class LotsDocumentView(LotView):
cw = csv.writer(data)
first = True
for lot in query:
l = LotRow(lot)
_lot = LotRow(lot)
if first:
cw.writerow(l.keys())
cw.writerow(_lot.keys())
first = False
cw.writerow(l.values())
cw.writerow(_lot.values())
bfile = data.getvalue().encode('utf-8')
output = make_response(bfile)
insert_hash(bfile)
@ -275,7 +287,7 @@ class StampsView(View):
ok = '100% coincidence. The attached file contains data 100% existing in \
to our backend'
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',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.oasis.opendocument.spreadsheet',
@ -304,9 +316,9 @@ class InternalStatsView(DeviceView):
create = '{}-{}'.format(ac.created.year, ac.created.month)
user = ac.author.email
if not user in d:
d[user] = {}
if not create in d[user]:
if user not in d:
d[user] = {}
if create not in d[user]:
d[user][create] = []
d[user][create].append(ac)
@ -434,4 +446,3 @@ class DocumentDef(Resource):
auth=app.auth)
wbconf_view = app.auth.requires_auth(wbconf_view)
self.add_url_rule('/wbconf/<string:wbtype>', view_func=wbconf_view, methods=get)

View File

@ -5,14 +5,32 @@ from ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources.deliverynote import schemas as s_deliverynote
from ereuse_devicehub.resources.device import schemas as s_device
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.lot import models as m
from ereuse_devicehub.resources.models import STR_SIZE
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)
name = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True)
description = SanitizedStr(description=m.Lot.description.comment)
@ -29,4 +47,11 @@ class Lot(Thing):
receiver_address = SanitizedStr(validate=f.validate.Length(max=42))
deliverynote = NestedOn(s_deliverynote.Deliverynote, 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)

View File

@ -1,4 +1,5 @@
import uuid
from sqlalchemy.util import OrderedSet
from collections import deque
from enum import Enum
from typing import Dict, List, Set, Union
@ -13,7 +14,7 @@ from teal.resource import View
from ereuse_devicehub.db import db
from ereuse_devicehub.query import things_response
from ereuse_devicehub.resources.device.models import Device, Computer
from ereuse_devicehub.resources.action.models import Trade, Confirm, Revoke, ConfirmRevoke
from ereuse_devicehub.resources.action.models import Trade, Confirm, Revoke
from ereuse_devicehub.resources.lot.models import Lot, Path
@ -230,7 +231,7 @@ class LotDeviceView(LotBaseChildrenView):
return
devices = set(Device.query.filter(Device.id.in_(ids)).filter(
Device.owner==g.user))
Device.owner == g.user))
lot.devices.update(devices)
@ -246,7 +247,8 @@ class LotDeviceView(LotBaseChildrenView):
return
if lot.trade:
return delete_from_trade(lot, ids)
devices = Device.query.filter(Device.id.in_(ids)).all()
return delete_from_trade(lot, devices)
if not g.user == lot.owner:
txt = 'This is not your lot'
@ -258,49 +260,45 @@ class LotDeviceView(LotBaseChildrenView):
lot.devices.difference_update(devices)
def delete_from_trade(lot: Lot, ids: Set[int]):
users = [lot.trade.user_from.id, lot.trade.user_to.id]
if not g.user.id in users:
def delete_from_trade(lot: Lot, devices: List):
users = [lot.trade.user_from, lot.trade.user_to]
if g.user not in users:
# theoretically this case is impossible
txt = 'This is not your trade'
raise ma.ValidationError(txt)
devices = set(Device.query.filter(Device.id.in_(ids)).filter(
Device.owner_id.in_(users)))
# we need lock the action revoke for devices than travel for futures trades
for dev in devices:
if dev.owner not in users:
txt = 'This is not your device'
raise ma.ValidationError(txt)
# Now we need to know which devices we need extract of the lot
without_confirms = set() # set of devs without confirms of user2
drop_of_lot = []
without_confirms = []
for dev in devices:
if dev.trading(lot) in ['NeedConfirmation', 'Confirm', 'NeedConfirmRevoke']:
drop_of_lot.append(dev)
dev.reset_owner()
# if the trade need confirmation, then extract all devs than
# have only one confirmation and is from the same user than try to do
# now the revoke action
if lot.trade.confirm:
for dev in devices:
# if have only one confirmation
# then can be revoked and deleted of the lot
# Confirm of dev.trading mean that there are only one confirmation
# and the first user than put this device in trade is the actual g.user
if dev.trading == 'Confirm':
without_confirms.add(dev)
dev.reset_owner()
if not lot.trade.confirm:
drop_of_lot.append(dev)
without_confirms.append(dev)
dev.reset_owner()
# we need to mark one revoke for every devs
revoke = Revoke(action=lot.trade, user=g.user, devices=devices)
revoke = Revoke(action=lot.trade, user=g.user, devices=set(devices))
db.session.add(revoke)
if not lot.trade.confirm:
# if the trade is with phantom account
without_confirms = devices
if without_confirms:
confirm_revoke = ConfirmRevoke(
action=revoke,
user=g.user,
devices=without_confirms
phantom = lot.trade.user_to
if lot.trade.user_to == g.user:
phantom = lot.trade.user_from
phantom_revoke = Revoke(
action=lot.trade,
user=phantom,
devices=set(without_confirms)
)
db.session.add(confirm_revoke)
lot.devices.difference_update(without_confirms)
lot.trade.devices = lot.devices
db.session.add(phantom_revoke)
lot.devices.difference_update(OrderedSet(drop_of_lot))
return revoke

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -328,7 +328,7 @@ def test_outgoinlot_status_actions(action_model: models.Action, user: UserClient
assert device['actions'][-1]['id'] == action['id']
assert action['author']['id'] == user.user['id']
assert action['rol_user']['id'] == user.user['id']
assert action['rol_user']['id'] == user2.user['id']
@pytest.mark.mvp
@ -1396,6 +1396,7 @@ def test_confirm_revoke(user: UserClient, user2: UserClient):
user.post(res=models.Action, data=request_post)
trade = models.Trade.query.one()
device = trade.devices[0]
request_confirm = {
'type': 'Confirm',
@ -1416,9 +1417,10 @@ def test_confirm_revoke(user: UserClient, user2: UserClient):
# Normal revoke
user2.post(res=models.Action, data=request_revoke)
# You can not to do one confirmation next of one revoke
user2.post(res=models.Action, data=request_confirm, status=422)
assert len(trade.acceptances) == 3
# You can to do one confirmation next of one revoke
user2.post(res=models.Action, data=request_confirm)
assert len(trade.acceptances) == 4
assert device.trading(trade.lot) == "TradeConfirmed"
@pytest.mark.mvp
@ -1516,9 +1518,6 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient):
'type': 'Confirm',
'action': trade.id,
'devices': [
snap1['device']['id'],
snap2['device']['id'],
snap3['device']['id'],
snap4['device']['id'],
snap5['device']['id'],
snap6['device']['id'],
@ -1554,31 +1553,28 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient):
# the SCRAP confirms the revoke action
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device_10.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [
snap10['device']['id']
]
}
user2.post(res=models.Action, data=request_confirm_revoke)
assert device_10.actions[-1].t == 'ConfirmRevoke'
assert device_10.actions[-1].t == 'Revoke'
assert device_10.actions[-2].t == 'Revoke'
# assert len(trade.lot.devices) == len(trade.devices) == 9
# assert not device_10 in trade.devices
# check validation error
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device_10.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [
snap9['device']['id']
]
}
user2.post(res=models.Action, data=request_confirm_revoke, status=422)
# The manager add again device_10
# assert len(trade.devices) == 9
lot, _ = user.post({},
@ -1604,7 +1600,7 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient):
assert device_10.actions[-1].user == trade.user_from
assert device_10.actions[-2].t == 'Confirm'
assert device_10.actions[-2].user == trade.user_to
assert device_10.actions[-3].t == 'ConfirmRevoke'
assert device_10.actions[-3].t == 'Revoke'
# assert len(device_10.actions) == 13
@ -1772,31 +1768,23 @@ def test_trade_case1(user: UserClient, user2: UserClient):
user.post(res=models.Action, data=request_post)
trade = models.Trade.query.one()
lot, _ = user.post({},
res=Lot,
item='{}/devices'.format(lot['id']),
query=devices[-1:])
lot = trade.lot
device = trade.devices[0]
device1, device2 = trade.devices
assert device.actions[-2].t == 'Trade'
assert device.actions[-1].t == 'Confirm'
assert device.actions[-1].user == trade.user_to
assert device1.actions[-2].t == 'Trade'
assert device1.actions[-1].t == 'Confirm'
assert device1.actions[-1].user == trade.user_to
assert device2.actions[-2].t == 'Trade'
assert device2.actions[-1].t == 'Confirm'
assert device2.actions[-1].user == trade.user_to
user.delete({},
res=Lot,
item='{}/devices'.format(lot.id),
query=devices[:-1], status=200)
lot, _ = user.delete({},
res=Lot,
item='{}/devices'.format(lot['id']),
query=devices, status=200)
assert device1.actions[-2].t == 'Revoke'
assert device1.actions[-1].t == 'ConfirmRevoke'
assert device1.actions[-1].user == trade.user_to
assert device2.actions[-2].t == 'Revoke'
assert device2.actions[-1].t == 'ConfirmRevoke'
assert device2.actions[-1].user == trade.user_to
assert device not in trade.lot.devices
assert device.trading(trade.lot) == 'RevokeConfirmed'
assert device.actions[-2].t == 'Confirm'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_to
@pytest.mark.mvp
@ -1855,12 +1843,13 @@ def test_trade_case2(user: UserClient, user2: UserClient):
# Normal revoke
user.post(res=models.Action, data=request_revoke)
assert device1.actions[-2].t == 'Revoke'
assert device1.actions[-1].t == 'ConfirmRevoke'
assert device1.actions[-2].t == 'Confirm'
assert device1.actions[-1].t == 'Revoke'
assert device1.actions[-1].user == trade.user_to
assert device2.actions[-2].t == 'Revoke'
assert device2.actions[-1].t == 'ConfirmRevoke'
assert device2.actions[-2].t == 'Confirm'
assert device2.actions[-1].t == 'Revoke'
assert device2.actions[-1].user == trade.user_to
assert device1.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -1868,7 +1857,6 @@ def test_trade_case2(user: UserClient, user2: UserClient):
def test_trade_case3(user: UserClient, user2: UserClient):
# the pRp (manatest_usecase_confirmationger) creates a temporary lot
lot, _ = user.post({'name': 'MyLot'}, res=Lot)
# The manager add 7 device into the lot
snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot)
@ -1915,9 +1903,10 @@ def test_trade_case3(user: UserClient, user2: UserClient):
item='{}/devices'.format(lot['id']),
query=devices[-1:], status=200)
assert device2.actions[-2].t == 'Revoke'
assert device2.actions[-1].t == 'ConfirmRevoke'
assert device2.actions[-2].t == 'Confirm'
assert device2.actions[-1].t == 'Revoke'
assert device2.actions[-1].user == trade.user_from
assert device2.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -1979,9 +1968,10 @@ def test_trade_case4(user: UserClient, user2: UserClient):
assert device1.actions[-2].t == 'Trade'
assert device1.actions[-1].t == 'Confirm'
assert device1.actions[-1].user == trade.user_to
assert device2.actions[-2].t == 'Revoke'
assert device2.actions[-1].t == 'ConfirmRevoke'
assert device2.actions[-2].t == 'Confirm'
assert device2.actions[-1].t == 'Revoke'
assert device2.actions[-1].user == trade.user_from
assert device2.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -2036,8 +2026,8 @@ def test_trade_case5(user: UserClient, user2: UserClient):
assert device2.actions[-1].user == trade.user_from
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device2.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device2.id],
}
@ -2045,8 +2035,9 @@ def test_trade_case5(user: UserClient, user2: UserClient):
user.post(res=models.Action, data=request_confirm_revoke)
assert device2.actions[-2].t == 'Revoke'
assert device2.actions[-1].t == 'ConfirmRevoke'
assert device2.actions[-1].t == 'Revoke'
assert device2.actions[-1].user == trade.user_to
assert device2.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -2106,8 +2097,8 @@ def test_trade_case6(user: UserClient, user2: UserClient):
assert device2.actions[-1].user == trade.user_to
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device2.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device2.id],
}
@ -2115,8 +2106,9 @@ def test_trade_case6(user: UserClient, user2: UserClient):
user2.post(res=models.Action, data=request_confirm_revoke)
assert device2.actions[-2].t == 'Revoke'
assert device2.actions[-1].t == 'ConfirmRevoke'
assert device2.actions[-1].t == 'Revoke'
assert device2.actions[-1].user == trade.user_from
assert device2.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -2158,6 +2150,7 @@ def test_trade_case7(user: UserClient, user2: UserClient):
# Normal revoke
user2.post(res=models.Action, data=request_confirm)
assert device.trading(trade.lot) == 'TradeConfirmed'
lot, _ = user.delete({},
res=Lot,
@ -2165,14 +2158,14 @@ def test_trade_case7(user: UserClient, user2: UserClient):
query=devices, status=200)
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device.id],
}
user2.post(res=models.Action, data=request_confirm_revoke)
assert device.actions[-1].t == 'ConfirmRevoke'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_from
assert device.actions[-2].t == 'Revoke'
assert device.actions[-2].user == trade.user_to
@ -2182,6 +2175,7 @@ def test_trade_case7(user: UserClient, user2: UserClient):
assert device.actions[-4].user == trade.user_to
assert device.actions[-5].t == 'Trade'
assert device.actions[-5].author == trade.user_to
assert device.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -2223,6 +2217,7 @@ def test_trade_case8(user: UserClient, user2: UserClient):
# Normal revoke
user2.post(res=models.Action, data=request_confirm)
assert device.trading(trade.lot) == 'TradeConfirmed'
request_revoke = {
'type': 'Revoke',
@ -2234,14 +2229,14 @@ def test_trade_case8(user: UserClient, user2: UserClient):
user.post(res=models.Action, data=request_revoke)
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device.id],
}
user2.post(res=models.Action, data=request_confirm_revoke)
assert device.actions[-1].t == 'ConfirmRevoke'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_from
assert device.actions[-2].t == 'Revoke'
assert device.actions[-2].user == trade.user_to
@ -2251,6 +2246,7 @@ def test_trade_case8(user: UserClient, user2: UserClient):
assert device.actions[-4].user == trade.user_to
assert device.actions[-5].t == 'Trade'
assert device.actions[-5].author == trade.user_to
assert device.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -2303,6 +2299,7 @@ def test_trade_case9(user: UserClient, user2: UserClient):
# Normal revoke
user.post(res=models.Action, data=request_confirm)
assert device.trading(trade.lot) == 'TradeConfirmed'
assert device.owner == trade.user_to
@ -2312,8 +2309,8 @@ def test_trade_case9(user: UserClient, user2: UserClient):
query=devices[-1:], status=200)
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device.id],
}
@ -2321,7 +2318,7 @@ def test_trade_case9(user: UserClient, user2: UserClient):
assert device.owner == trade.user_from
assert device.actions[-1].t == 'ConfirmRevoke'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_to
assert device.actions[-2].t == 'Revoke'
assert device.actions[-2].user == trade.user_from
@ -2331,6 +2328,7 @@ def test_trade_case9(user: UserClient, user2: UserClient):
assert device.actions[-4].user == trade.user_from
assert device.actions[-5].t == 'Trade'
assert device.actions[-5].author == trade.user_to
assert device.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -2374,6 +2372,7 @@ def test_trade_case10(user: UserClient, user2: UserClient):
device1, device = trade.devices
assert device.owner == trade.user_from
# assert device.trading(trade.lot) == 'Confirm'
request_confirm = {
'type': 'Confirm',
@ -2383,6 +2382,7 @@ def test_trade_case10(user: UserClient, user2: UserClient):
# Normal confirm
user.post(res=models.Action, data=request_confirm)
# assert device.trading(trade.lot) == 'TradeConfirmed'
assert device.owner == trade.user_to
@ -2394,18 +2394,18 @@ def test_trade_case10(user: UserClient, user2: UserClient):
# Normal revoke
user2.post(res=models.Action, data=request_revoke)
assert device.trading(trade.lot) == 'Revoke'
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device.id],
}
user.post(res=models.Action, data=request_confirm_revoke)
assert device.owner == trade.user_from
assert device.actions[-1].t == 'ConfirmRevoke'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_to
assert device.actions[-2].t == 'Revoke'
assert device.actions[-2].user == trade.user_from
@ -2415,6 +2415,7 @@ def test_trade_case10(user: UserClient, user2: UserClient):
assert device.actions[-4].user == trade.user_from
assert device.actions[-5].t == 'Trade'
assert device.actions[-5].author == trade.user_to
assert device.trading(trade.lot) == 'RevokeConfirmed'
@pytest.mark.mvp
@ -2451,6 +2452,7 @@ def test_trade_case11(user: UserClient, user2: UserClient):
trade = models.Trade.query.one()
device1, device = trade.devices
assert device.trading(trade.lot) == 'Confirm'
request_confirm = {
'type': 'Confirm',
@ -2459,21 +2461,24 @@ def test_trade_case11(user: UserClient, user2: UserClient):
}
user2.post(res=models.Action, data=request_confirm)
assert device.trading(trade.lot) == 'TradeConfirmed'
lot, _ = user2.delete({},
res=Lot,
item='{}/devices'.format(lot['id']),
query=devices[-1:], status=200)
assert device.trading(trade.lot) == 'Revoke'
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device.id],
}
user.post(res=models.Action, data=request_confirm_revoke)
assert device.trading(trade.lot) == 'RevokeConfirmed'
assert device.actions[-1].t == 'ConfirmRevoke'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_to
assert device.actions[-2].t == 'Revoke'
assert device.actions[-2].user == trade.user_from
@ -2519,6 +2524,7 @@ def test_trade_case12(user: UserClient, user2: UserClient):
trade = models.Trade.query.one()
device1, device = trade.devices
assert device.trading(trade.lot) == 'Confirm'
# Normal confirm
request_confirm = {
@ -2528,6 +2534,7 @@ def test_trade_case12(user: UserClient, user2: UserClient):
}
user2.post(res=models.Action, data=request_confirm)
assert device.trading(trade.lot) == 'TradeConfirmed'
request_revoke = {
'type': 'Revoke',
@ -2537,16 +2544,18 @@ def test_trade_case12(user: UserClient, user2: UserClient):
# Normal revoke
user2.post(res=models.Action, data=request_revoke)
assert device.trading(trade.lot) == 'Revoke'
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device.id],
}
user.post(res=models.Action, data=request_confirm_revoke)
assert device.trading(trade.lot) == 'RevokeConfirmed'
assert device.actions[-1].t == 'ConfirmRevoke'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_to
assert device.actions[-2].t == 'Revoke'
assert device.actions[-2].user == trade.user_from
@ -2597,6 +2606,8 @@ def test_trade_case13(user: UserClient, user2: UserClient):
query=devices[-1:])
device1, device = trade.devices
assert device1.trading(trade.lot) == 'NeedConfirmation'
assert device.trading(trade.lot) == 'Confirm'
request_confirm = {
'type': 'Confirm',
@ -2605,21 +2616,26 @@ def test_trade_case13(user: UserClient, user2: UserClient):
}
user.post(res=models.Action, data=request_confirm)
assert device1.trading(trade.lot) == 'Confirm'
assert device.trading(trade.lot) == 'TradeConfirmed'
lot, _ = user.delete({},
res=Lot,
item='{}/devices'.format(lot['id']),
query=devices[-1:], status=200)
assert device1.trading(trade.lot) == 'Confirm'
assert device.trading(trade.lot) == 'Revoke'
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device.id],
}
user2.post(res=models.Action, data=request_confirm_revoke)
assert device.trading(trade.lot) == 'RevokeConfirmed'
assert device.actions[-1].t == 'ConfirmRevoke'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_from
assert device.actions[-2].t == 'Revoke'
assert device.actions[-2].user == trade.user_to
@ -2670,6 +2686,8 @@ def test_trade_case14(user: UserClient, user2: UserClient):
query=devices[-1:])
device1, device = trade.devices
assert device1.trading(trade.lot) == 'NeedConfirmation'
assert device.trading(trade.lot) == 'Confirm'
# Normal confirm
request_confirm = {
@ -2679,6 +2697,7 @@ def test_trade_case14(user: UserClient, user2: UserClient):
}
user.post(res=models.Action, data=request_confirm)
assert device.trading(trade.lot) == 'TradeConfirmed'
request_revoke = {
'type': 'Revoke',
@ -2688,16 +2707,18 @@ def test_trade_case14(user: UserClient, user2: UserClient):
# Normal revoke
user.post(res=models.Action, data=request_revoke)
assert device.trading(trade.lot) == 'Revoke'
request_confirm_revoke = {
'type': 'ConfirmRevoke',
'action': device.actions[-1].id,
'type': 'Revoke',
'action': trade.id,
'devices': [device.id],
}
user2.post(res=models.Action, data=request_confirm_revoke)
assert device.trading(trade.lot) == 'RevokeConfirmed'
assert device.actions[-1].t == 'ConfirmRevoke'
assert device.actions[-1].t == 'Revoke'
assert device.actions[-1].user == trade.user_from
assert device.actions[-2].t == 'Revoke'
assert device.actions[-2].user == trade.user_to

View File

@ -309,9 +309,10 @@ def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
# they are not the same tags though
# tag is a transient obj and db_tag the one from the db
# 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 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
@ -346,8 +347,9 @@ def test_sync_execute_register_tag_linked_same_device():
pc.tags.add(Tag(id='foo'))
db_pc = Sync().execute_register(pc)
assert db_pc.id == orig_pc.id
assert len(db_pc.tags) == 1
assert next(iter(db_pc.tags)).id == 'foo'
assert len(db_pc.tags) == 2
for tag in db_pc.tags:
assert tag.id in ['foo', db_pc.devicehub_id]
@pytest.mark.mvp
@ -399,6 +401,7 @@ def test_sync_execute_register_mismatch_between_tags_and_hid():
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_get_device(user: UserClient):
"""Checks GETting a d.Desktop with its components."""
g.user = User.query.one()
pc = d.Desktop(model='p1mo',
manufacturer='p1ma',
serial_number='p1s',
@ -437,6 +440,7 @@ def test_get_device(user: UserClient):
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_get_devices(app: Devicehub, user: UserClient):
"""Checks GETting multiple devices."""
g.user = User.query.one()
pc = d.Desktop(model='p1mo',
manufacturer='p1ma',
serial_number='p1s',
@ -502,7 +506,8 @@ def test_get_devices_permissions(app: Devicehub, user: UserClient, user2: UserCl
@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."""
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,
item='{}/devices'.format(my_lot['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'
@ -604,6 +610,7 @@ def test_device_public(user: UserClient, client: Client):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_computer_accessory_model(user: UserClient):
g.user = User.query.one()
sai = d.SAI(owner_id=user.user['id'])
db.session.add(sai)
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.usefixtures(conftest.app_context.__name__)
def test_networking_model(user: UserClient):
g.user = User.query.one()
router = d.Router(speed=1000, wireless=True, owner_id=user.user['id'])
db.session.add(router)
switch = d.Switch(speed=1000, wireless=False, owner_id=user.user['id'])

View File

@ -183,7 +183,7 @@ def test_device_query(user: UserClient):
pc = next(d for d in i['items'] if d['type'] == 'Desktop')
assert len(pc['actions']) == 4
assert len(pc['components']) == 3
assert not pc['tags']
assert pc['tags'][0]['id'] == pc['devicehubID']
@pytest.mark.mvp

View File

@ -251,13 +251,13 @@ def test_check_insert_hash(app: Devicehub, user: UserClient, client: Client):
assert ReportHash.query.filter_by(hash3=hash3).count() == 1
result, status = client.get(res=documents.DocumentDef.t, item='check/', query=[('hash', hash3)])
assert status.status_code == 200
assert result == True
assert result
ff = open('/tmp/test.csv', 'w')
ff.write(csv_str)
ff.close()
a= open('/tmp/test.csv').read()
a = open('/tmp/test.csv').read()
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)
with app.app_context():
# 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.tags.add(tag)
db.session.add(pc)
db.session.commit()

View File

@ -313,7 +313,6 @@ def test_post_get_lot(user: UserClient):
assert l['name'] == 'Foo'
l, _ = user.get(res=Lot, item=l['id'])
assert l['name'] == 'Foo'
assert not l['children']
def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
@ -355,45 +354,50 @@ def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
@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
removing it with DELETE.
"""
# todo check with components
with app.app_context():
device = Desktop(serial_number='foo',
model='bar',
manufacturer='foobar',
chassis=ComputerChassis.Lunchbox,
owner_id=user.user['id'])
db.session.add(device)
db.session.commit()
device_id = device.id
devicehub_id = device.devicehub_id
g.user = User.query.one()
device = Desktop(serial_number='foo',
model='bar',
manufacturer='foobar',
chassis=ComputerChassis.Lunchbox,
owner_id=user.user['id'])
db.session.add(device)
db.session.commit()
device_id = device.id
devicehub_id = device.devicehub_id
parent, _ = user.post(({'name': 'lot'}), res=Lot)
lot, _ = user.post({},
res=Lot,
item='{}/devices'.format(parent['id']),
query=[('id', device_id)])
assert lot['devices'][0]['id'] == device_id, 'Lot contains device'
device, _ = user.get(res=Device, item=devicehub_id)
assert len(device['lots']) == 1
assert device['lots'][0]['id'] == lot['id'], 'Device is inside lot'
lot = Lot.query.filter_by(id=lot['id']).one()
assert list(lot.devices)[0].id == device_id, 'Lot contains device'
device = Device.query.filter_by(devicehub_id=devicehub_id).one()
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
lot, _ = user.delete(res=Lot,
item='{}/devices'.format(parent['id']),
query=[('id', device_id)],
status=200)
assert not len(lot['devices'])
user.delete(res=Lot,
item='{}/devices'.format(parent['id']),
query=[('id', device_id)],
status=200)
assert not len(lot.devices)
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_lot_error_add_device_from_other_user(user: UserClient):
# TODO
"""Tests adding a device to a lot using POST and
removing it with DELETE.
"""
g.user = User.query.one()
user2 = User(email='baz@baz.cxm', password='baz')
user2.individuals.add(Person(name='Tommy'))
db.session.add(user2)
@ -413,8 +417,9 @@ def test_lot_error_add_device_from_other_user(user: UserClient):
res=Lot,
item='{}/devices'.format(parent['id']),
query=[('id', device_id)])
assert lot['devices'] == [], 'Lot contains device'
assert len(lot['devices']) == 0
lot = Lot.query.filter_by(id=lot['id']).one()
assert list(lot.devices) == [], 'Lot contains device'
assert len(lot.devices) == 0
@pytest.mark.mvp

View File

@ -2,6 +2,9 @@ import pytest
from ereuse_devicehub.client import UserClient
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.conftest import file, yaml2json, json_encode
@ -20,8 +23,7 @@ def test_simple_metrics(user: UserClient):
"finalUserCode": "abcdefjhi",
"devices": [device_id], "description": "aaa",
"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
user.post(res=ma.Allocate, data=post_request)
@ -65,8 +67,7 @@ def test_second_hdd_metrics(user: UserClient):
"finalUserCode": "abcdefjhi",
"devices": [device_id], "description": "aaa",
"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
user.post(res=ma.Allocate, data=post_request)
@ -109,8 +110,7 @@ def test_metrics_with_live_null(user: UserClient):
"finalUserCode": "abcdefjhi",
"devices": [device_id], "description": "aaa",
"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
user.post(res=ma.Allocate, data=post_request)
@ -120,3 +120,240 @@ def test_metrics_with_live_null(user: UserClient):
res, _ = user.get("/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

View File

@ -37,7 +37,6 @@ from tests import conftest
@pytest.mark.mvp
@pytest.mark.usefixtures('auth_app_context')
# cayop
def test_snapshot_model():
"""Tests creating a Snapshot with its relationships ensuring correct
DB mapping.
@ -318,7 +317,7 @@ def test_snapshot_tag_inner_tag(user: UserClient, tag_id: str, app: Devicehub):
snapshot_and_check(user, b,
action_types=(RateComputer.t, BenchmarkProcessor.t, VisualTest.t))
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'
@ -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):
pc1 = file('basic.snapshot')
user.post(pc1, res=Snapshot)
user.post(pc1, res=Snapshot, status=UniqueViolation)
user.post(pc1, res=Snapshot, status=400)
@pytest.mark.mvp

View File

@ -2,6 +2,7 @@ import pathlib
import pytest
import requests_mock
from flask import g
from boltons.urlutils import URL
from ereuse_utils.session import DevicehubClient
from pytest import raises
@ -11,6 +12,7 @@ from teal.marshmallow import ValidationError
from ereuse_devicehub.client import UserClient, Client
from ereuse_devicehub.db import db
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.agent.models import Organization
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, \
TagNotLinked
from tests import conftest
from tests.conftest import file, yaml2json, json_encode
from tests.conftest import yaml2json, json_encode
@pytest.mark.mvp
@ -41,6 +43,7 @@ def test_create_tag(user: UserClient):
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_create_tag_with_device(user: UserClient):
"""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'])
db.session.add(pc)
db.session.commit()
@ -60,13 +63,14 @@ def test_create_tag_with_device(user: UserClient):
def test_delete_tags(user: UserClient, client: Client):
"""Delete a named tag."""
# Delete Tag Named
g.user = User.query.one()
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
db.session.add(pc)
db.session.commit()
tag = Tag(id='bar', owner_id=user.user['id'], device_id=pc.id)
db.session.add(tag)
db.session.commit()
tag = Tag.query.one()
tag = Tag.query.all()[-1]
assert tag.id == 'bar'
# Is not possible delete one tag linked to one device
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'])
db.session.add(tag)
db.session.commit()
tag = Tag.query.one()
tag = Tag.query.all()[-1]
assert tag.id == 'bar-1'
res, _ = user.delete(res=Tag, item=tag.id, status=422)
msg = 'This tag {} is unnamed tag. It is imposible delete.'.format(tag.id)
assert msg in res['message']
tag = Tag.query.one()
tag = Tag.query.all()[-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"""
with app.app_context():
# Create a pc with a tag
g.user = User.query.one()
tag = Tag(id='foo-bar', owner_id=user.user['id'])
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
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
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.commit()
@ -276,6 +282,7 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
Checks search has the term.
"""
with app.app_context():
g.user = User.query.one()
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'])
db.session.add(desktop)
@ -284,7 +291,7 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
devicehub_id = desktop.devicehub_id
user.put({}, res=Tag, item='foo-bar/device/{}'.format(desktop_id), status=204)
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
# 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'}]
snapshot, _ = user.post(json_encode(s), res=Snapshot)
device, _ = user.get(res=Device, item=snapshot['device']['devicehubID'])
assert device['tags'][0]['id'] == 'foo'
assert device['tags'][0]['secondary'] == 'bar'
assert 'foo' in [x['id'] for x in device['tags']]
assert 'bar' in [x.get('secondary') for x in device['tags']]
r, _ = user.get(res=Device, query=[('search', 'foo'), ('filter', {'type': ['Computer']})])
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."""
with app.app_context():
# Create a pc with a tag
g.user = User.query.all()[0]
tag = Tag(id='foo-bar', owner_id=user.user['id'])
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
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)
assert res.status_code == 200
assert res2.status_code == 200
assert len(computer['items']) == 1
assert len(computer['items']) == 2
assert len(computer2['items']) == 0

View File

@ -66,8 +66,8 @@ def test_workbench_server_condensed(user: UserClient):
assert device['rate']['rating'] == 1
assert device['rate']['type'] == RateComputer.t
# TODO JN why haven't same order in actions on each execution?
assert device['actions'][2]['type'] == BenchmarkProcessor.t or device['actions'][2]['type'] == BenchmarkRamSysbench.t
assert device['tags'][0]['id'] == 'tag1'
assert any([ac['type'] in [BenchmarkProcessor.t, BenchmarkRamSysbench.t] for ac in device['actions']])
assert 'tag1' in [x['id'] for x in device['tags']]
@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['manufacturer'] == 'asustek computer inc.'
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['rate']
rate = pc['rate']