Merge pull request #316 from eReuse/feature/3598-binding

Feature/3598 binding
This commit is contained in:
cayop 2022-08-03 16:25:00 +02:00 committed by GitHub
commit ae847b8ee2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1916 additions and 387 deletions

View File

@ -1,10 +1,10 @@
repos: repos:
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.1.0 rev: 22.6.0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: 5.9.3 rev: 5.10.1
hooks: hooks:
- id: isort - id: isort
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8

View File

@ -7,6 +7,7 @@ ml).
## testing ## testing
- [added] #312 Placeholder: new, edit, update. (manually and with excel). - [added] #312 Placeholder: new, edit, update. (manually and with excel).
- [added] #316 Placeholder: binding/unbinding. (manually).
- [fixed] #313 Bump numpy from 1.21.6 to 1.22.0. - [fixed] #313 Bump numpy from 1.21.6 to 1.22.0.
- [fixed] #314 bugs create placeholder from lot. - [fixed] #314 bugs create placeholder from lot.
- [fixed] #317 bugs about exports placeholders. - [fixed] #317 bugs about exports placeholders.

View File

@ -199,10 +199,10 @@ class Dummy:
inventory, _ = user1.get(res=Device) inventory, _ = user1.get(res=Device)
assert len(inventory['items']) assert len(inventory['items'])
i, _ = user1.get(res=Device, query=[('search', 'intel')]) # i, _ = user1.get(res=Device, query=[('search', 'intel')])
assert 12 == len(i['items']) # assert len(i['items']) in [14, 12]
i, _ = user1.get(res=Device, query=[('search', 'pc')]) # i, _ = user1.get(res=Device, query=[('search', 'pc')])
assert 14 == len(i['items']) # assert len(i['items']) in [17, 14]
# Let's create a set of actions for the pc device # Let's create a set of actions for the pc device
# Make device Ready # Make device Ready

View File

@ -136,9 +136,13 @@ class FilterForm(FlaskForm):
if self.lot_id: if self.lot_id:
self.lot = self.lots.filter(Lot.id == self.lot_id).one() self.lot = self.lots.filter(Lot.id == self.lot_id).one()
device_ids = (d.id for d in self.lot.devices) device_ids = (d.id for d in self.lot.devices)
self.devices = Device.query.filter(Device.id.in_(device_ids)) self.devices = Device.query.filter(Device.id.in_(device_ids)).filter(
Device.binding == None
)
else: else:
self.devices = Device.query.filter(Device.owner_id == g.user.id) self.devices = Device.query.filter(Device.owner_id == g.user.id).filter(
Device.binding == None
)
if self.only_unassigned: if self.only_unassigned:
self.devices = self.devices.filter_by(lots=None) self.devices = self.devices.filter_by(lots=None)
@ -451,7 +455,7 @@ class NewDeviceForm(FlaskForm):
if self.phid.data and self.amount.data == 1 and not self._obj: if self.phid.data and self.amount.data == 1 and not self._obj:
dev = Placeholder.query.filter( dev = Placeholder.query.filter(
Placeholder.phid == self.phid.data, Device.owner == g.user Placeholder.phid == self.phid.data, Placeholder.owner == g.user
).first() ).first()
if dev: if dev:
msg = "Sorry, exist one snapshot device with this HID" msg = "Sorry, exist one snapshot device with this HID"
@ -564,6 +568,7 @@ class NewDeviceForm(FlaskForm):
'id_device_supplier': self.id_device_supplier.data, 'id_device_supplier': self.id_device_supplier.data,
'info': self.info.data, 'info': self.info.data,
'pallet': self.pallet.data, 'pallet': self.pallet.data,
'is_abstract': False,
} }
) )
return self.placeholder return self.placeholder
@ -573,6 +578,7 @@ class NewDeviceForm(FlaskForm):
self._obj.placeholder.id_device_supplier = self.id_device_supplier.data or None self._obj.placeholder.id_device_supplier = self.id_device_supplier.data or None
self._obj.placeholder.info = self.info.data or None self._obj.placeholder.info = self.info.data or None
self._obj.placeholder.pallet = self.pallet.data or None self._obj.placeholder.pallet = self.pallet.data or None
self._obj.placeholder.is_abstract = False
self._obj.model = self.model.data self._obj.model = self.model.data
self._obj.manufacturer = self.manufacturer.data self._obj.manufacturer = self.manufacturer.data
self._obj.serial_number = self.serial_number.data self._obj.serial_number = self.serial_number.data
@ -1551,6 +1557,7 @@ class UploadPlaceholderForm(FlaskForm):
'id_device_supplier': data['Id device Supplier'][i], 'id_device_supplier': data['Id device Supplier'][i],
'pallet': data['Pallet'][i], 'pallet': data['Pallet'][i],
'info': data['Info'][i], 'info': data['Info'][i],
'is_abstract': False,
} }
snapshot_json = schema.load(json_snapshot) snapshot_json = schema.load(json_snapshot)
@ -1602,3 +1609,43 @@ class EditPlaceholderForm(FlaskForm):
db.session.commit() db.session.commit()
return self.placeholders return self.placeholders
class BindingForm(FlaskForm):
phid = StringField('Phid', [validators.DataRequired()])
def __init__(self, *args, **kwargs):
self.device = kwargs.pop('device', None)
self.placeholder = kwargs.pop('placeholder', None)
super().__init__(*args, **kwargs)
def validate(self, extra_validators=None):
is_valid = super().validate(extra_validators)
if not is_valid:
txt = "This placeholder not exist."
self.phid.errors = [txt]
return False
if self.device.placeholder:
txt = "This is not a device Workbench."
self.phid.errors = [txt]
return False
if not self.placeholder:
self.placeholder = Placeholder.query.filter(
Placeholder.phid == self.phid.data, Placeholder.owner == g.user
).first()
if not self.placeholder:
txt = "This placeholder not exist."
self.phid.errors = [txt]
return False
if self.placeholder.binding:
txt = "This placeholder have a binding with other device. "
txt += "Before you need to do an unbinding with this other device."
self.phid.errors = [txt]
return False
return True

View File

@ -1,3 +1,4 @@
import copy
import csv import csv
import logging import logging
import os import os
@ -19,6 +20,7 @@ from ereuse_devicehub.db import db
from ereuse_devicehub.inventory.forms import ( from ereuse_devicehub.inventory.forms import (
AdvancedSearchForm, AdvancedSearchForm,
AllocateForm, AllocateForm,
BindingForm,
DataWipeForm, DataWipeForm,
EditTransferForm, EditTransferForm,
FilterForm, FilterForm,
@ -36,7 +38,12 @@ from ereuse_devicehub.inventory.forms import (
from ereuse_devicehub.labels.forms import PrintLabelsForm from ereuse_devicehub.labels.forms import PrintLabelsForm
from ereuse_devicehub.parser.models import PlaceholdersLog, SnapshotsLog from ereuse_devicehub.parser.models import PlaceholdersLog, SnapshotsLog
from ereuse_devicehub.resources.action.models import Trade from ereuse_devicehub.resources.action.models import Trade
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Device from ereuse_devicehub.resources.device.models import (
Computer,
DataStorage,
Device,
Placeholder,
)
from ereuse_devicehub.resources.documents.device_row import ActionRow, DeviceRow from ereuse_devicehub.resources.documents.device_row import ActionRow, DeviceRow
from ereuse_devicehub.resources.enums import SnapshotSoftware from ereuse_devicehub.resources.enums import SnapshotSoftware
from ereuse_devicehub.resources.hash_reports import insert_hash from ereuse_devicehub.resources.hash_reports import insert_hash
@ -129,6 +136,7 @@ class AdvancedSearchView(DeviceListMixin):
class DeviceDetailView(GenericMixin): class DeviceDetailView(GenericMixin):
methods = ['GET', 'POST']
decorators = [login_required] decorators = [login_required]
template_name = 'inventory/device_detail.html' template_name = 'inventory/device_detail.html'
@ -140,15 +148,147 @@ class DeviceDetailView(GenericMixin):
.one() .one()
) )
form_binding = BindingForm(device=device)
self.context.update( self.context.update(
{ {
'device': device, 'device': device,
'placeholder': device.binding or device.placeholder,
'page_title': 'Device {}'.format(device.devicehub_id), 'page_title': 'Device {}'.format(device.devicehub_id),
'form_binding': form_binding,
'active_binding': False,
} }
) )
if form_binding.validate_on_submit():
next_url = url_for(
'inventory.binding',
dhid=form_binding.device.devicehub_id,
phid=form_binding.placeholder.phid,
)
return flask.redirect(next_url)
elif form_binding.phid.data:
self.context['active_binding'] = True
return flask.render_template(self.template_name, **self.context) return flask.render_template(self.template_name, **self.context)
class BindingView(GenericMixin):
methods = ['GET', 'POST']
decorators = [login_required]
template_name = 'inventory/binding.html'
def dispatch_request(self, dhid, phid):
self.get_context()
device = (
Device.query.filter(Device.owner_id == g.user.id)
.filter(Device.devicehub_id == dhid)
.one()
)
placeholder = (
Placeholder.query.filter(Placeholder.owner_id == g.user.id)
.filter(Placeholder.phid == phid)
.one()
)
if request.method == 'POST':
old_placeholder = device.binding
old_device_placeholder = old_placeholder.device
if old_placeholder.is_abstract:
for plog in PlaceholdersLog.query.filter_by(
placeholder_id=old_placeholder.id
):
db.session.delete(plog)
db.session.delete(old_device_placeholder)
device.binding = placeholder
db.session.commit()
next_url = url_for('inventory.device_details', id=dhid)
messages.success(
'Device "{}" bind successfully with {}!'.format(dhid, phid)
)
return flask.redirect(next_url)
self.context.update(
{
'device': device.binding.device,
'placeholder': placeholder,
'page_title': 'Binding confirm',
}
)
return flask.render_template(self.template_name, **self.context)
class UnBindingView(GenericMixin):
methods = ['GET', 'POST']
decorators = [login_required]
template_name = 'inventory/unbinding.html'
def dispatch_request(self, phid):
placeholder = (
Placeholder.query.filter(Placeholder.owner_id == g.user.id)
.filter(Placeholder.phid == phid)
.one()
)
if not placeholder.binding:
next_url = url_for(
'inventory.device_details', id=placeholder.device.devicehub_id
)
return flask.redirect(next_url)
device = placeholder.binding
self.get_context()
if request.method == 'POST':
self.clone_device(device)
next_url = url_for(
'inventory.device_details', id=placeholder.device.devicehub_id
)
messages.success('Device "{}" unbind successfully!'.format(phid))
return flask.redirect(next_url)
self.context.update(
{
'device': device,
'placeholder': placeholder,
'page_title': 'Unbinding confirm',
}
)
return flask.render_template(self.template_name, **self.context)
def clone_device(self, device):
if device.binding.is_abstract:
return
dict_device = copy.copy(device.__dict__)
dict_device.pop('_sa_instance_state')
dict_device.pop('id', None)
dict_device.pop('devicehub_id', None)
dict_device.pop('actions_multiple', None)
dict_device.pop('actions_one', None)
dict_device.pop('components', None)
dict_device.pop('tags', None)
dict_device.pop('system_uuid', None)
dict_device.pop('binding', None)
dict_device.pop('placeholder', None)
new_device = device.__class__(**dict_device)
db.session.add(new_device)
if hasattr(device, 'components'):
for c in device.components:
if c.binding:
c.binding.device.parent = new_device
placeholder = Placeholder(device=new_device, binding=device, is_abstract=True)
db.session.add(placeholder)
db.session.commit()
return new_device
class LotCreateView(GenericMixin): class LotCreateView(GenericMixin):
methods = ['GET', 'POST'] methods = ['GET', 'POST']
decorators = [login_required] decorators = [login_required]
@ -993,3 +1133,9 @@ devices.add_url_rule(
devices.add_url_rule( devices.add_url_rule(
'/placeholder-logs/', view_func=PlaceholderLogListView.as_view('placeholder_logs') '/placeholder-logs/', view_func=PlaceholderLogListView.as_view('placeholder_logs')
) )
devices.add_url_rule(
'/binding/<string:dhid>/<string:phid>/', view_func=BindingView.as_view('binding')
)
devices.add_url_rule(
'/unbinding/<string:phid>/', view_func=UnBindingView.as_view('unbinding')
)

View File

@ -0,0 +1,71 @@
"""add owner to placeholder
Revision ID: d7ea9a3b2da1
Revises: 2b90b41a556a
Create Date: 2022-07-27 14:40:15.513820
"""
import sqlalchemy as sa
from alembic import context, op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '2b90b41a556a'
down_revision = '3e3a67f62972'
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()
sql = f"select {get_inv()}.placeholder.id, {get_inv()}.device.owner_id from {get_inv()}.placeholder"
sql += f" join {get_inv()}.device on {get_inv()}.device.id={get_inv()}.placeholder.device_id;"
for c in con.execute(sql):
id_placeholder = c.id
id_owner = c.owner_id
sql_update = f"update {get_inv()}.placeholder set owner_id='{id_owner}', is_abstract=False where id={id_placeholder};"
con.execute(sql_update)
def upgrade():
op.add_column(
'placeholder',
sa.Column('is_abstract', sa.Boolean(), nullable=True),
schema=f'{get_inv()}',
)
op.add_column(
'placeholder',
sa.Column('owner_id', postgresql.UUID(), nullable=True),
schema=f'{get_inv()}',
)
op.create_foreign_key(
"fk_placeholder_owner_id_user_id",
"placeholder",
"user",
["owner_id"],
["id"],
ondelete="SET NULL",
source_schema=f'{get_inv()}',
referent_schema='common',
)
upgrade_data()
def downgrade():
op.drop_constraint(
"fk_placeholder_owner_id_user_id",
"placeholder",
type_="foreignkey",
schema=f'{get_inv()}',
)
op.drop_column('placeholder', 'owner_id', schema=f'{get_inv()}')
op.drop_column('placeholder', 'is_abstract', schema=f'{get_inv()}')

View File

@ -0,0 +1,240 @@
"""Create placeholders
Revision ID: 2b90b41a556a
Revises: 3e3a67f62972
Create Date: 2022-07-19 12:17:16.690865
"""
import copy
from alembic import context, op
from ereuse_devicehub.config import DevicehubConfig
from ereuse_devicehub.db import db
from ereuse_devicehub.devicehub import Devicehub
from ereuse_devicehub.inventory.models import Transfer
from ereuse_devicehub.parser.models import PlaceholdersLog
from ereuse_devicehub.resources.action.models import (
ActionDevice,
Allocate,
DataWipe,
Deallocate,
Management,
Prepare,
Ready,
Recycling,
Refurbish,
ToPrepare,
ToRepair,
Use,
)
from ereuse_devicehub.resources.device.models import Computer, Device, Placeholder
from ereuse_devicehub.resources.lot.models import LotDevice
# revision identifiers, used by Alembic.
revision = 'd7ea9a3b2da1'
down_revision = '2b90b41a556a'
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 init_app():
app = Devicehub(inventory=f'{get_inv()}')
app.app_context().push()
def clone_computers():
for computer in Computer.query.all():
clone_device(computer)
def clone_device(device):
if device.binding:
return
dict_device = copy.copy(device.__dict__)
dict_device.pop('_sa_instance_state')
dict_device.pop('id', None)
dict_device.pop('devicehub_id', None)
dict_device.pop('actions_multiple', None)
dict_device.pop('actions_one', None)
dict_device.pop('components', None)
dict_device.pop('tags', None)
dict_device.pop('system_uuid', None)
new_device = device.__class__(**dict_device)
db.session.add(new_device)
if hasattr(device, 'components'):
for c in device.components:
new_c = clone_device(c)
new_c.parent = new_device
placeholder = Placeholder(device=new_device, binding=device, is_abstract=True, owner_id=device.owner_id)
db.session.add(placeholder)
tags = [x for x in device.tags]
for tag in tags:
tag.device = new_device
lots = [x for x in device.lots]
for lot in lots:
for rel_lot in LotDevice.query.filter_by(lot_id=lot.id, device=device):
rel_lot.device = new_device
return new_device
def manual_actions():
MANUAL_ACTIONS = (
Recycling,
Use,
Refurbish,
Management,
Allocate,
Deallocate,
ToPrepare,
Prepare,
DataWipe,
ToRepair,
Ready,
Transfer,
)
for action in MANUAL_ACTIONS:
change_device(action)
def change_device(action):
for ac in action.query.all():
if hasattr(ac, 'device'):
if not ac.device.binding:
continue
ac.device = ac.device.binding.device
if hasattr(ac, 'devices'):
for act in ActionDevice.query.filter_by(action_id=ac.id):
if not act.device.binding:
continue
act.device = act.device.binding.device
def change_lot():
for placeholder in Placeholder.query.all():
device = placeholder.device
binding = placeholder.binding
if not device or not binding:
continue
lots = [x for x in device.lots]
for lot in lots:
for rel_lot in LotDevice.query.filter_by(
lot_id=lot.id, device_id=device.id
):
if binding:
rel_lot.device_id = binding.id
db.session.commit()
def change_tags():
for placeholder in Placeholder.query.all():
device = placeholder.device
binding = placeholder.binding
if not device or not binding:
continue
tags = [x for x in device.tags]
for tag in tags:
tag.device = binding
db.session.commit()
def remove_manual_actions():
MANUAL_ACTIONS = (
Recycling,
Use,
Refurbish,
Management,
Allocate,
Deallocate,
ToPrepare,
Prepare,
DataWipe,
ToRepair,
Ready,
Transfer,
)
for action in MANUAL_ACTIONS:
remove_change_device(action)
def remove_change_device(action):
for ac in action.query.all():
if hasattr(ac, 'device'):
if not ac.device.placeholder:
continue
if not ac.device.placeholder.binding:
continue
ac.device = ac.device.placeholder.binding
if hasattr(ac, 'devices'):
for act in ActionDevice.query.filter_by(action_id=ac.id):
if not act.device.placeholder:
continue
if not act.device.placeholder.binding:
continue
act.device = act.device.placeholder.binding
db.session.commit()
def remove_placeholders():
devices = []
for placeholder in Placeholder.query.all():
device = placeholder.device
binding = placeholder.binding
if not device or not binding:
continue
devices.append(placeholder.device.id)
for dev in Device.query.filter(Device.id.in_(devices)):
db.session.delete(dev)
for placeholder in Placeholder.query.all():
device = placeholder.device
binding = placeholder.binding
if not device or not binding:
continue
for plog in PlaceholdersLog.query.filter_by(placeholder=placeholder).all():
db.session.delete(plog)
db.session.delete(placeholder)
db.session.commit()
def upgrade():
con = op.get_bind()
devices = con.execute(f'select * from {get_inv()}.device')
if not list(devices):
return
init_app()
clone_computers()
manual_actions()
db.session.commit()
def downgrade():
con = op.get_bind()
devices = con.execute(f'select * from {get_inv()}.device')
if not list(devices):
return
init_app()
remove_manual_actions()
change_lot()
change_tags()
remove_placeholders()

View File

@ -76,7 +76,10 @@ class Action(Thing):
if 'end_time' in data and data['end_time'].replace(tzinfo=tzutc()) < unix_time: if 'end_time' in data and data['end_time'].replace(tzinfo=tzutc()) < unix_time:
data['end_time'] = unix_time data['end_time'] = unix_time
if 'start_time' in data and data['start_time'].replace(tzinfo=tzutc()) < unix_time: if (
'start_time' in data
and data['start_time'].replace(tzinfo=tzutc()) < unix_time
):
data['start_time'] = unix_time data['start_time'] = unix_time
if data.get('end_time') and data.get('start_time'): if data.get('end_time') and data.get('start_time'):
@ -930,6 +933,10 @@ class Delete(ActionWithMultipleDevicesCheckingOwner):
for dev in data['devices']: for dev in data['devices']:
if dev.last_action_trading is None: if dev.last_action_trading is None:
dev.active = False dev.active = False
if dev.binding:
dev.binding.device.active = False
if dev.placeholder:
dev.placeholder.device.active = False
class Migrate(ActionWithMultipleDevices): class Migrate(ActionWithMultipleDevices):

View File

@ -215,6 +215,24 @@ class Device(Thing):
def reverse_actions(self) -> list: def reverse_actions(self) -> list:
return reversed(self.actions) return reversed(self.actions)
@property
def manual_actions(self) -> list:
mactions = [
'ActionDevice',
'Allocate',
'DataWipe',
'Deallocate',
'Management',
'Prepare',
'Ready',
'Recycling',
'Refurbish',
'ToPrepare',
'ToRepair',
'Use',
]
return [a for a in self.actions if a in mactions]
@property @property
def actions(self) -> list: def actions(self) -> list:
"""All the actions where the device participated, including: """All the actions where the device participated, including:
@ -411,7 +429,16 @@ class Device(Thing):
@property @property
def sid(self): def sid(self):
actions = []
if self.placeholder and self.placeholder.binding:
actions = [
x
for x in self.placeholder.binding.actions
if x.t == 'Snapshot' and x.sid
]
else:
actions = [x for x in self.actions if x.t == 'Snapshot' and x.sid] actions = [x for x in self.actions if x.t == 'Snapshot' and x.sid]
if actions: if actions:
return actions[0].sid return actions[0].sid
@ -601,6 +628,16 @@ class Device(Thing):
args[POLYMORPHIC_ON] = cls.type args[POLYMORPHIC_ON] = cls.type
return args return args
def phid(self):
if self.placeholder:
return self.placeholder.phid
if self.binding:
return self.binding.phid
return ''
def list_tags(self):
return ', '.join([t.id for t in self.tags])
def appearance(self): def appearance(self):
actions = copy.copy(self.actions) actions = copy.copy(self.actions)
actions.sort(key=lambda x: x.created) actions.sort(key=lambda x: x.created)
@ -833,6 +870,7 @@ class Placeholder(Thing):
pallet.comment = "used for identification where from where is this placeholders" pallet.comment = "used for identification where from where is this placeholders"
info = db.Column(CIText()) info = db.Column(CIText())
info.comment = "more info of placeholders" info.comment = "more info of placeholders"
is_abstract = db.Column(Boolean, default=False)
id_device_supplier = db.Column(CIText()) id_device_supplier = db.Column(CIText())
id_device_supplier.comment = ( id_device_supplier.comment = (
"Identification used for one supplier of one placeholders" "Identification used for one supplier of one placeholders"
@ -845,7 +883,7 @@ class Placeholder(Thing):
) )
device = db.relationship( device = db.relationship(
Device, Device,
backref=backref('placeholder', lazy=True, uselist=False), backref=backref('placeholder', lazy=True, cascade="all, delete-orphan", uselist=False),
primaryjoin=device_id == Device.id, primaryjoin=device_id == Device.id,
) )
device_id.comment = "datas of the placeholder" device_id.comment = "datas of the placeholder"
@ -861,6 +899,13 @@ class Placeholder(Thing):
primaryjoin=binding_id == Device.id, primaryjoin=binding_id == Device.id,
) )
binding_id.comment = "binding placeholder with workbench device" binding_id.comment = "binding placeholder with workbench device"
owner_id = db.Column(
UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=False,
default=lambda: g.user.id,
)
owner = db.relationship(User, primaryjoin=owner_id == User.id)
class Computer(Device): class Computer(Device):
@ -1481,9 +1526,9 @@ def create_code_tag(mapper, connection, device):
""" """
from ereuse_devicehub.resources.tag.model import Tag from ereuse_devicehub.resources.tag.model import Tag
if isinstance(device, Computer): if isinstance(device, Computer) and not device.placeholder:
tag = Tag(device_id=device.id, id=device.devicehub_id) tag = Tag(device_id=device.id, id=device.devicehub_id)
db.session.add(tag) db.session.add(tag)
event.listen(Device, 'after_insert', create_code_tag, propagate=True) # event.listen(Device, 'after_insert', create_code_tag, propagate=True)

View File

@ -1,3 +1,4 @@
import copy
import difflib import difflib
from contextlib import suppress from contextlib import suppress
from itertools import groupby from itertools import groupby
@ -87,6 +88,7 @@ class Sync:
# We only want to perform Add/Remove to not new components # We only want to perform Add/Remove to not new components
actions = self.add_remove(db_device, not_new_components) actions = self.add_remove(db_device, not_new_components)
db_device.components = db_components db_device.components = db_components
self.create_placeholder(db_device)
return db_device, actions return db_device, actions
def execute_register_component( def execute_register_component(
@ -113,6 +115,7 @@ class Sync:
- A flag stating if the device is new or it already - A flag stating if the device is new or it already
existed in the DB. existed in the DB.
""" """
# if device.serial_number == 'b8oaas048286':
assert inspect(component).transient, 'Component should not be synced from DB' assert inspect(component).transient, 'Component should not be synced from DB'
# if not is a DataStorage, then need build a new one # if not is a DataStorage, then need build a new one
if component.t in DEVICES_ALLOW_DUPLICITY: if component.t in DEVICES_ALLOW_DUPLICITY:
@ -124,7 +127,7 @@ class Sync:
try: try:
if component.hid: if component.hid:
db_component = Device.query.filter_by( db_component = Device.query.filter_by(
hid=component.hid, owner_id=g.user.id hid=component.hid, owner_id=g.user.id, placeholder=None
).one() ).one()
assert isinstance( assert isinstance(
db_component, Device db_component, Device
@ -183,18 +186,24 @@ class Sync:
if device.system_uuid: if device.system_uuid:
with suppress(ResourceNotFound): with suppress(ResourceNotFound):
db_device = Computer.query.filter_by( db_device = Computer.query.filter_by(
system_uuid=device.system_uuid, owner_id=g.user.id, active=True system_uuid=device.system_uuid,
owner_id=g.user.id,
active=True,
placeholder=None,
).one() ).one()
# if no there are any Computer by uuid search by hid # if no there are any Computer by uuid search by hid
if not db_device and device.hid: if not db_device and device.hid:
with suppress(ResourceNotFound): with suppress(ResourceNotFound):
db_device = Device.query.filter_by( db_device = Device.query.filter_by(
hid=device.hid, owner_id=g.user.id, active=True hid=device.hid,
owner_id=g.user.id,
active=True,
placeholder=None,
).one() ).one()
elif device.hid: elif device.hid:
with suppress(ResourceNotFound): with suppress(ResourceNotFound):
db_device = Device.query.filter_by( db_device = Device.query.filter_by(
hid=device.hid, owner_id=g.user.id, active=True hid=device.hid, owner_id=g.user.id, active=True, placeholder=None
).one() ).one()
if db_device and db_device.allocated: if db_device and db_device.allocated:
@ -278,22 +287,40 @@ class Sync:
if hasattr(device, 'system_uuid') and device.system_uuid: if hasattr(device, 'system_uuid') and device.system_uuid:
db_device.system_uuid = device.system_uuid db_device.system_uuid = device.system_uuid
if device.placeholder and db_device.placeholder: @staticmethod
db_device.placeholder.pallet = device.placeholder.pallet def create_placeholder(device: Device):
db_device.placeholder.info = device.placeholder.info """If the device is new, we need create automaticaly a new placeholder"""
db_device.placeholder.id_device_supplier = ( if device.binding:
device.placeholder.id_device_supplier return
dict_device = copy.copy(device.__dict__)
dict_device.pop('_sa_instance_state')
dict_device.pop('id', None)
dict_device.pop('devicehub_id', None)
dict_device.pop('actions_multiple', None)
dict_device.pop('actions_one', None)
dict_device.pop('components', None)
dev_placeholder = device.__class__(**dict_device)
for c in device.components:
c_dict = copy.copy(c.__dict__)
c_dict.pop('_sa_instance_state')
c_dict.pop('id', None)
c_dict.pop('devicehub_id', None)
c_dict.pop('actions_multiple', None)
c_dict.pop('actions_one', None)
c_placeholder = c.__class__(**c_dict)
c_placeholder.parent = dev_placeholder
c.parent = device
component_placeholder = Placeholder(
device=c_placeholder, binding=c, is_abstract=True
) )
db_device.sku = device.sku db.session.add(c_placeholder)
db_device.image = device.image db.session.add(component_placeholder)
db_device.brand = device.brand
db_device.generation = device.generation placeholder = Placeholder(
db_device.variant = device.variant device=dev_placeholder, binding=device, is_abstract=True
db_device.version = device.version )
db_device.width = device.width db.session.add(dev_placeholder)
db_device.height = device.height db.session.add(placeholder)
db_device.depth = device.depth
db_device.weight = device.weight
@staticmethod @staticmethod
def add_remove(device: Computer, components: Set[Component]) -> OrderedSet: def add_remove(device: Computer, components: Set[Component]) -> OrderedSet:

View File

@ -3,28 +3,33 @@ import uuid
from itertools import filterfalse from itertools import filterfalse
import marshmallow import marshmallow
from flask import g, current_app as app, render_template, request, Response from flask import Response
from flask import current_app as app
from flask import g, render_template, request
from flask.json import jsonify from flask.json import jsonify
from flask_sqlalchemy import Pagination from flask_sqlalchemy import Pagination
from marshmallow import Schema as MarshmallowSchema
from marshmallow import fields
from marshmallow import fields as f
from marshmallow import validate as v
from sqlalchemy.util import OrderedSet from sqlalchemy.util import OrderedSet
from marshmallow import fields, fields as f, validate as v, Schema as MarshmallowSchema
from teal import query from teal import query
from teal.db import ResourceNotFound
from teal.cache import cache from teal.cache import cache
from teal.resource import View from teal.db import ResourceNotFound
from teal.marshmallow import ValidationError from teal.marshmallow import ValidationError
from teal.resource import View
from ereuse_devicehub import auth from ereuse_devicehub import auth
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.query import SearchQueryParser, things_response from ereuse_devicehub.query import SearchQueryParser, things_response
from ereuse_devicehub.resources import search from ereuse_devicehub.resources import search
from ereuse_devicehub.resources.action import models as actions from ereuse_devicehub.resources.action import models as actions
from ereuse_devicehub.resources.action.models import Trade
from ereuse_devicehub.resources.device import states from ereuse_devicehub.resources.device import states
from ereuse_devicehub.resources.device.models import Device, Manufacturer, Computer from ereuse_devicehub.resources.device.models import Computer, Device, Manufacturer
from ereuse_devicehub.resources.device.search import DeviceSearch from ereuse_devicehub.resources.device.search import DeviceSearch
from ereuse_devicehub.resources.enums import SnapshotSoftware from ereuse_devicehub.resources.enums import SnapshotSoftware
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
from ereuse_devicehub.resources.action.models import Trade
from ereuse_devicehub.resources.tag.model import Tag from ereuse_devicehub.resources.tag.model import Tag
@ -61,15 +66,16 @@ class Filters(query.Query):
manufacturer = query.ILike(Device.manufacturer) manufacturer = query.ILike(Device.manufacturer)
serialNumber = query.ILike(Device.serial_number) serialNumber = query.ILike(Device.serial_number)
# todo test query for rating (and possibly other filters) # todo test query for rating (and possibly other filters)
rating = query.Join((Device.id == actions.ActionWithOneDevice.device_id) rating = query.Join(
(Device.id == actions.ActionWithOneDevice.device_id)
& (actions.ActionWithOneDevice.id == actions.Rate.id), & (actions.ActionWithOneDevice.id == actions.Rate.id),
RateQ) RateQ,
)
tag = query.Join(Device.id == Tag.device_id, TagQ) tag = query.Join(Device.id == Tag.device_id, TagQ)
# todo This part of the query is really slow # todo This part of the query is really slow
# And forces usage of distinct, as it returns many rows # And forces usage of distinct, as it returns many rows
# due to having multiple paths to the same # due to having multiple paths to the same
lot = query.Join((Device.id == LotDeviceDescendants.device_id), lot = query.Join((Device.id == LotDeviceDescendants.device_id), LotQ)
LotQ)
class Sorting(query.Sort): class Sorting(query.Sort):
@ -104,12 +110,15 @@ class DeviceView(View):
return super().get(id) return super().get(id)
def patch(self, id): def patch(self, id):
dev = Device.query.filter_by(id=id, owner_id=g.user.id, active=True).one() dev = Device.query.filter_by(
id=id, owner_id=g.user.id, active=True
).one()
if isinstance(dev, Computer): if isinstance(dev, Computer):
resource_def = app.resources['Computer'] resource_def = app.resources['Computer']
# TODO check how to handle the 'actions_one' # TODO check how to handle the 'actions_one'
patch_schema = resource_def.SCHEMA( patch_schema = resource_def.SCHEMA(
only=['transfer_state', 'actions_one'], partial=True) only=['transfer_state', 'actions_one'], partial=True
)
json = request.get_json(schema=patch_schema) json = request.get_json(schema=patch_schema)
# TODO check how to handle the 'actions_one' # TODO check how to handle the 'actions_one'
json.pop('actions_one') json.pop('actions_one')
@ -129,12 +138,16 @@ class DeviceView(View):
return self.one_private(id) return self.one_private(id)
def one_public(self, id: int): def one_public(self, id: int):
device = Device.query.filter_by(devicehub_id=id, active=True).one() device = Device.query.filter_by(
devicehub_id=id, active=True
).one()
return render_template('devices/layout.html', device=device, states=states) return render_template('devices/layout.html', device=device, states=states)
@auth.Auth.requires_auth @auth.Auth.requires_auth
def one_private(self, id: str): def one_private(self, id: str):
device = Device.query.filter_by(devicehub_id=id, owner_id=g.user.id, active=True).first() device = Device.query.filter_by(
devicehub_id=id, owner_id=g.user.id, active=True
).first()
if not device: if not device:
return self.one_public(id) return self.one_public(id)
return self.schema.jsonify(device) return self.schema.jsonify(device)
@ -148,7 +161,11 @@ class DeviceView(View):
devices = query.paginate(page=args['page'], per_page=30) # type: Pagination devices = query.paginate(page=args['page'], per_page=30) # type: Pagination
return things_response( return things_response(
self.schema.dump(devices.items, many=True, nested=1), self.schema.dump(devices.items, many=True, nested=1),
devices.page, devices.per_page, devices.total, devices.prev_num, devices.next_num devices.page,
devices.per_page,
devices.total,
devices.prev_num,
devices.next_num,
) )
def query(self, args): def query(self, args):
@ -158,9 +175,11 @@ class DeviceView(View):
trades_dev_ids = {d.id for t in trades for d in t.devices} trades_dev_ids = {d.id for t in trades for d in t.devices}
query = Device.query.filter(Device.active == True).filter( query = (
(Device.owner_id == g.user.id) | (Device.id.in_(trades_dev_ids)) Device.query.filter(Device.active == True)
).distinct() .filter((Device.owner_id == g.user.id) | (Device.id.in_(trades_dev_ids)))
.distinct()
)
unassign = args.get('unassign', None) unassign = args.get('unassign', None)
search_p = args.get('search', None) search_p = args.get('search', None)
@ -168,14 +187,18 @@ class DeviceView(View):
properties = DeviceSearch.properties properties = DeviceSearch.properties
tags = DeviceSearch.tags tags = DeviceSearch.tags
devicehub_ids = DeviceSearch.devicehub_ids devicehub_ids = DeviceSearch.devicehub_ids
query = query.join(DeviceSearch).filter( query = (
search.Search.match(properties, search_p) | query.join(DeviceSearch)
search.Search.match(tags, search_p) | .filter(
search.Search.match(devicehub_ids, search_p) search.Search.match(properties, search_p)
).order_by( | search.Search.match(tags, search_p)
search.Search.rank(properties, search_p) + | search.Search.match(devicehub_ids, search_p)
search.Search.rank(tags, search_p) + )
search.Search.rank(devicehub_ids, search_p) .order_by(
search.Search.rank(properties, search_p)
+ search.Search.rank(tags, search_p)
+ search.Search.rank(devicehub_ids, search_p)
)
) )
if unassign: if unassign:
subquery = LotDeviceDescendants.query.with_entities( subquery = LotDeviceDescendants.query.with_entities(
@ -221,10 +244,16 @@ class DeviceMergeView(View):
raise ValidationError('The devices is not the same type.') raise ValidationError('The devices is not the same type.')
# Adding actions of self.with_device # Adding actions of self.with_device
with_actions_one = [a for a in self.with_device.actions with_actions_one = [
if isinstance(a, actions.ActionWithOneDevice)] a
with_actions_multiple = [a for a in self.with_device.actions for a in self.with_device.actions
if isinstance(a, actions.ActionWithMultipleDevices)] if isinstance(a, actions.ActionWithOneDevice)
]
with_actions_multiple = [
a
for a in self.with_device.actions
if isinstance(a, actions.ActionWithMultipleDevices)
]
# Moving the tags from `with_device` to `base_device` # Moving the tags from `with_device` to `base_device`
# Union of tags the device had plus the (potentially) new ones # Union of tags the device had plus the (potentially) new ones
@ -269,20 +298,22 @@ class DeviceMergeView(View):
class ManufacturerView(View): class ManufacturerView(View):
class FindArgs(marshmallow.Schema): class FindArgs(marshmallow.Schema):
search = marshmallow.fields.Str(required=True, search = marshmallow.fields.Str(
required=True,
# Disallow like operators # Disallow like operators
validate=lambda x: '%' not in x and '_' not in x) validate=lambda x: '%' not in x and '_' not in x,
)
@cache(datetime.timedelta(days=1)) @cache(datetime.timedelta(days=1))
def find(self, args: dict): def find(self, args: dict):
search = args['search'] search = args['search']
manufacturers = Manufacturer.query \ manufacturers = Manufacturer.query.filter(
.filter(Manufacturer.name.ilike(search + '%')) \ Manufacturer.name.ilike(search + '%')
.paginate(page=1, per_page=6) # type: Pagination ).paginate(
page=1, per_page=6
) # type: Pagination
return jsonify( return jsonify(
items=app.resources[Manufacturer.t].schema.dump( items=app.resources[Manufacturer.t].schema.dump(
manufacturers.items, manufacturers.items, many=True, nested=1
many=True,
nested=1
) )
) )

View File

@ -7,6 +7,7 @@ from citext import CIText
from flask import g from flask import g
from sqlalchemy import TEXT from sqlalchemy import TEXT
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from sqlalchemy_utils import LtreeType from sqlalchemy_utils import LtreeType
from sqlalchemy_utils.types.ltree import LQUERY from sqlalchemy_utils.types.ltree import LQUERY
from teal.db import CASCADE_OWN, IntEnum, UUIDLtree, check_range from teal.db import CASCADE_OWN, IntEnum, UUIDLtree, check_range
@ -243,6 +244,10 @@ class LotDevice(db.Model):
) )
author = db.relationship(User, primaryjoin=author_id == User.id) author = db.relationship(User, primaryjoin=author_id == User.id)
author_id.comment = """The user that put the device in the lot.""" author_id.comment = """The user that put the device in the lot."""
device = relationship(
'Device',
primaryjoin='Device.id == LotDevice.device_id',
)
class Path(db.Model): class Path(db.Model):

View File

@ -53,6 +53,8 @@ function save_settings() {
data['logo'] = $("#logoCheck").prop('checked'); data['logo'] = $("#logoCheck").prop('checked');
data['dhid'] = $("#dhidCheck").prop('checked'); data['dhid'] = $("#dhidCheck").prop('checked');
data['sid'] = $("#sidCheck").prop('checked'); data['sid'] = $("#sidCheck").prop('checked');
data['phid'] = $("#phidCheck").prop('checked');
data['tags'] = $("#tagsCheck").prop('checked');
data['qr'] = $("#qrCheck").prop('checked'); data['qr'] = $("#qrCheck").prop('checked');
data['serial_number'] = $("#serialNumberCheck").prop('checked'); data['serial_number'] = $("#serialNumberCheck").prop('checked');
data['manufacturer'] = $("#manufacturerCheck").prop('checked'); data['manufacturer'] = $("#manufacturerCheck").prop('checked');
@ -69,11 +71,13 @@ function load_settings() {
$("#qrCheck").prop('checked', data.qr); $("#qrCheck").prop('checked', data.qr);
$("#dhidCheck").prop('checked', data.dhid); $("#dhidCheck").prop('checked', data.dhid);
$("#sidCheck").prop('checked', data.sid); $("#sidCheck").prop('checked', data.sid);
$("#phidCheck").prop('checked', data.phid);
$("#tagsCheck").prop('checked', data.tags);
$("#serialNumberCheck").prop('checked', data.serial_number); $("#serialNumberCheck").prop('checked', data.serial_number);
$("#manufacturerCheck").prop('checked', data.manufacturer); $("#manufacturerCheck").prop('checked', data.manufacturer);
$("#modelCheck").prop('checked', data.model); $("#modelCheck").prop('checked', data.model);
if (data.logo) { if (data.logo) {
$("#logoCheck").prop('checked', data.sid); // $("#logoCheck").prop('checked', data.sid);
previewLogo(data.logoImg); previewLogo(data.logoImg);
$("#logoCheck").prop('checked', data.logo); $("#logoCheck").prop('checked', data.logo);
} else { } else {
@ -89,6 +93,8 @@ function reset_settings() {
$("#qrCheck").prop('checked', true); $("#qrCheck").prop('checked', true);
$("#dhidCheck").prop('checked', true); $("#dhidCheck").prop('checked', true);
$("#sidCheck").prop('checked', true); $("#sidCheck").prop('checked', true);
$("#phidCheck").prop('checked', true);
$("#tagsCheck").prop('checked', false);
$("#serialNumberCheck").prop('checked', false); $("#serialNumberCheck").prop('checked', false);
$("#logoCheck").prop('checked', false); $("#logoCheck").prop('checked', false);
$("#manufacturerCheck").prop('checked', false); $("#manufacturerCheck").prop('checked', false);
@ -135,6 +141,18 @@ function change_check() {
$(".sid").hide(); $(".sid").hide();
}; };
if ($("#phidCheck").prop('checked')) {
$(".phid").show();
} else {
$(".phid").hide();
};
if ($("#tagsCheck").prop('checked')) {
$(".tags").show();
} else {
$(".tags").hide();
};
if ($("#serialNumberCheck").prop('checked')) { if ($("#serialNumberCheck").prop('checked')) {
$(".serial_number").show(); $(".serial_number").show();
} else { } else {
@ -190,6 +208,12 @@ function printpdf() {
if ($("#sidCheck").prop('checked')) { if ($("#sidCheck").prop('checked')) {
height_need += line; height_need += line;
}; };
if ($("#phidCheck").prop('checked')) {
height_need += line;
};
if ($("#tagsCheck").prop('checked')) {
height_need += line;
};
if ($("#serialNumberCheck").prop('checked')) { if ($("#serialNumberCheck").prop('checked')) {
height_need += line; height_need += line;
}; };
@ -248,6 +272,22 @@ function printpdf() {
hspace += line; hspace += line;
} }
}; };
if ($("#phidCheck").prop('checked')) {
var sn = $(y).data('phid');
pdf.setFontSize(12);
if (sn) {
pdf.text(String(sn), border, hspace);
hspace += line;
}
};
if ($("#tagsCheck").prop('checked')) {
var sn = $(y).data('tags');
pdf.setFontSize(12);
if (sn) {
pdf.text(String(sn), border, hspace);
hspace += line;
}
};
if ($("#serialNumberCheck").prop('checked')) { if ($("#serialNumberCheck").prop('checked')) {
var sn = $(y).data('serial-number'); var sn = $(y).data('serial-number');
pdf.setFontSize(12); pdf.setFontSize(12);

View File

@ -0,0 +1,185 @@
{% extends "ereuse_devicehub/base_site.html" %}
{% block main %}
<div class="pagetitle">
<h1>{{ title }}</h1>
<nav>
<ol class="breadcrumb">
<!-- TODO@slamora replace with lot list URL when exists -->
</ol>
</nav>
</div><!-- End Page Title -->
<section class="section profile">
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="card-body">
<div class="pt-4 pb-2">
<h5 class="card-title text-center pb-0 fs-4">{{ title }}</h5>
<p class="text-center">Please check that the information is correct.</p>
</div>
<table class="table table-hover">
<thead>
<tr class="text-center">
<th scope="col">Basic Data</th>
<th scope="col">Info to be Entered</th>
<th scope="col">Info to be Decoupled</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">PHID:</th>
<td class="table-success text-right">{{ placeholder.phid or '' }}</td>
<td class="table-danger">{{ device.placeholder.phid or '' }}</td>
</tr>
<tr>
<th scope="row">Manufacturer:</th>
<td class="table-success text-right">{{ placeholder.device.manufacturer or '' }}</td>
<td class="table-danger">{{ device.manufacturer or '' }}</td>
</tr>
<tr>
<th scope="row">Model:</th>
<td class="table-success">{{ placeholder.device.model or '' }}</td>
<td class="table-danger">{{ device.model or '' }}</td>
</tr>
<tr>
<th scope="row">Serial Number:</th>
<td class="table-success">{{ placeholder.device.serial_number or '' }}</td>
<td class="table-danger">{{ device.serial_number or '' }}</td>
</tr>
<tr>
<th scope="row">Brand:</th>
<td class="table-success">{{ placeholder.device.brand or '' }}</td>
<td class="table-danger">{{ device.brand or '' }}</td>
</tr>
<tr>
<th scope="row">Sku:</th>
<td class="table-success">{{ placeholder.device.sku or '' }}</td>
<td class="table-danger">{{ device.sku or '' }}</td>
</tr>
<tr>
<th scope="row">Generation:</th>
<td class="table-success">{{ placeholder.device.generation or '' }}</td>
<td class="table-danger">{{ device.generation or '' }}</td>
</tr>
<tr>
<th scope="row">Version:</th>
<td class="table-success">{{ placeholder.device.version or '' }}</td>
<td class="table-danger">{{ device.version or '' }}</td>
</tr>
<tr>
<th scope="row">Weight:</th>
<td class="table-success">{{ placeholder.device.weight or '' }}</td>
<td class="table-danger">{{ device.weight or '' }}</td>
</tr>
<tr>
<th scope="row">Width:</th>
<td class="table-success">{{ placeholder.device.width or '' }}</td>
<td class="table-danger">{{ device.width or '' }}</td>
</tr>
<tr>
<th scope="row">Height:</th>
<td class="table-success">{{ placeholder.device.height or '' }}</td>
<td class="table-danger">{{ device.height or '' }}</td>
</tr>
<tr>
<th scope="row">Depth:</th>
<td class="table-success">{{ placeholder.device.depth or '' }}</td>
<td class="table-danger">{{ device.depth or '' }}</td>
</tr>
<tr>
<th scope="row">Color:</th>
<td class="table-success">{{ placeholder.device.color or '' }}</td>
<td class="table-danger">{{ device.color or '' }}</td>
</tr>
<tr>
<th scope="row">Production date:</th>
<td class="table-success">{{ placeholder.device.production_date or '' }}</td>
<td class="table-danger">{{ device.production_date or '' }}</td>
</tr>
<tr>
<th scope="row">Variant:</th>
<td class="table-success">{{ placeholder.device.variant or '' }}</td>
<td class="table-danger">{{ device.variant or '' }}</td>
</tr>
</tbody>
</table>
<br />
{% if placeholder.device.components or device.components %}
<h2>Components</h2>
<table class="table table-hover">
<thead>
<tr class="text-center">
<th scope="col">Info to be Entered</th>
<th scope="col">Info to be Decoupled</th>
</tr>
</thead>
<tbody>
<tr>
<td class="table-success text-right">
{% for c in placeholder.device.components %}
* {{ c.verbose_name }}<br />
{% endfor %}
</td>
<td class="table-danger">
{% for c in device.components %}
* {{ c.verbose_name }}<br />
{% endfor %}
</td>
</tr>
</tbody>
</table>
{% endif %}
<br />
{% if placeholder.device.actions or device.actions %}
<h2>Actions</h2>
<table class="table table-hover">
<thead>
<tr class="text-center">
<th scope="col">Info to be Entered</th>
<th scope="col">Info to be Decoupled</th>
</tr>
</thead>
<tbody>
<tr>
<td class="table-success text-right">
{% for a in placeholder.device.actions %}
* {{ a.t }}<br />
{% endfor %}
</td>
<td class="table-danger">
{% for a in device.actions %}
* {{ a.t }}<br />
{% endfor %}
</td>
</tr>
</tbody>
</table>
{% endif %}
<div>
<form method="post">
<a href="{{ url_for('inventory.device_details', id=device.placeholder.binding.devicehub_id) }}" class="btn btn-danger">Cancel</a>
<button class="btn btn-primary" type="submit">Confirm</button>
</form>
</div>
</div>
</div>
</div>
<div class="col-xl-8">
</div>
</div>
</section>
{% endblock main %}

View File

@ -22,9 +22,21 @@
<!-- Bordered Tabs --> <!-- Bordered Tabs -->
<ul class="nav nav-tabs nav-tabs-bordered"> <ul class="nav nav-tabs nav-tabs-bordered">
{% if placeholder %}
<li class="nav-item"> <li class="nav-item">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#type">Type</button> <a class="nav-link" href="{{ url_for('inventory.device_details', id=placeholder.device.devicehub_id) }}">Placeholder device</a>
</li> </li>
{% else %}
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#type">General details</button>
</li>
{% endif %}
{% if placeholder.binding %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('inventory.device_details', id=placeholder.binding.devicehub_id) }}">Workbench device</a>
</li>
{% endif %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ device.public_link }}" target="_blank">Web</a> <a class="nav-link" href="{{ device.public_link }}" target="_blank">Web</a>
@ -50,10 +62,22 @@
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button>
</li> </li>
{% if device.binding %}
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#binding">Binding</button>
</li>
{% endif %}
{% if device.placeholder and placeholder.binding %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('inventory.unbinding', phid=placeholder.phid) }}">Unbinding</a>
</li>
{% endif %}
</ul> </ul>
<div class="tab-content pt-2"> <div class="tab-content pt-2">
<div class="tab-pane fade show active" id="type"> <div class="tab-pane fade {% if active_binding %}profile-overview{% else %}show active{% endif %}" id="type">
<h5 class="card-title">Details</h5> <h5 class="card-title">Details</h5>
{% if device.placeholder %}(<a href="{{ url_for('inventory.device_edit', id=device.devicehub_id)}}">edit</a>){% endif %} {% if device.placeholder %}(<a href="{{ url_for('inventory.device_edit', id=device.devicehub_id)}}">edit</a>){% endif %}
@ -204,6 +228,42 @@
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
{% if placeholder.binding %}
<div class="tab-pane fade {% if active_binding %}show active{% else %}profile-overview{% endif %}" id="binding">
<h5 class="card-title">Binding</h5>
<div class="list-group col-6">
<p>
Be careful, binding implies changes in the data of a device that affect its
traceability.
</p>
</div>
<div class="list-group col-6">
<form action="{{ url_for('inventory.device_details', id=placeholder.binding.devicehub_id) }}" method="post">
{{ form_binding.csrf_token }}
{% for field in form_binding %}
{% if field != form_binding.csrf_token %}
<div class="col-12">
{{ field.label(class_="form-label") }}:
{{ field }}
{% if field.errors %}
<p class="text-danger">
{% for error in field.errors %}
{{ error }}<br/>
{% endfor %}
</p>
{% endif %}
</div>
{% endif %}
{% endfor %}
<div class="col-12 mt-2">
<input type="submit" class="btn btn-primary" value="Search" />
</div>
</form>
</div>
</div>
{% endif %}
</div> </div>
</div> </div>

View File

@ -401,6 +401,8 @@
<th scope="col">Select</th> <th scope="col">Select</th>
<th scope="col">Title</th> <th scope="col">Title</th>
<th scope="col">DHID</th> <th scope="col">DHID</th>
<th scope="col">PHID</th>
<th scope="col">Is Abstract</th>
<th scope="col">Unique Identifiers</th> <th scope="col">Unique Identifiers</th>
<th scope="col">Lifecycle Status</th> <th scope="col">Lifecycle Status</th>
<th scope="col">Allocated Status</th> <th scope="col">Allocated Status</th>
@ -442,6 +444,12 @@
{{ dev.devicehub_id }} {{ dev.devicehub_id }}
</a> </a>
</td> </td>
<td>
{{ dev.binding and dev.binding.phid or dev.placeholder and dev.placeholder.phid or '' }}
</td>
<td>
{{ dev.binding and dev.binding.is_abstract and '✓' or dev.placeholder and dev.placeholder.is_abstract and '✓' or '' }}
</td>
<td> <td>
{% for t in dev.tags | sort(attribute="id") %} {% for t in dev.tags | sort(attribute="id") %}
<a href="{{ url_for('labels.label_details', id=t.id)}}">{{ t.id }}</a> <a href="{{ url_for('labels.label_details', id=t.id)}}">{{ t.id }}</a>

View File

@ -0,0 +1,185 @@
{% extends "ereuse_devicehub/base_site.html" %}
{% block main %}
<div class="pagetitle">
<h1>{{ title }}</h1>
<nav>
<ol class="breadcrumb">
<!-- TODO@slamora replace with lot list URL when exists -->
</ol>
</nav>
</div><!-- End Page Title -->
<section class="section profile">
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="card-body">
<div class="pt-4 pb-2">
<h5 class="card-title text-center pb-0 fs-4">{{ title }}</h5>
<p class="text-center">Please check that the information is correct.</p>
</div>
<table class="table table-hover">
<thead>
<tr class="text-center">
<th scope="col">Basic Data</th>
<th scope="col">Info to be Entered</th>
<th scope="col">Info to be Decoupled</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">PHID:</th>
<td class="table-success"></td>
<td class="table-danger text-right">{{ placeholder.phid or '' }}</td>
</tr>
<tr>
<th scope="row">Manufacturer:</th>
<td class="table-success">{{ device.manufacturer or '' }}</td>
<td class="table-danger text-right">{{ placeholder.device.manufacturer or '' }}</td>
</tr>
<tr>
<th scope="row">Model:</th>
<td class="table-success">{{ device.model or '' }}</td>
<td class="table-danger">{{ placeholder.device.model or '' }}</td>
</tr>
<tr>
<th scope="row">Serial Number:</th>
<td class="table-success">{{ device.serial_number or '' }}</td>
<td class="table-danger">{{ placeholder.device.serial_number or '' }}</td>
</tr>
<tr>
<th scope="row">Brand:</th>
<td class="table-success">{{ device.brand or '' }}</td>
<td class="table-danger">{{ placeholder.device.brand or '' }}</td>
</tr>
<tr>
<th scope="row">Sku:</th>
<td class="table-success">{{ device.sku or '' }}</td>
<td class="table-danger">{{ placeholder.device.sku or '' }}</td>
</tr>
<tr>
<th scope="row">Generation:</th>
<td class="table-success">{{ device.generation or '' }}</td>
<td class="table-danger">{{ placeholder.device.generation or '' }}</td>
</tr>
<tr>
<th scope="row">Version:</th>
<td class="table-success">{{ device.version or '' }}</td>
<td class="table-danger">{{ placeholder.device.version or '' }}</td>
</tr>
<tr>
<th scope="row">Weight:</th>
<td class="table-success">{{ device.weight or '' }}</td>
<td class="table-danger">{{ placeholder.device.weight or '' }}</td>
</tr>
<tr>
<th scope="row">Width:</th>
<td class="table-success">{{ device.width or '' }}</td>
<td class="table-danger">{{ placeholder.device.width or '' }}</td>
</tr>
<tr>
<th scope="row">Height:</th>
<td class="table-success">{{ device.height or '' }}</td>
<td class="table-danger">{{ placeholder.device.height or '' }}</td>
</tr>
<tr>
<th scope="row">Depth:</th>
<td class="table-success">{{ device.depth or '' }}</td>
<td class="table-danger">{{ placeholder.device.depth or '' }}</td>
</tr>
<tr>
<th scope="row">Color:</th>
<td class="table-success">{{ device.color or '' }}</td>
<td class="table-danger">{{ placeholder.device.color or '' }}</td>
</tr>
<tr>
<th scope="row">Production date:</th>
<td class="table-success">{{ device.production_date or '' }}</td>
<td class="table-danger">{{ placeholder.device.production_date or '' }}</td>
</tr>
<tr>
<th scope="row">Variant:</th>
<td class="table-success">{{ device.variant or '' }}</td>
<td class="table-danger">{{ placeholder.device.variant or '' }}</td>
</tr>
</tbody>
</table>
<br />
{% if placeholder.device.components or device.components %}
<h2>Components</h2>
<table class="table table-hover">
<thead>
<tr class="text-center">
<th scope="col">Info to be Entered</th>
<th scope="col">Info to be Decoupled</th>
</tr>
</thead>
<tbody>
<tr>
<td class="table-success">
{% for c in device.components %}
* {{ c.verbose_name }}<br />
{% endfor %}
</td>
<td class="table-danger text-right">
{% for c in placeholder.device.components %}
* {{ c.verbose_name }}<br />
{% endfor %}
</td>
</tr>
</tbody>
</table>
{% endif %}
<br />
{% if placeholder.device.manual_actions or device.manual_actions %}
<h2>Actions</h2>
<table class="table table-hover">
<thead>
<tr class="text-center">
<th scope="col">Info to be Entered</th>
<th scope="col">Info to be Decoupled</th>
</tr>
</thead>
<tbody>
<tr>
<td class="table-success">
{% for a in device.manual_actions %}
* {{ a.t }}<br />
{% endfor %}
</td>
<td class="table-danger text-right">
{% for a in placeholder.device.manual_actions %}
* {{ a.t }}<br />
{% endfor %}
</td>
</tr>
</tbody>
</table>
{% endif %}
<div>
<form method="post">
<a href="{{ url_for('inventory.device_details', id=placeholder.device.devicehub_id) }}" class="btn btn-danger">Cancel</a>
<button class="btn btn-primary" type="submit">Confirm</button>
</form>
</div>
</div>
</div>
</div>
<div class="col-xl-8">
</div>
</div>
</section>
{% endblock main %}

View File

@ -39,15 +39,33 @@
<b class="tag" data-serial-number="{{ dev.serial_number or '' }}" <b class="tag" data-serial-number="{{ dev.serial_number or '' }}"
data-manufacturer="{{ dev.manufacturer or '' }}" data-manufacturer="{{ dev.manufacturer or '' }}"
data-model="{{ dev.model or '' }}" data-model="{{ dev.model or '' }}"
data-tags="{{ dev.list_tags() }}"
data-phid="{{ dev.phid() }}"
data-sid="{{ dev.sid or '' }}">{{ dev.devicehub_id }}</b> data-sid="{{ dev.sid or '' }}">{{ dev.devicehub_id }}</b>
</div> </div>
</div> </div>
<div class="col sid" style="display: none"> </div>
<div class="row phid" style="display: none">
<div class="col">
<div>
<b>{{ dev.phid() }}</b>
</div>
</div>
</div>
<div class="row sid" style="display: none">
<div class="col">
<div> <div>
<b>{{ dev.sid or '' }}</b> <b>{{ dev.sid or '' }}</b>
</div> </div>
</div> </div>
</div> </div>
<div class="row tags" style="display: none">
<div class="col">
<div>
<b>{{ dev.list_tags() }}</b>
</div>
</div>
</div>
<div class="row serial_number" style="display: none"> <div class="row serial_number" style="display: none">
<div class="col"> <div class="col">
<div> <div>
@ -125,10 +143,18 @@
<input class="form-check-input" name="dhid" type="checkbox" id="dhidCheck" checked=""> <input class="form-check-input" name="dhid" type="checkbox" id="dhidCheck" checked="">
<label class="form-check-label" for="dhidCheck">Dhid</label> <label class="form-check-label" for="dhidCheck">Dhid</label>
</div> </div>
<div class="form-switch">
<input class="form-check-input" name="phid" type="checkbox" id="phidCheck">
<label class="form-check-label" for="phidCheck">Phid</label>
</div>
<div class="form-switch"> <div class="form-switch">
<input class="form-check-input" name="sid" type="checkbox" id="sidCheck"> <input class="form-check-input" name="sid" type="checkbox" id="sidCheck">
<label class="form-check-label" for="sidCheck">Sid</label> <label class="form-check-label" for="sidCheck">Sid</label>
</div> </div>
<div class="form-switch">
<input class="form-check-input" name="tags" type="checkbox" id="tagsCheck">
<label class="form-check-label" for="tagsCheck">Tags</label>
</div>
<div class="form-switch"> <div class="form-switch">
<input class="form-check-input" name="serial_number" type="checkbox" id="serialNumberCheck"> <input class="form-check-input" name="serial_number" type="checkbox" id="serialNumberCheck">
<label class="form-check-label" for="serialNumberCheck">Serial number</label> <label class="form-check-label" for="serialNumberCheck">Serial number</label>

View File

@ -1,2 +1,3 @@
"Type";"Chassis";"Serial Number";"Model";"Manufacturer";"Registered in";"Physical state";"Allocate state";"Lifecycle state";"Processor";"RAM (MB)";"Data Storage Size (MB)" "Type";"Chassis";"Serial Number";"Model";"Manufacturer";"Registered in";"Physical state";"Allocate state";"Lifecycle state";"Processor";"RAM (MB)";"Data Storage Size (MB)"
"Desktop";"Microtower";"d1s";"d1ml";"d1mr";"Mon Aug 1 20:20:51 2022";"";"";"";"p1ml";"0";"0" "Desktop";"Microtower";"d1s";"d1ml";"d1mr";"Tue Aug 2 12:57:43 2022";"";"";"";"p1ml";"0";"0"
"Desktop";"Microtower";"d1s";"d1ml";"d1mr";"Tue Aug 2 12:57:43 2022";"";"";"";"p1ml";"0";"0"

1 Type Chassis Serial Number Model Manufacturer Registered in Physical state Allocate state Lifecycle state Processor RAM (MB) Data Storage Size (MB)
2 Desktop Microtower d1s d1ml d1mr Mon Aug 1 20:20:51 2022 Tue Aug 2 12:57:43 2022 p1ml 0 0
3 Desktop Microtower d1s d1ml d1mr Tue Aug 2 12:57:43 2022 p1ml 0 0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -75,6 +75,8 @@ def test_api_docs(client: Client):
'/inventory/upload-placeholder/', '/inventory/upload-placeholder/',
'/inventory/lot/{lot_id}/upload-placeholder/', '/inventory/lot/{lot_id}/upload-placeholder/',
'/inventory/placeholder-logs/', '/inventory/placeholder-logs/',
'/inventory/unbinding/{phid}/',
'/inventory/binding/{dhid}/{phid}/',
'/labels/', '/labels/',
'/labels/add/', '/labels/add/',
'/labels/print', '/labels/print',

View File

@ -347,7 +347,7 @@ 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) == 2 assert len(db_pc.tags) == 1
for tag in db_pc.tags: for tag in db_pc.tags:
assert tag.id in ['foo', db_pc.devicehub_id] assert tag.id in ['foo', db_pc.devicehub_id]
@ -501,7 +501,7 @@ def test_get_devices_permissions(app: Devicehub, user: UserClient, user2: UserCl
devices2, res2 = user2.get(url, None) devices2, 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(devices['items']) == 1 assert len(devices['items']) == 2
assert len(devices2['items']) == 0 assert len(devices2['items']) == 0
@ -515,13 +515,13 @@ def test_get_devices_unassigned(user: UserClient):
devices, res = user.get(url, None) devices, res = user.get(url, None)
assert res.status_code == 200 assert res.status_code == 200
assert len(devices['items']) == 1 assert len(devices['items']) == 2
url = '/devices/?filter={"type":["Computer"]}&unassign=1' url = '/devices/?filter={"type":["Computer"]}&unassign=1'
devices, res = user.get(url, None) devices, res = user.get(url, None)
assert res.status_code == 200 assert res.status_code == 200
assert len(devices['items']) == 1 assert len(devices['items']) == 2
from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.lot.models import Lot
device_id = devices['items'][0]['id'] device_id = devices['items'][0]['id']
@ -537,13 +537,13 @@ def test_get_devices_unassigned(user: UserClient):
devices, res = user.get(url, None) devices, res = user.get(url, None)
assert res.status_code == 200 assert res.status_code == 200
assert len(devices['items']) == 1 assert len(devices['items']) == 2
url = '/devices/?filter={"type":["Computer"]}&unassign=1' url = '/devices/?filter={"type":["Computer"]}&unassign=1'
devices, res = user.get(url, None) devices, res = user.get(url, None)
assert res.status_code == 200 assert res.status_code == 200
assert len(devices['items']) == 0 assert len(devices['items']) == 1
@pytest.mark.mvp @pytest.mark.mvp
@ -580,7 +580,7 @@ def test_manufacturer_enforced():
def test_device_properties_format(app: Devicehub, user: UserClient): def test_device_properties_format(app: Devicehub, user: UserClient):
user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot) user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
with app.app_context(): with app.app_context():
pc = d.Laptop.query.one() # type: d.Laptop pc = d.Laptop.query.filter_by(placeholder=None).one() # type: d.Laptop
assert format(pc) == 'Laptop 3: model 1000h, S/N 94oaaq021116' assert format(pc) == 'Laptop 3: model 1000h, S/N 94oaaq021116'
assert format(pc, 't') == 'Netbook 1000h' assert format(pc, 't') == 'Netbook 1000h'
assert format(pc, 's') == '(asustek computer inc.) S/N 94OAAQ021116' assert format(pc, 's') == '(asustek computer inc.) S/N 94OAAQ021116'
@ -589,12 +589,12 @@ def test_device_properties_format(app: Devicehub, user: UserClient):
assert pc.graphic_card_model == 'mobile 945gse express integrated graphics controller' assert pc.graphic_card_model == 'mobile 945gse express integrated graphics controller'
assert pc.processor_model == 'intel atom cpu n270 @ 1.60ghz' assert pc.processor_model == 'intel atom cpu n270 @ 1.60ghz'
net = next(c for c in pc.components if isinstance(c, d.NetworkAdapter)) net = next(c for c in pc.components if isinstance(c, d.NetworkAdapter))
assert format(net) == 'NetworkAdapter 4: model ar8121/ar8113/ar8114 ' \ assert format(net) == 'NetworkAdapter 5: model ar8121/ar8113/ar8114 ' \
'gigabit or fast ethernet, S/N 00:24:8c:7f:cf:2d' 'gigabit or fast ethernet, S/N 00:24:8c:7f:cf:2d'
assert format(net, 't') == 'NetworkAdapter ar8121/ar8113/ar8114 gigabit or fast ethernet' assert format(net, 't') == 'NetworkAdapter ar8121/ar8113/ar8114 gigabit or fast ethernet'
assert format(net, 's') == 'qualcomm atheros 00:24:8C:7F:CF:2D 100 Mbps' assert format(net, 's') == 'qualcomm atheros 00:24:8C:7F:CF:2D 100 Mbps'
hdd = next(c for c in pc.components if isinstance(c, d.DataStorage)) hdd = next(c for c in pc.components if isinstance(c, d.DataStorage))
assert format(hdd) == 'HardDrive 9: model st9160310as, S/N 5sv4tqa6' assert format(hdd) == 'HardDrive 10: model st9160310as, S/N 5sv4tqa6'
assert format(hdd, 't') == 'HardDrive st9160310as' assert format(hdd, 't') == 'HardDrive st9160310as'
assert format(hdd, 's') == 'seagate 5SV4TQA6 152 GB' assert format(hdd, 's') == 'seagate 5SV4TQA6 152 GB'
@ -702,7 +702,7 @@ def test_hid_with_2networkadapters(app: Devicehub, user: UserClient):
laptop = devices['items'][0] laptop = devices['items'][0]
assert laptop['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' assert laptop['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'
assert len([c for c in devices['items'] if c['type'] == 'Laptop']) == 1 assert len([c for c in devices['items'] if c['type'] == 'Laptop']) == 2
@pytest.mark.mvp @pytest.mark.mvp
@ -723,7 +723,7 @@ def test_hid_with_2network_and_drop_no_mac_in_hid(app: Devicehub, user: UserClie
devices, _ = user.get(res=d.Device) devices, _ = user.get(res=d.Device)
laptop = devices['items'][0] laptop = devices['items'][0]
assert laptop['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' assert laptop['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'
assert len([c for c in devices['items'] if c['type'] == 'Laptop']) == 1 assert len([c for c in devices['items'] if c['type'] == 'Laptop']) == 2
assert len([c for c in laptop['components'] if c['type'] == 'NetworkAdapter']) == 1 assert len([c for c in laptop['components'] if c['type'] == 'NetworkAdapter']) == 1
@ -746,22 +746,21 @@ def test_hid_with_2network_and_drop_mac_in_hid(app: Devicehub, user: UserClient)
user.post(json_encode(snapshot), res=m.Snapshot) user.post(json_encode(snapshot), res=m.Snapshot)
devices, _ = user.get(res=d.Device) devices, _ = user.get(res=d.Device)
laptops = [c for c in devices['items'] if c['type'] == 'Laptop'] laptops = [c for c in devices['items'] if c['type'] == 'Laptop']
assert len(laptops) == 2 assert len(laptops) == 4
hids = [h['hid'] for h in laptops] hids = [laptops[0]['hid'], laptops[2]['hid']]
proof_hid = ['laptop-asustek_computer_inc-1000h-94oaaq021116-a0:24:8c:7f:cf:2d', proof_hid = ['laptop-asustek_computer_inc-1000h-94oaaq021116-a0:24:8c:7f:cf:2d',
'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'] 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d']
assert all([h in proof_hid for h in hids]) assert all([h in proof_hid for h in hids])
# we drop all network cards # we drop all network cards
snapshot['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abc' snapshot['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abc'
snapshot['components'] = [c for c in snapshot['components'] if not c in [network, network2]] snapshot['components'] = [c for c in snapshot['components'] if c not in [network, network2]]
user.post(json_encode(snapshot), res=m.Snapshot) user.post(json_encode(snapshot), res=m.Snapshot)
devices, _ = user.get(res=d.Device) devices, _ = user.get(res=d.Device)
laptops = [c for c in devices['items'] if c['type'] == 'Laptop'] laptops = [c for c in devices['items'] if c['type'] == 'Laptop']
assert len(laptops) == 3 assert len(laptops) == 4
hids = [h['hid'] for h in laptops] hids = [laptops[0]['hid'], laptops[2]['hid']]
proof_hid = ['laptop-asustek_computer_inc-1000h-94oaaq021116-a0:24:8c:7f:cf:2d', proof_hid = ['laptop-asustek_computer_inc-1000h-94oaaq021116-a0:24:8c:7f:cf:2d',
'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d', 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d',
'laptop-asustek_computer_inc-1000h-94oaaq021116'] 'laptop-asustek_computer_inc-1000h-94oaaq021116']
assert all([h in proof_hid for h in hids]) assert all([h in proof_hid for h in hids])

View File

@ -183,7 +183,6 @@ 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']) == 3 assert len(pc['actions']) == 3
assert len(pc['components']) == 3 assert len(pc['components']) == 3
assert pc['tags'][0]['id'] == pc['devicehubID']
@pytest.mark.mvp @pytest.mark.mvp

View File

@ -508,12 +508,14 @@ def test_report_devices_stock_control(user: UserClient, user2: UserClient):
fixture_csv = list(obj_csv) fixture_csv = list(obj_csv)
assert user.user['id'] != user2.user['id'] assert user.user['id'] != user2.user['id']
assert len(export_csv) == 2 assert len(export_csv) == 3
export_csv[0] = export_csv[0][0].split(';') export_csv[0] = export_csv[0][0].split(';')
export_csv[1] = export_csv[1][0].split(';') export_csv[1] = export_csv[1][0].split(';')
export_csv[2] = export_csv[2][0].split(';')
fixture_csv[0] = fixture_csv[0][0].split(';') fixture_csv[0] = fixture_csv[0][0].split(';')
fixture_csv[1] = fixture_csv[1][0].split(';') fixture_csv[1] = fixture_csv[1][0].split(';')
fixture_csv[2] = fixture_csv[2][0].split(';')
# assert isinstance( # assert isinstance(
# datetime.strptime(export_csv[1][5], '%c'), datetime # datetime.strptime(export_csv[1][5], '%c'), datetime
@ -522,9 +524,12 @@ def test_report_devices_stock_control(user: UserClient, user2: UserClient):
# Pop dates fields from csv lists to compare them # Pop dates fields from csv lists to compare them
fixture_csv[1] = fixture_csv[1][:5] + fixture_csv[1][6:] fixture_csv[1] = fixture_csv[1][:5] + fixture_csv[1][6:]
export_csv[1] = export_csv[1][:5] + export_csv[1][6:] export_csv[1] = export_csv[1][:5] + export_csv[1][6:]
fixture_csv[2] = fixture_csv[2][:5] + fixture_csv[2][6:]
export_csv[2] = export_csv[2][:5] + export_csv[2][6:]
assert fixture_csv[0] == export_csv[0], 'Headers are not equal' assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
assert fixture_csv[1] == export_csv[1], 'Computer information are not equal' assert fixture_csv[1] == export_csv[1], 'Computer information are not equal'
assert fixture_csv[2] == export_csv[2], 'Computer information are not equal'
assert fixture_csv == export_csv assert fixture_csv == export_csv

View File

@ -188,7 +188,7 @@ def test_complet_metrics_with_trade(user: UserClient, user2: UserClient):
body1_lenovo += '"foo2@foo.com";"Supplier";"NeedConfirmation";"Use";"";' body1_lenovo += '"foo2@foo.com";"Supplier";"NeedConfirmation";"Use";"";'
body2_lenovo = ';"";"0";"0";"Trade";"0";"0"\n' body2_lenovo = ';"";"0";"0";"Trade";"0";"0"\n'
body1_acer = '"J2MA2";"laptop-acer-aohappy-lusea0d010038879a01601-00:26:c7:8e:cb:8c";"";"Trade";' body1_acer = '"K3XW2";"laptop-acer-aohappy-lusea0d010038879a01601-00:26:c7:8e:cb:8c";"";"Trade";'
body1_acer += '"foo@foo.com";"foo2@foo.com";"Supplier";"NeedConfirmation";"";"";"";"";"0";' body1_acer += '"foo@foo.com";"foo2@foo.com";"Supplier";"NeedConfirmation";"";"";"";"";"0";'
body2_acer = ';"";"0";"0";"Trade";"0";"4692.0"\n' body2_acer = ';"";"0";"0";"Trade";"0";"4692.0"\n'

View File

@ -14,7 +14,7 @@ from ereuse_devicehub.client import UserClient, UserClientFlask
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.action.models import Snapshot from ereuse_devicehub.resources.action.models import Snapshot
from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.device.models import Device, Placeholder
from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.lot.models import Lot
from ereuse_devicehub.resources.user.models import User from ereuse_devicehub.resources.user.models import User
from tests import conftest from tests import conftest
@ -190,7 +190,7 @@ def test_inventory_with_device(user3: UserClientFlask):
assert status == '200 OK' assert status == '200 OK'
assert "Unassigned" in body assert "Unassigned" in body
assert db_snapthot.device.devicehub_id in body assert db_snapthot.device.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -203,7 +203,7 @@ def test_inventory_filter(user3: UserClientFlask):
assert status == '200 OK' assert status == '200 OK'
assert "Unassigned" in body assert "Unassigned" in body
assert db_snapthot.device.devicehub_id in body assert db_snapthot.device.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -310,7 +310,7 @@ def test_label_details(user3: UserClientFlask):
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_link_tag_to_device(user3: UserClientFlask): def test_link_tag_to_device(user3: UserClientFlask):
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev = snap.device dev = snap.device.binding.device
uri = '/labels/add/' uri = '/labels/add/'
user3.get(uri) user3.get(uri)
@ -331,7 +331,7 @@ def test_link_tag_to_device(user3: UserClientFlask):
uri = '/inventory/tag/devices/add/' uri = '/inventory/tag/devices/add/'
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(list(dev.tags)) == 2 assert len(list(dev.tags)) == 1
tags = [tag.id for tag in dev.tags] tags = [tag.id for tag in dev.tags]
assert "tag1" in tags assert "tag1" in tags
@ -341,7 +341,7 @@ def test_link_tag_to_device(user3: UserClientFlask):
def test_unlink_tag_to_device(user3: UserClientFlask): def test_unlink_tag_to_device(user3: UserClientFlask):
# create device # create device
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev = snap.device dev = snap.device.binding.device
# create tag # create tag
uri = '/labels/add/' uri = '/labels/add/'
@ -379,9 +379,7 @@ def test_unlink_tag_to_device(user3: UserClientFlask):
} }
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(list(dev.tags)) == 1 assert len(list(dev.tags)) == 0
tag = list(dev.tags)[0]
assert not tag.id == "tag1"
@pytest.mark.mvp @pytest.mark.mvp
@ -389,7 +387,7 @@ def test_unlink_tag_to_device(user3: UserClientFlask):
def test_print_labels(user3: UserClientFlask): def test_print_labels(user3: UserClientFlask):
# create device # create device
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev = snap.device dev = snap.device.binding.device
# create tag # create tag
uri = '/labels/add/' uri = '/labels/add/'
@ -411,7 +409,7 @@ def test_print_labels(user3: UserClientFlask):
uri = '/inventory/tag/devices/add/' uri = '/inventory/tag/devices/add/'
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(list(dev.tags)) == 2 assert len(list(dev.tags)) == 1
uri = '/labels/print' uri = '/labels/print'
data = { data = {
@ -423,7 +421,7 @@ def test_print_labels(user3: UserClientFlask):
assert status == '200 OK' assert status == '200 OK'
path = "/inventory/device/{}/".format(dev.devicehub_id) path = "/inventory/device/{}/".format(dev.devicehub_id)
assert path in body assert path in body
assert "tag1" not in body assert "tag1" in body
@pytest.mark.mvp @pytest.mark.mvp
@ -708,7 +706,7 @@ def test_action_recycling(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
@ -721,15 +719,15 @@ def test_action_recycling(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Recycling", 'type': "Recycling",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'Recycling' assert dev.binding.device.actions[-1].type == 'Recycling'
assert 'Action &#34;Recycling&#34; created successfully!' in body assert 'Action &#34;Recycling&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -763,15 +761,15 @@ def test_action_use(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Use", 'type': "Use",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'Use' assert dev.binding.device.actions[-1].type == 'Use'
assert 'Action &#34;Use&#34; created successfully!' in body assert 'Action &#34;Use&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -786,15 +784,15 @@ def test_action_refurbish(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Refurbish", 'type': "Refurbish",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'Refurbish' assert dev.binding.device.actions[-1].type == 'Refurbish'
assert 'Action &#34;Refurbish&#34; created successfully!' in body assert 'Action &#34;Refurbish&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -809,15 +807,15 @@ def test_action_management(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Management", 'type': "Management",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'Management' assert dev.binding.device.actions[-1].type == 'Management'
assert 'Action &#34;Management&#34; created successfully!' in body assert 'Action &#34;Management&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -832,7 +830,7 @@ def test_action_allocate(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01', 'start_time': '2000-01-01',
'end_time': '2000-06-01', 'end_time': '2000-06-01',
'end_users': 2, 'end_users': 2,
@ -841,9 +839,9 @@ def test_action_allocate(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/' uri = '/inventory/action/allocate/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'Allocate' assert dev.binding.device.actions[-1].type == 'Allocate'
assert 'Action &#34;Allocate&#34; created successfully!' in body assert 'Action &#34;Allocate&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -858,18 +856,18 @@ def test_action_allocate_error_required(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Trade", 'type': "Trade",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/allocate/add/' uri = '/inventory/action/allocate/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert dev.actions[-1].type != 'Allocate' assert 'Allocate' not in [x.type for x in dev.binding.device.actions]
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/allocate/add/' uri = '/inventory/action/allocate/add/'
@ -891,7 +889,7 @@ def test_action_allocate_error_dates(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-06-01', 'start_time': '2000-06-01',
'end_time': '2000-01-01', 'end_time': '2000-01-01',
'end_users': 2, 'end_users': 2,
@ -902,7 +900,7 @@ def test_action_allocate_error_dates(user3: UserClientFlask):
assert status == '200 OK' assert status == '200 OK'
assert 'Action Allocate error' in body assert 'Action Allocate error' in body
assert 'The action cannot finish before it starts.' in body assert 'The action cannot finish before it starts.' in body
assert dev.actions[-1].type != 'Allocate' assert 'Allocate' not in [x.type for x in dev.binding.device.actions]
@pytest.mark.mvp @pytest.mark.mvp
@ -919,7 +917,7 @@ def test_action_allocate_error_future_dates(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': start_time, 'start_time': start_time,
'end_time': end_time, 'end_time': end_time,
'end_users': 2, 'end_users': 2,
@ -930,7 +928,7 @@ def test_action_allocate_error_future_dates(user3: UserClientFlask):
assert status == '200 OK' assert status == '200 OK'
assert 'Action Allocate error' in body assert 'Action Allocate error' in body
assert 'Not a valid date value.!' in body assert 'Not a valid date value.!' in body
assert dev.actions[-1].type != 'Allocate' assert 'Allocate' not in [x.type for x in dev.binding.device.actions]
@pytest.mark.mvp @pytest.mark.mvp
@ -945,7 +943,7 @@ def test_action_deallocate(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01', 'start_time': '2000-01-01',
'end_time': '2000-06-01', 'end_time': '2000-06-01',
'end_users': 2, 'end_users': 2,
@ -954,22 +952,22 @@ def test_action_deallocate(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/' uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data) user3.post(uri, data=data)
assert dev.allocated_status.type == 'Allocate' assert dev.binding.device.allocated_status.type == 'Allocate'
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Deallocate", 'type': "Deallocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01', 'start_time': '2000-01-01',
'end_time': '2000-06-01', 'end_time': '2000-06-01',
'end_users': 2, 'end_users': 2,
} }
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.allocated_status.type == 'Deallocate' assert dev.binding.device.allocated_status.type == 'Deallocate'
assert 'Action &#34;Deallocate&#34; created successfully!' in body assert 'Action &#34;Deallocate&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -984,7 +982,7 @@ def test_action_deallocate_error(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-05-01', 'start_time': '2000-05-01',
'end_time': '2000-06-01', 'end_time': '2000-06-01',
'end_users': 2, 'end_users': 2,
@ -993,20 +991,20 @@ def test_action_deallocate_error(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/' uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data) user3.post(uri, data=data)
assert dev.allocated_status.type == 'Allocate' assert dev.binding.device.allocated_status.type == 'Allocate'
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Deallocate", 'type': "Deallocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01', 'start_time': '2000-01-01',
'end_time': '2000-02-01', 'end_time': '2000-02-01',
'end_users': 2, 'end_users': 2,
} }
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.allocated_status.type != 'Deallocate' assert dev.binding.device.allocated_status.type != 'Deallocate'
assert 'Action Deallocate error!' in body assert 'Action Deallocate error!' in body
assert 'Sorry some of this devices are actually deallocate' in body assert 'Sorry some of this devices are actually deallocate' in body
@ -1023,7 +1021,7 @@ def test_action_allocate_deallocate_error(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01', 'start_time': '2000-01-01',
'end_time': '2000-01-01', 'end_time': '2000-01-01',
'end_users': 2, 'end_users': 2,
@ -1032,36 +1030,36 @@ def test_action_allocate_deallocate_error(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/' uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data) user3.post(uri, data=data)
assert dev.allocated_status.type == 'Allocate' assert dev.binding.device.allocated_status.type == 'Allocate'
assert len(dev.actions) == 11 assert len(dev.binding.device.actions) == 1
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Deallocate", 'type': "Deallocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-02-01', 'start_time': '2000-02-01',
'end_time': '2000-02-01', 'end_time': '2000-02-01',
'end_users': 2, 'end_users': 2,
} }
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.allocated_status.type == 'Deallocate' assert dev.binding.device.allocated_status.type == 'Deallocate'
assert len(dev.actions) == 12 assert len(dev.binding.device.actions) == 2
# is not possible to do an allocate between an allocate and an deallocate # is not possible to do an allocate between an allocate and an deallocate
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-15', 'start_time': '2000-01-15',
'end_time': '2000-01-15', 'end_time': '2000-01-15',
'end_users': 2, 'end_users': 2,
} }
user3.post(uri, data=data) user3.post(uri, data=data)
assert dev.allocated_status.type == 'Deallocate' assert dev.binding.device.allocated_status.type == 'Deallocate'
# assert 'Action Deallocate error!' in body # assert 'Action Deallocate error!' in body
# assert 'Sorry some of this devices are actually deallocate' in body # assert 'Sorry some of this devices are actually deallocate' in body
# #
@ -1069,14 +1067,14 @@ def test_action_allocate_deallocate_error(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Deallocate", 'type': "Deallocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-15', 'start_time': '2000-01-15',
'end_time': '2000-01-15', 'end_time': '2000-01-15',
'end_users': 2, 'end_users': 2,
} }
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(dev.actions) == 12 assert len(dev.binding.device.actions) == 2
@pytest.mark.mvp @pytest.mark.mvp
@ -1091,7 +1089,7 @@ def test_action_allocate_deallocate_error2(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-10', 'start_time': '2000-01-10',
'end_users': 2, 'end_users': 2,
} }
@ -1099,25 +1097,25 @@ def test_action_allocate_deallocate_error2(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/' uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(dev.actions) == 11 assert len(dev.binding.device.actions) == 1
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Deallocate", 'type': "Deallocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-20', 'start_time': '2000-01-20',
'end_users': 2, 'end_users': 2,
} }
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert len(dev.actions) == 12 assert len(dev.binding.device.actions) == 2
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-02-10', 'start_time': '2000-02-10',
'end_users': 2, 'end_users': 2,
} }
@ -1125,40 +1123,40 @@ def test_action_allocate_deallocate_error2(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/' uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(dev.actions) == 13 assert len(dev.binding.device.actions) == 3
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Deallocate", 'type': "Deallocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-02-20', 'start_time': '2000-02-20',
'end_users': 2, 'end_users': 2,
} }
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(dev.actions) == 14 assert len(dev.binding.device.actions) == 4
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Allocate", 'type': "Allocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-25', 'start_time': '2000-01-25',
'end_users': 2, 'end_users': 2,
} }
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(dev.actions) == 15 assert len(dev.binding.device.actions) == 5
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Deallocate", 'type': "Deallocate",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-27', 'start_time': '2000-01-27',
'end_users': 2, 'end_users': 2,
} }
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(dev.actions) == 16 assert len(dev.binding.device.actions) == 6
@pytest.mark.mvp @pytest.mark.mvp
@ -1173,15 +1171,15 @@ def test_action_toprepare(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "ToPrepare", 'type': "ToPrepare",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'ToPrepare' assert dev.binding.device.actions[-1].type == 'ToPrepare'
assert 'Action &#34;ToPrepare&#34; created successfully!' in body assert 'Action &#34;ToPrepare&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -1196,15 +1194,15 @@ def test_action_prepare(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Prepare", 'type': "Prepare",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'Prepare' assert dev.binding.device.actions[-1].type == 'Prepare'
assert 'Action &#34;Prepare&#34; created successfully!' in body assert 'Action &#34;Prepare&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -1219,15 +1217,15 @@ def test_action_torepair(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "ToRepair", 'type': "ToRepair",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'ToRepair' assert dev.binding.device.actions[-1].type == 'ToRepair'
assert 'Action &#34;ToRepair&#34; created successfully!' in body assert 'Action &#34;ToRepair&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -1242,15 +1240,15 @@ def test_action_ready(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "Ready", 'type': "Ready",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
} }
uri = '/inventory/action/add/' uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'Ready' assert dev.binding.device.actions[-1].type == 'Ready'
assert 'Action &#34;Ready&#34; created successfully!' in body assert 'Action &#34;Ready&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -1269,16 +1267,16 @@ def test_action_datawipe(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
'type': "DataWipe", 'type': "DataWipe",
'severity': "Info", 'severity': "Info",
'devices': "{}".format(dev.id), 'devices': "{}".format(dev.binding.device.id),
'document-file_name': file_upload, 'document-file_name': file_upload,
} }
uri = '/inventory/action/datawipe/add/' uri = '/inventory/action/datawipe/add/'
body, status = user3.post(uri, data=data, content_type="multipart/form-data") body, status = user3.post(uri, data=data, content_type="multipart/form-data")
assert status == '200 OK' assert status == '200 OK'
assert dev.actions[-1].type == 'DataWipe' assert dev.binding.device.actions[-1].type == 'DataWipe'
assert 'Action &#34;DataWipe&#34; created successfully!' in body assert 'Action &#34;DataWipe&#34; created successfully!' in body
assert dev.devicehub_id in body assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp @pytest.mark.mvp
@ -1618,7 +1616,6 @@ def test_export_snapshot_json(user3: UserClientFlask):
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_add_placeholder_excel(user3: UserClientFlask): def test_add_placeholder_excel(user3: UserClientFlask):
uri = '/inventory/upload-placeholder/' uri = '/inventory/upload-placeholder/'
body, status = user3.get(uri) body, status = user3.get(uri)
assert status == '200 OK' assert status == '200 OK'
@ -2009,3 +2006,191 @@ def test_add_new_placeholder_from_lot(user3: UserClientFlask):
assert dev.hid == 'laptop-samsung-lc27t55-aaaab' assert dev.hid == 'laptop-samsung-lc27t55-aaaab'
assert dev.placeholder.phid == 'ace' assert dev.placeholder.phid == 'ace'
assert len(lot.devices) == 1 assert len(lot.devices) == 1
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_manual_binding(user3: UserClientFlask):
# create placeholder manual
uri = '/inventory/device/add/'
user3.get(uri)
data = {
'csrf_token': generate_csrf(),
'type': "Laptop",
'phid': 'sid',
'serial_number': "AAAAB",
'model': "LC27T55",
'manufacturer': "Samsung",
'generation': 1,
'weight': 0.1,
'height': 0.1,
'depth': 0.1,
'id_device_supplier': "b2",
}
user3.post(uri, data=data)
dev = Device.query.one()
assert dev.hid == 'laptop-samsung-lc27t55-aaaab'
assert dev.placeholder.phid == 'sid'
assert dev.placeholder.is_abstract is False
# add device from wb
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev_wb = snap.device
uri = '/inventory/device/'
user3.get(uri)
assert dev_wb.binding.is_abstract is True
assert dev_wb.hid == 'laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b'
assert dev_wb.binding.phid == '11'
old_placeholder = dev_wb.binding
# page binding
dhid = dev_wb.devicehub_id
uri = f'/inventory/binding/{dhid}/sid/'
body, status = user3.get(uri)
assert status == '200 OK'
assert 'sid' in body
assert 'Confirm' in body
# action binding
body, status = user3.post(uri, data={})
assert status == '200 OK'
assert f"Device &#34;{dhid}&#34; bind successfully with sid!" in body
# check new structure
assert dev_wb.binding.phid == 'sid'
assert dev_wb.binding.device == dev
assert Placeholder.query.filter_by(id=old_placeholder.id).first() is None
assert Device.query.filter_by(id=old_placeholder.device.id).first() is None
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_edit_and_binding(user3: UserClientFlask):
uri = '/inventory/device/add/'
user3.get(uri)
data = {
'csrf_token': generate_csrf(),
'type': "Laptop",
'serial_number': "AAAAB",
'model': "LC27T55",
'manufacturer': "Samsung",
'generation': 1,
'weight': 0.1,
'height': 0.1,
'depth': 0.1,
'id_device_supplier': "b2",
}
user3.post(uri, data=data)
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev_wb = snap.device
uri = '/inventory/device/'
user3.get(uri)
uri = '/inventory/device/edit/{}/'.format(dev_wb.binding.device.devicehub_id)
body, status = user3.get(uri)
assert status == '200 OK'
assert "Edit Device" in body
data = {
'csrf_token': generate_csrf(),
'type': "Laptop",
'serial_number': "AAAAC",
'model': "LC27T56",
'manufacturer': "Samsung",
'generation': 1,
'weight': 0.1,
'height': 0.1,
'depth': 0.1,
'id_device_supplier': "a2",
}
assert dev_wb.binding.is_abstract is True
user3.post(uri, data=data)
assert dev_wb.binding.is_abstract is False
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_unbinding(user3: UserClientFlask):
# create placeholder manual
uri = '/inventory/device/add/'
user3.get(uri)
data = {
'csrf_token': generate_csrf(),
'type': "Laptop",
'phid': 'sid',
'serial_number': "AAAAB",
'model': "LC27T55",
'manufacturer': "Samsung",
'generation': 1,
'weight': 0.1,
'height': 0.1,
'depth': 0.1,
'id_device_supplier': "b2",
}
user3.post(uri, data=data)
dev = Device.query.one()
# add device from wb
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev_wb = snap.device
uri = '/inventory/device/'
user3.get(uri)
old_placeholder = dev_wb.binding
# page binding
dhid = dev_wb.devicehub_id
uri = f'/inventory/binding/{dhid}/sid/'
user3.get(uri)
# action binding
assert dev.placeholder.binding is None
user3.post(uri, data={})
assert dev.placeholder.binding == dev_wb
# action unbinding
uri = '/inventory/unbinding/sid/'
body, status = user3.post(uri, data={})
assert status == '200 OK'
assert 'Device &#34;sid&#34; unbind successfully!' in body
# check new structure
assert dev.placeholder.binding is None
assert dev_wb.binding.phid == '2'
assert old_placeholder.device.model == dev_wb.binding.device.model
assert old_placeholder.device != dev_wb.binding.device
assert Placeholder.query.filter_by(id=old_placeholder.id).first() is None
assert Device.query.filter_by(id=old_placeholder.device.id).first() is None
assert Placeholder.query.filter_by(id=dev_wb.binding.id).first()
assert Device.query.filter_by(id=dev_wb.binding.device.id).first()
assert Device.query.filter_by(id=dev.id).first()
assert Placeholder.query.filter_by(id=dev.placeholder.id).first()
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_unbindingnot_used(user3: UserClientFlask):
# add device from wb
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev_wb = snap.device
uri = '/inventory/device/'
user3.get(uri)
old_placeholder = dev_wb.binding
# action unbinding
uri = '/inventory/unbinding/{}/'.format(dev_wb.binding.phid)
body, status = user3.post(uri, data={})
assert status == '200 OK'
# check new structure
assert dev_wb.binding == old_placeholder
assert Placeholder.query.filter_by(id=old_placeholder.id).first()
assert Device.query.filter_by(id=old_placeholder.device.id).first()
assert Device.query.filter_by(id=dev_wb.id).first()

View File

@ -367,18 +367,6 @@ def test_snapshot_mismatch_id():
pass pass
@pytest.mark.mvp
def test_snapshot_tag_inner_tag(user: UserClient, tag_id: str, app: Devicehub):
"""Tests a posting Snapshot with a local tag."""
b = yaml2json('basic.snapshot')
b['device']['tags'] = [{'type': 'Tag', 'id': tag_id}]
snapshot_and_check(user, b, action_types=(BenchmarkProcessor.t, VisualTest.t))
with app.app_context():
tag = Tag.query.all()[0] # type: Tag
assert tag.device_id == 3, 'Tag should be linked to the first device'
@pytest.mark.mvp @pytest.mark.mvp
def test_snapshot_tag_inner_tag_mismatch_between_tags_and_hid( def test_snapshot_tag_inner_tag_mismatch_between_tags_and_hid(
user: UserClient, tag_id: str user: UserClient, tag_id: str
@ -1311,5 +1299,42 @@ def test_snapshot_check_tests_lite(user: UserClient):
bodyLite, res = user.post(snapshot_lite, uri="/api/inventory/") bodyLite, res = user.post(snapshot_lite, uri="/api/inventory/")
assert res.status_code == 201 assert res.status_code == 201
SnapshotsLog.query.all() assert SnapshotsLog.query.count() == 1
m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one()
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_placeholder(user: UserClient):
"""This check the structure of one placeholder generated automatically by a snapshot"""
snapshot_lite = file_json(
'test_lite/2022-4-13-19-5_user@dhub.com_b27dbf43-b88a-4505-ae27-10de5a95919e.json'
)
bodyLite, res = user.post(snapshot_lite, uri="/api/inventory/")
assert res.status_code == 201
dev = m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one() dev = m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one()
assert dev.placeholder is None
assert dev.binding.phid == '12'
assert len(dev.binding.device.components) == 11
assert len(dev.components) == 11
assert dev.binding.device.placeholder == dev.binding
assert dev.components != dev.binding.device.components
assert dev.binding.device.actions == []
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_placeholder_actions(user: UserClient):
"""This test the actions of a placeholder of one snapshot"""
s = yaml2json('erase-sectors.snapshot')
snap1, _ = user.post(s, res=Snapshot)
dev = m.Device.query.filter_by(id=snap1['device']['id']).one()
assert dev.placeholder is None
assert dev.binding.phid == '4'
assert len(dev.binding.device.components) == 3
assert len(dev.components) == 3
assert dev.binding.device.placeholder == dev.binding
assert dev.components != dev.binding.device.components
assert dev.binding.device.actions == []
assert len(dev.components[0].actions) == 3
assert len(dev.binding.device.components[0].actions) == 0

View File

@ -92,7 +92,7 @@ def test_wb11_to_wb11_with_uuid_api(user: UserClient):
db_snapthot = Snapshot.query.one() db_snapthot = Snapshot.query.one()
device = db_snapthot.device device = db_snapthot.device
assert Computer.query.count() == 1 assert Computer.query.count() == 2
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert device.system_uuid is None assert device.system_uuid is None
@ -106,9 +106,13 @@ def test_wb11_to_wb11_with_uuid_api(user: UserClient):
snapshot_11['debug']['lshw']['configuration']['uuid'] snapshot_11['debug']['lshw']['configuration']['uuid']
== '364ee69c-9c82-9cb1-2111-88ae1da6f3d0' == '364ee69c-9c82-9cb1-2111-88ae1da6f3d0'
) )
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -123,9 +127,13 @@ def test_wb11_with_uuid_to_wb11_api(user: UserClient):
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003' snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
# insert the same computer with wb11 with hid and with uuid, (new version) # insert the same computer with wb11 with hid and with uuid, (new version)
@ -133,9 +141,13 @@ def test_wb11_with_uuid_to_wb11_api(user: UserClient):
assert 'debug' not in snapshot_11 assert 'debug' not in snapshot_11
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -150,9 +162,13 @@ def test_wb11_with_uuid_to_wb11_without_hid_api(user: UserClient):
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003' snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
# insert the same computer with wb11 with hid and with uuid, (new version) # insert the same computer with wb11 with hid and with uuid, (new version)
@ -162,7 +178,7 @@ def test_wb11_with_uuid_to_wb11_without_hid_api(user: UserClient):
snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']} snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']}
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1 assert Computer.query.count() == 2
@pytest.mark.mvp @pytest.mark.mvp
@ -186,7 +202,7 @@ def test_wb11_to_wb11_with_uuid_form(user3: UserClientFlask):
db_snapthot = Snapshot.query.one() db_snapthot = Snapshot.query.one()
device = db_snapthot.device device = db_snapthot.device
assert Computer.query.count() == 1 assert Computer.query.count() == 2
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert device.system_uuid is None assert device.system_uuid is None
@ -203,9 +219,13 @@ def test_wb11_to_wb11_with_uuid_form(user3: UserClientFlask):
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -231,9 +251,13 @@ def test_wb11_with_uuid_to_wb11_form(user3: UserClientFlask):
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
# insert the same computer with wb11 with hid and with uuid, (new version) # insert the same computer with wb11 with hid and with uuid, (new version)
@ -248,9 +272,13 @@ def test_wb11_with_uuid_to_wb11_form(user3: UserClientFlask):
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -276,9 +304,13 @@ def test_wb11_with_uuid_to_wb11_without_hid_form(user3: UserClientFlask):
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
# insert the same computer with wb11 with hid and with uuid, (new version) # insert the same computer with wb11 with hid and with uuid, (new version)
@ -295,7 +327,7 @@ def test_wb11_with_uuid_to_wb11_without_hid_form(user3: UserClientFlask):
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
@pytest.mark.mvp @pytest.mark.mvp
@ -305,16 +337,24 @@ def test_wb11_to_wblite_api(user: UserClient):
# insert computer with wb11 with hid and without uuid, (old version) # insert computer with wb11 with hid and without uuid, (old version)
snapshot_11 = conftest.file_json('system_uuid3.json') snapshot_11 = conftest.file_json('system_uuid3.json')
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None assert device.system_uuid is None
snapshot_lite = conftest.file_json('system_uuid2.json') snapshot_lite = conftest.file_json('system_uuid2.json')
user.post(snapshot_lite, uri="/api/inventory/") user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -324,16 +364,24 @@ def test_wblite_to_wb11_api(user: UserClient):
snapshot_lite = conftest.file_json('system_uuid2.json') snapshot_lite = conftest.file_json('system_uuid2.json')
user.post(snapshot_lite, uri="/api/inventory/") user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
snapshot_11 = conftest.file_json('system_uuid3.json') snapshot_11 = conftest.file_json('system_uuid3.json')
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -354,9 +402,13 @@ def test_wb11_to_wblite_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None assert device.system_uuid is None
file_name = 'system_uuid2.json' file_name = 'system_uuid2.json'
@ -369,9 +421,13 @@ def test_wb11_to_wblite_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -392,9 +448,13 @@ def test_wblite_to_wb11_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
file_name = 'system_uuid3.json' file_name = 'system_uuid3.json'
@ -407,9 +467,13 @@ def test_wblite_to_wb11_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -419,17 +483,25 @@ def test_wblite_to_wblite_api(user: UserClient):
snapshot_lite = conftest.file_json('system_uuid2.json') snapshot_lite = conftest.file_json('system_uuid2.json')
user.post(snapshot_lite, uri="/api/inventory/") user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
snapshot_lite = conftest.file_json('system_uuid2.json') snapshot_lite = conftest.file_json('system_uuid2.json')
snapshot_lite['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003' snapshot_lite['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
user.post(snapshot_lite, uri="/api/inventory/") user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -450,9 +522,13 @@ def test_wblite_to_wblite_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
file_name = 'system_uuid2.json' file_name = 'system_uuid2.json'
@ -466,9 +542,13 @@ def test_wblite_to_wblite_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -479,9 +559,13 @@ def test_wb11_to_wb11_duplicity_api(user: UserClient):
# insert computer with wb11 with hid and without uuid, (old version) # insert computer with wb11 with hid and without uuid, (old version)
snapshot_11 = conftest.file_json('system_uuid3.json') snapshot_11 = conftest.file_json('system_uuid3.json')
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None assert device.system_uuid is None
snapshot_11 = conftest.file_json('system_uuid3.json') snapshot_11 = conftest.file_json('system_uuid3.json')
@ -489,7 +573,7 @@ def test_wb11_to_wb11_duplicity_api(user: UserClient):
components = [x for x in snapshot_11['components'] if x['type'] != 'NetworkAdapter'] components = [x for x in snapshot_11['components'] if x['type'] != 'NetworkAdapter']
snapshot_11['components'] = components snapshot_11['components'] = components
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 2 assert Computer.query.count() == 4
for c in Computer.query.all(): for c in Computer.query.all():
assert 'laptop-acer-aohappy-lusea0d010038879a01601' in c.hid assert 'laptop-acer-aohappy-lusea0d010038879a01601' in c.hid
assert c.system_uuid is None assert c.system_uuid is None
@ -512,9 +596,13 @@ def test_wb11_to_wb11_duplicity_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None assert device.system_uuid is None
snapshot_11 = conftest.file_json('system_uuid3.json') snapshot_11 = conftest.file_json('system_uuid3.json')
@ -530,10 +618,12 @@ def test_wb11_to_wb11_duplicity_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 2
for c in Computer.query.all(): assert Computer.query.count() == 4
assert 'laptop-acer-aohappy-lusea0d010038879a01601' in c.hid for device in Computer.query.all():
assert c.system_uuid is None if device.binding:
assert 'laptop-acer-aohappy-lusea0d010038879a01601' in device.hid
assert device.system_uuid is None
@pytest.mark.mvp @pytest.mark.mvp
@ -543,9 +633,13 @@ def test_wb11_smbios_2_5_api(user: UserClient):
# insert computer with wb11 with hid and without uuid, (old version) # insert computer with wb11 with hid and without uuid, (old version)
snapshot_11 = conftest.file_json('system_uuid4.json') snapshot_11 = conftest.file_json('system_uuid4.json')
user.post(snapshot_11, res=Snapshot) user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None assert device.system_uuid is None
@ -566,9 +660,13 @@ def test_wb11_smbios_2_5_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None assert device.system_uuid is None
@ -580,9 +678,13 @@ def test_wblite_smbios_2_5_api(user: UserClient):
snapshot_lite = conftest.file_json('system_uuid2.json') snapshot_lite = conftest.file_json('system_uuid2.json')
snapshot_lite['data']['lshw']['capabilities']['smbios-3.0'] = 'SMBIOS version 2.5' snapshot_lite['data']['lshw']['capabilities']['smbios-3.0'] = 'SMBIOS version 2.5'
user.post(snapshot_lite, uri="/api/inventory/") user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
@ -604,7 +706,11 @@ def test_wblite_smbios_2_5_form(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
user3.post(uri, data=data, content_type="multipart/form-data") user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1 assert Computer.query.count() == 2
device = Computer.query.one() for device in Computer.query.all():
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0' if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0' assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'

View File

@ -2,26 +2,29 @@ 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 flask import g
from pytest import raises from pytest import raises
from teal.db import MultipleResourcesFound, ResourceNotFound, UniqueViolation, DBError from teal.db import DBError, MultipleResourcesFound, ResourceNotFound, UniqueViolation
from teal.marshmallow import ValidationError from teal.marshmallow import ValidationError
from ereuse_devicehub.client import UserClient, Client from ereuse_devicehub.client import Client, UserClient
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
from ereuse_devicehub.resources.enums import ComputerChassis from ereuse_devicehub.resources.enums import ComputerChassis
from ereuse_devicehub.resources.tag import Tag from ereuse_devicehub.resources.tag import Tag
from ereuse_devicehub.resources.tag.view import CannotCreateETag, LinkedToAnotherDevice, \ from ereuse_devicehub.resources.tag.view import (
TagNotLinked CannotCreateETag,
LinkedToAnotherDevice,
TagNotLinked,
)
from ereuse_devicehub.resources.user.models import User
from tests import conftest from tests import conftest
from tests.conftest import yaml2json, json_encode from tests.conftest import json_encode, yaml2json
@pytest.mark.mvp @pytest.mark.mvp
@ -29,7 +32,9 @@ from tests.conftest import yaml2json, json_encode
def test_create_tag(user: UserClient): def test_create_tag(user: UserClient):
"""Creates a tag specifying a custom organization.""" """Creates a tag specifying a custom organization."""
org = Organization(name='bar', tax_id='bartax') org = Organization(name='bar', tax_id='bartax')
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.one()
@ -44,7 +49,9 @@ def test_create_tag(user: UserClient):
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() 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']) tag = Tag(id='bar', owner_id=user.user['id'])
@ -64,7 +71,9 @@ 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() 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)
@ -89,7 +98,9 @@ def test_delete_tags(user: UserClient, client: Client):
# Delete Tag UnNamed # Delete Tag UnNamed
org = Organization(name='bar', tax_id='bartax') org = Organization(name='bar', tax_id='bartax')
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.all()[-1] tag = Tag.query.all()[-1]
@ -106,7 +117,9 @@ def test_delete_tags(user: UserClient, client: Client):
def test_create_tag_default_org(user: UserClient): def test_create_tag_default_org(user: UserClient):
"""Creates a tag using the default organization.""" """Creates a tag using the default organization."""
tag = Tag(id='foo-1', owner_id=user.user['id']) tag = Tag(id='foo-1', owner_id=user.user['id'])
assert not tag.org_id, 'org-id is set as default value so it should only load on flush' assert (
not tag.org_id
), 'org-id is set as default value so it should only load on flush'
# We don't want the organization to load, or it would make this # We don't want the organization to load, or it would make this
# object, from transient to new (added to session) # object, from transient to new (added to session)
assert 'org' not in vars(tag), 'Organization should not have been loaded' assert 'org' not in vars(tag), 'Organization should not have been loaded'
@ -188,7 +201,9 @@ def test_tag_get_device_from_tag_endpoint(app: Devicehub, user: UserClient):
# Create a pc with a tag # Create a pc with a tag
g.user = User.query.one() 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)
db.session.add(pc) db.session.add(pc)
db.session.commit() db.session.commit()
@ -213,7 +228,9 @@ def test_tag_get_device_from_tag_endpoint_no_tag(user: UserClient):
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: UserClient, user2: UserClient, client: Client): def test_tag_get_device_from_tag_endpoint_multiple_tags(
app: Devicehub, user: UserClient, user2: UserClient, client: Client
):
"""As above, but when there are two tags with the secondary ID, the """As above, but when there are two tags with the secondary ID, the
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.
@ -232,8 +249,12 @@ def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: Us
tag1 = Tag.from_an_id('foo').filter_by(owner_id=user.user['id']).one() tag1 = Tag.from_an_id('foo').filter_by(owner_id=user.user['id']).one()
tag2 = Tag.from_an_id('foo').filter_by(owner_id=user2.user['id']).one() tag2 = Tag.from_an_id('foo').filter_by(owner_id=user2.user['id']).one()
pc1 = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']) pc1 = Desktop(
pc2 = Desktop(serial_number='sn2', chassis=ComputerChassis.Tower, owner_id=user2.user['id']) serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']
)
pc2 = Desktop(
serial_number='sn2', chassis=ComputerChassis.Tower, owner_id=user2.user['id']
)
pc1.tags.add(tag1) pc1.tags.add(tag1)
pc2.tags.add(tag2) pc2.tags.add(tag2)
db.session.add(pc1) db.session.add(pc1)
@ -266,7 +287,17 @@ def test_tag_create_etags_cli(app: Devicehub, user: UserClient):
# todo what happens to organization? # todo what happens to organization?
owner_id = user.user['id'] owner_id = user.user['id']
runner = app.test_cli_runner() runner = app.test_cli_runner()
args = ('tag', 'add', '-p', 'https://t.ereuse.org', '-s', 'foo', 'DT-BARBAR', '-u', owner_id) args = (
'tag',
'add',
'-p',
'https://t.ereuse.org',
'-s',
'foo',
'DT-BARBAR',
'-u',
owner_id,
)
runner.invoke(*args) runner.invoke(*args)
with app.app_context(): with app.app_context():
tag = Tag.query.one() # type: Tag tag = Tag.query.one() # type: Tag
@ -284,7 +315,11 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
with app.app_context(): with app.app_context():
g.user = User.query.one() 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)
db.session.commit() db.session.commit()
desktop_id = desktop.id desktop_id = desktop.id
@ -330,12 +365,20 @@ 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 'foo' in [x['id'] for x in device['tags']] desktop = Device.query.filter_by(
assert 'bar' in [x.get('secondary') for x in device['tags']] devicehub_id=snapshot['device']['devicehubID']
).one()
assert [] == [x['id'] for x in device['tags']]
assert 'foo' in [x.id for x in desktop.binding.device.tags]
assert 'bar' in [x.secondary for x in desktop.binding.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
r, _ = user.get(res=Device, query=[('search', 'bar'), ('filter', {'type': ['Computer']})]) r, _ = user.get(
res=Device, query=[('search', 'bar'), ('filter', {'type': ['Computer']})]
)
assert len(r['items']) == 1 assert len(r['items']) == 1
@ -359,19 +402,24 @@ def test_tag_multiple_secondary_org(user: UserClient):
@pytest.mark.mvp @pytest.mark.mvp
def test_create_num_regular_tags(user: UserClient, requests_mock: requests_mock.mocker.Mocker): def test_create_num_regular_tags(
user: UserClient, requests_mock: requests_mock.mocker.Mocker
):
"""Create regular tags. This is done using a tag provider that """Create regular tags. This is done using a tag provider that
returns IDs. These tags are printable. returns IDs. These tags are printable.
""" """
requests_mock.post('https://example.com/', requests_mock.post(
'https://example.com/',
# request # request
request_headers={ request_headers={
'Authorization': 'Basic {}'.format(DevicehubClient.encode_token( 'Authorization': 'Basic {}'.format(
'52dacef0-6bcb-4919-bfed-f10d2c96ecee')) DevicehubClient.encode_token('52dacef0-6bcb-4919-bfed-f10d2c96ecee')
)
}, },
# response # response
json=['tag1id', 'tag2id'], json=['tag1id', 'tag2id'],
status_code=201) status_code=201,
)
data, _ = user.post({}, res=Tag, query=[('num', 2)]) data, _ = user.post({}, res=Tag, query=[('num', 2)])
assert data['items'][0]['id'] == 'tag1id' assert data['items'][0]['id'] == 'tag1id'
assert data['items'][0]['printable'], 'Tags made this way are printable' assert data['items'][0]['printable'], 'Tags made this way are printable'
@ -380,28 +428,37 @@ def test_create_num_regular_tags(user: UserClient, requests_mock: requests_mock.
@pytest.mark.mvp @pytest.mark.mvp
def test_get_tags_endpoint(user: UserClient, app: Devicehub, def test_get_tags_endpoint(
requests_mock: requests_mock.mocker.Mocker): user: UserClient, app: Devicehub, requests_mock: requests_mock.mocker.Mocker
):
"""Performs GET /tags after creating 3 tags, 2 printable and one """Performs GET /tags after creating 3 tags, 2 printable and one
not. Only the printable ones are returned. not. Only the printable ones are returned.
""" """
# Prepare test # Prepare test
with app.app_context(): with app.app_context():
org = Organization(name='bar', tax_id='bartax') org = Organization(name='bar', tax_id='bartax')
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()
assert not tag.printable assert not tag.printable
requests_mock.post('https://example.com/', requests_mock.post(
'https://example.com/',
# request # request
request_headers={ request_headers={
'Authorization': 'Basic {}'.format(DevicehubClient.encode_token( 'Authorization': 'Basic {}'.format(
'52dacef0-6bcb-4919-bfed-f10d2c96ecee')) DevicehubClient.encode_token('52dacef0-6bcb-4919-bfed-f10d2c96ecee')
)
}, },
# response # response
json=['tag1id', 'tag2id'], json=['tag1id', 'tag2id'],
status_code=201) status_code=201,
)
user.post({}, res=Tag, query=[('num', 2)]) user.post({}, res=Tag, query=[('num', 2)])
# Test itself # Test itself
@ -421,7 +478,9 @@ def test_get_tag_permissions(app: Devicehub, user: UserClient, user2: UserClient
# Create a pc with a tag # Create a pc with a tag
g.user = User.query.all()[0] 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)
db.session.add(pc) db.session.add(pc)
db.session.commit() db.session.commit()
@ -432,5 +491,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']) == 2 assert len(computer['items']) == 1
assert len(computer2['items']) == 0 assert len(computer2['items']) == 0

View File

@ -1,17 +1,21 @@
"""Tests that emulates the behaviour of a WorkbenchServer.""" """Tests that emulates the behaviour of a WorkbenchServer."""
import json import json
import math
import pathlib import pathlib
import math
import pytest import pytest
from ereuse_devicehub.client import UserClient from ereuse_devicehub.client import UserClient
from ereuse_devicehub.resources.action import models as em from ereuse_devicehub.resources.action import models as em
from ereuse_devicehub.resources.action.models import RateComputer, BenchmarkProcessor, BenchmarkRamSysbench from ereuse_devicehub.resources.action.models import (
BenchmarkProcessor,
BenchmarkRamSysbench,
RateComputer,
)
from ereuse_devicehub.resources.device.exceptions import NeedsId from ereuse_devicehub.resources.device.exceptions import NeedsId
from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.device.models import Device
from ereuse_devicehub.resources.tag.model import Tag from ereuse_devicehub.resources.tag.model import Tag
from tests.conftest import file, file_workbench, yaml2json, json_encode from tests.conftest import file, file_workbench, json_encode, yaml2json
@pytest.mark.mvp @pytest.mark.mvp
@ -22,10 +26,9 @@ def test_workbench_server_condensed(user: UserClient):
""" """
s = yaml2json('workbench-server-1.snapshot') s = yaml2json('workbench-server-1.snapshot')
s['device']['actions'].append(yaml2json('workbench-server-2.stress-test')) s['device']['actions'].append(yaml2json('workbench-server-2.stress-test'))
s['components'][4]['actions'].extend(( s['components'][4]['actions'].extend(
yaml2json('workbench-server-3.erase'), (yaml2json('workbench-server-3.erase'), yaml2json('workbench-server-4.install'))
yaml2json('workbench-server-4.install') )
))
s['components'][5]['actions'].append(yaml2json('workbench-server-3.erase')) s['components'][5]['actions'].append(yaml2json('workbench-server-3.erase'))
# Create tags # Create tags
for t in s['device']['tags']: for t in s['device']['tags']:
@ -34,7 +37,7 @@ def test_workbench_server_condensed(user: UserClient):
snapshot, _ = user.post(res=em.Snapshot, data=json_encode(s)) snapshot, _ = user.post(res=em.Snapshot, data=json_encode(s))
pc_id = snapshot['device']['id'] pc_id = snapshot['device']['id']
cpu_id = snapshot['components'][3]['id'] cpu_id = snapshot['components'][3]['id']
ssd_id= snapshot['components'][4]['id'] ssd_id = snapshot['components'][4]['id']
hdd_id = snapshot['components'][5]['id'] hdd_id = snapshot['components'][5]['id']
actions = snapshot['actions'] actions = snapshot['actions']
assert {(action['type'], action['device']) for action in actions} == { assert {(action['type'], action['device']) for action in actions} == {
@ -60,8 +63,13 @@ def test_workbench_server_condensed(user: UserClient):
assert device['processorModel'] == device['components'][3]['model'] == 'p1-1ml' assert device['processorModel'] == device['components'][3]['model'] == 'p1-1ml'
assert device['ramSize'] == 2048, 'There are 3 RAM: 2 x 1024 and 1 None sizes' assert device['ramSize'] == 2048, 'There are 3 RAM: 2 x 1024 and 1 None sizes'
# 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 any([ac['type'] in [BenchmarkProcessor.t, BenchmarkRamSysbench.t] for ac in device['actions']]) assert any(
assert 'tag1' in [x['id'] for x in device['tags']] [
ac['type'] in [BenchmarkProcessor.t, BenchmarkRamSysbench.t]
for ac in device['actions']
]
)
assert 'tag1' not in [x['id'] for x in device['tags']]
@pytest.mark.xfail(reason='Functionality not yet developed.') @pytest.mark.xfail(reason='Functionality not yet developed.')
@ -136,7 +144,10 @@ def test_real_hp_11(user: UserClient):
s = file('real-hp.snapshot.11') s = file('real-hp.snapshot.11')
snapshot, _ = user.post(res=em.Snapshot, data=s) snapshot, _ = user.post(res=em.Snapshot, data=s)
pc = snapshot['device'] pc = snapshot['device']
assert pc['hid'] == 'desktop-hewlett-packard-hp_compaq_8100_elite_sff-czc0408yjg-6c:62:6d:81:22:9f' assert (
pc['hid']
== 'desktop-hewlett-packard-hp_compaq_8100_elite_sff-czc0408yjg-6c:62:6d:81:22:9f'
)
assert pc['chassis'] == 'Tower' assert pc['chassis'] == 'Tower'
assert set(e['type'] for e in snapshot['actions']) == { assert set(e['type'] for e in snapshot['actions']) == {
'BenchmarkDataStorage', 'BenchmarkDataStorage',
@ -146,7 +157,7 @@ def test_real_hp_11(user: UserClient):
'BenchmarkRamSysbench', 'BenchmarkRamSysbench',
'StressTest', 'StressTest',
'TestBios', 'TestBios',
'VisualTest' 'VisualTest',
} }
assert len(list(e['type'] for e in snapshot['actions'])) == 8 assert len(list(e['type'] for e in snapshot['actions'])) == 8
@ -168,7 +179,6 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
"""Checks the values of the device, components, """Checks the values of the device, components,
actions and their relationships of a real pc. actions and their relationships of a real pc.
""" """
# import pdb; pdb.set_trace()
s = file('real-eee-1001pxd.snapshot.11') s = file('real-eee-1001pxd.snapshot.11')
snapshot, _ = user.post(res=em.Snapshot, data=s) snapshot, _ = user.post(res=em.Snapshot, data=s)
pc, _ = user.get(res=Device, item=snapshot['device']['devicehubID']) pc, _ = user.get(res=Device, item=snapshot['device']['devicehubID'])
@ -177,22 +187,32 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
assert pc['model'] == '1001pxd' assert pc['model'] == '1001pxd'
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 (
assert len(pc['tags']) == 1 pc['hid']
assert pc['networkSpeeds'] == [100, 0], 'Although it has WiFi we do not know the speed' == 'laptop-asustek_computer_inc-1001pxd-b8oaas048286-14:da:e9:42:f6:7c'
)
assert len(pc['tags']) == 0
assert pc['networkSpeeds'] == [
100,
0,
], 'Although it has WiFi we do not know the speed'
# assert pc['actions'][0]['appearanceRange'] == 'A' # assert pc['actions'][0]['appearanceRange'] == 'A'
# assert pc['actions'][0]['functionalityRange'] == 'B' # assert pc['actions'][0]['functionalityRange'] == 'B'
# TODO add appearance and functionality Range in device[rate] # TODO add appearance and functionality Range in device[rate]
components = snapshot['components'] components = snapshot['components']
wifi = components[0] wifi = components[0]
assert wifi['hid'] == 'networkadapter-qualcomm_atheros-' \ assert (
wifi['hid'] == 'networkadapter-qualcomm_atheros-'
'ar9285_wireless_network_adapter-74_2f_68_8b_fd_c8' 'ar9285_wireless_network_adapter-74_2f_68_8b_fd_c8'
)
assert wifi['serialNumber'] == '74:2f:68:8b:fd:c8' assert wifi['serialNumber'] == '74:2f:68:8b:fd:c8'
assert wifi['wireless'] assert wifi['wireless']
eth = components[1] eth = components[1]
assert eth['hid'] == 'networkadapter-qualcomm_atheros-' \ assert (
eth['hid'] == 'networkadapter-qualcomm_atheros-'
'ar8152_v2_0_fast_ethernet-14_da_e9_42_f6_7c' 'ar8152_v2_0_fast_ethernet-14_da_e9_42_f6_7c'
)
assert eth['speed'] == 100 assert eth['speed'] == 100
assert not eth['wireless'] assert not eth['wireless']
cpu = components[2] cpu = components[2]
@ -219,7 +239,10 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
assert em.Snapshot.t in action_types assert em.Snapshot.t in action_types
assert len(actions) == 6 assert len(actions) == 6
gpu = components[3] gpu = components[3]
assert gpu['model'] == 'atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller' assert (
gpu['model']
== 'atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller'
)
assert gpu['manufacturer'] == 'intel corporation' assert gpu['manufacturer'] == 'intel corporation'
assert gpu['memory'] == 256 assert gpu['memory'] == 256
gpu, _ = user.get(res=Device, item=gpu['devicehubID']) gpu, _ = user.get(res=Device, item=gpu['devicehubID'])
@ -285,25 +308,26 @@ SNAPSHOTS_NEED_ID = {
'nox.snapshot.json', 'nox.snapshot.json',
'ecs-computers.snapshot.json', 'ecs-computers.snapshot.json',
'custom.snapshot.json', 'custom.snapshot.json',
'ecs-2.snapshot.json' 'ecs-2.snapshot.json',
} }
"""Snapshots that do not generate HID requiring a custom ID.""" """Snapshots that do not generate HID requiring a custom ID."""
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.parametrize('file', @pytest.mark.parametrize(
(pytest.param(f, id=f.name) 'file',
for f in pathlib.Path(__file__).parent.joinpath('workbench_files').iterdir()) (
) pytest.param(f, id=f.name)
for f in pathlib.Path(__file__).parent.joinpath('workbench_files').iterdir()
),
)
def test_workbench_fixtures(file: pathlib.Path, user: UserClient): def test_workbench_fixtures(file: pathlib.Path, user: UserClient):
"""Uploads the Snapshot files Workbench tests generate. """Uploads the Snapshot files Workbench tests generate.
Keep this files up to date with the Workbench version. Keep this files up to date with the Workbench version.
""" """
s = json.load(file.open()) s = json.load(file.open())
user.post(res=em.Snapshot, user.post(res=em.Snapshot, data=json_encode(s), status=201)
data=json_encode(s),
status=201)
@pytest.mark.mvp @pytest.mark.mvp