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:
- repo: https://github.com/psf/black
rev: 22.1.0
rev: 22.6.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 5.9.3
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8

View File

@ -7,6 +7,7 @@ ml).
## testing
- [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] #314 bugs create placeholder from lot.
- [fixed] #317 bugs about exports placeholders.

View File

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

View File

@ -136,9 +136,13 @@ class FilterForm(FlaskForm):
if self.lot_id:
self.lot = self.lots.filter(Lot.id == self.lot_id).one()
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:
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:
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:
dev = Placeholder.query.filter(
Placeholder.phid == self.phid.data, Device.owner == g.user
Placeholder.phid == self.phid.data, Placeholder.owner == g.user
).first()
if dev:
msg = "Sorry, exist one snapshot device with this HID"
@ -564,6 +568,7 @@ class NewDeviceForm(FlaskForm):
'id_device_supplier': self.id_device_supplier.data,
'info': self.info.data,
'pallet': self.pallet.data,
'is_abstract': False,
}
)
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.info = self.info.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.manufacturer = self.manufacturer.data
self._obj.serial_number = self.serial_number.data
@ -1551,6 +1557,7 @@ class UploadPlaceholderForm(FlaskForm):
'id_device_supplier': data['Id device Supplier'][i],
'pallet': data['Pallet'][i],
'info': data['Info'][i],
'is_abstract': False,
}
snapshot_json = schema.load(json_snapshot)
@ -1602,3 +1609,43 @@ class EditPlaceholderForm(FlaskForm):
db.session.commit()
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 logging
import os
@ -19,6 +20,7 @@ from ereuse_devicehub.db import db
from ereuse_devicehub.inventory.forms import (
AdvancedSearchForm,
AllocateForm,
BindingForm,
DataWipeForm,
EditTransferForm,
FilterForm,
@ -36,7 +38,12 @@ from ereuse_devicehub.inventory.forms import (
from ereuse_devicehub.labels.forms import PrintLabelsForm
from ereuse_devicehub.parser.models import PlaceholdersLog, SnapshotsLog
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.enums import SnapshotSoftware
from ereuse_devicehub.resources.hash_reports import insert_hash
@ -129,6 +136,7 @@ class AdvancedSearchView(DeviceListMixin):
class DeviceDetailView(GenericMixin):
methods = ['GET', 'POST']
decorators = [login_required]
template_name = 'inventory/device_detail.html'
@ -140,15 +148,147 @@ class DeviceDetailView(GenericMixin):
.one()
)
form_binding = BindingForm(device=device)
self.context.update(
{
'device': device,
'placeholder': device.binding or device.placeholder,
'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)
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):
methods = ['GET', 'POST']
decorators = [login_required]
@ -993,3 +1133,9 @@ devices.add_url_rule(
devices.add_url_rule(
'/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:
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
if data.get('end_time') and data.get('start_time'):
@ -930,6 +933,10 @@ class Delete(ActionWithMultipleDevicesCheckingOwner):
for dev in data['devices']:
if dev.last_action_trading is None:
dev.active = False
if dev.binding:
dev.binding.device.active = False
if dev.placeholder:
dev.placeholder.device.active = False
class Migrate(ActionWithMultipleDevices):

View File

@ -215,6 +215,24 @@ class Device(Thing):
def reverse_actions(self) -> list:
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
def actions(self) -> list:
"""All the actions where the device participated, including:
@ -411,7 +429,16 @@ class Device(Thing):
@property
def sid(self):
actions = [x for x in self.actions if x.t == 'Snapshot' and x.sid]
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]
if actions:
return actions[0].sid
@ -601,6 +628,16 @@ class Device(Thing):
args[POLYMORPHIC_ON] = cls.type
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):
actions = copy.copy(self.actions)
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"
info = db.Column(CIText())
info.comment = "more info of placeholders"
is_abstract = db.Column(Boolean, default=False)
id_device_supplier = db.Column(CIText())
id_device_supplier.comment = (
"Identification used for one supplier of one placeholders"
@ -845,7 +883,7 @@ class Placeholder(Thing):
)
device = db.relationship(
Device,
backref=backref('placeholder', lazy=True, uselist=False),
backref=backref('placeholder', lazy=True, cascade="all, delete-orphan", uselist=False),
primaryjoin=device_id == Device.id,
)
device_id.comment = "datas of the placeholder"
@ -861,6 +899,13 @@ class Placeholder(Thing):
primaryjoin=binding_id == Device.id,
)
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):
@ -1481,9 +1526,9 @@ def create_code_tag(mapper, connection, device):
"""
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)
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
from contextlib import suppress
from itertools import groupby
@ -87,6 +88,7 @@ class Sync:
# We only want to perform Add/Remove to not new components
actions = self.add_remove(db_device, not_new_components)
db_device.components = db_components
self.create_placeholder(db_device)
return db_device, actions
def execute_register_component(
@ -113,6 +115,7 @@ class Sync:
- A flag stating if the device is new or it already
existed in the DB.
"""
# if device.serial_number == 'b8oaas048286':
assert inspect(component).transient, 'Component should not be synced from DB'
# if not is a DataStorage, then need build a new one
if component.t in DEVICES_ALLOW_DUPLICITY:
@ -124,7 +127,7 @@ class Sync:
try:
if component.hid:
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()
assert isinstance(
db_component, Device
@ -183,18 +186,24 @@ class Sync:
if device.system_uuid:
with suppress(ResourceNotFound):
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()
# if no there are any Computer by uuid search by hid
if not db_device and device.hid:
with suppress(ResourceNotFound):
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()
elif device.hid:
with suppress(ResourceNotFound):
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()
if db_device and db_device.allocated:
@ -278,22 +287,40 @@ class Sync:
if hasattr(device, 'system_uuid') and device.system_uuid:
db_device.system_uuid = device.system_uuid
if device.placeholder and db_device.placeholder:
db_device.placeholder.pallet = device.placeholder.pallet
db_device.placeholder.info = device.placeholder.info
db_device.placeholder.id_device_supplier = (
device.placeholder.id_device_supplier
@staticmethod
def create_placeholder(device: Device):
"""If the device is new, we need create automaticaly a new placeholder"""
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)
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_device.image = device.image
db_device.brand = device.brand
db_device.generation = device.generation
db_device.variant = device.variant
db_device.version = device.version
db_device.width = device.width
db_device.height = device.height
db_device.depth = device.depth
db_device.weight = device.weight
db.session.add(c_placeholder)
db.session.add(component_placeholder)
placeholder = Placeholder(
device=dev_placeholder, binding=device, is_abstract=True
)
db.session.add(dev_placeholder)
db.session.add(placeholder)
@staticmethod
def add_remove(device: Computer, components: Set[Component]) -> OrderedSet:

View File

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

View File

@ -7,6 +7,7 @@ from citext import CIText
from flask import g
from sqlalchemy import TEXT
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from sqlalchemy_utils import LtreeType
from sqlalchemy_utils.types.ltree import LQUERY
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_id.comment = """The user that put the device in the lot."""
device = relationship(
'Device',
primaryjoin='Device.id == LotDevice.device_id',
)
class Path(db.Model):

View File

@ -53,6 +53,8 @@ function save_settings() {
data['logo'] = $("#logoCheck").prop('checked');
data['dhid'] = $("#dhidCheck").prop('checked');
data['sid'] = $("#sidCheck").prop('checked');
data['phid'] = $("#phidCheck").prop('checked');
data['tags'] = $("#tagsCheck").prop('checked');
data['qr'] = $("#qrCheck").prop('checked');
data['serial_number'] = $("#serialNumberCheck").prop('checked');
data['manufacturer'] = $("#manufacturerCheck").prop('checked');
@ -69,11 +71,13 @@ function load_settings() {
$("#qrCheck").prop('checked', data.qr);
$("#dhidCheck").prop('checked', data.dhid);
$("#sidCheck").prop('checked', data.sid);
$("#phidCheck").prop('checked', data.phid);
$("#tagsCheck").prop('checked', data.tags);
$("#serialNumberCheck").prop('checked', data.serial_number);
$("#manufacturerCheck").prop('checked', data.manufacturer);
$("#modelCheck").prop('checked', data.model);
if (data.logo) {
$("#logoCheck").prop('checked', data.sid);
// $("#logoCheck").prop('checked', data.sid);
previewLogo(data.logoImg);
$("#logoCheck").prop('checked', data.logo);
} else {
@ -89,6 +93,8 @@ function reset_settings() {
$("#qrCheck").prop('checked', true);
$("#dhidCheck").prop('checked', true);
$("#sidCheck").prop('checked', true);
$("#phidCheck").prop('checked', true);
$("#tagsCheck").prop('checked', false);
$("#serialNumberCheck").prop('checked', false);
$("#logoCheck").prop('checked', false);
$("#manufacturerCheck").prop('checked', false);
@ -135,6 +141,18 @@ function change_check() {
$(".sid").hide();
};
if ($("#phidCheck").prop('checked')) {
$(".phid").show();
} else {
$(".phid").hide();
};
if ($("#tagsCheck").prop('checked')) {
$(".tags").show();
} else {
$(".tags").hide();
};
if ($("#serialNumberCheck").prop('checked')) {
$(".serial_number").show();
} else {
@ -190,6 +208,12 @@ function printpdf() {
if ($("#sidCheck").prop('checked')) {
height_need += line;
};
if ($("#phidCheck").prop('checked')) {
height_need += line;
};
if ($("#tagsCheck").prop('checked')) {
height_need += line;
};
if ($("#serialNumberCheck").prop('checked')) {
height_need += line;
};
@ -248,6 +272,22 @@ function printpdf() {
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')) {
var sn = $(y).data('serial-number');
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 -->
<ul class="nav nav-tabs nav-tabs-bordered">
{% if placeholder %}
<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>
{% 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">
<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>
</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>
<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>
{% if device.placeholder %}(<a href="{{ url_for('inventory.device_edit', id=device.devicehub_id)}}">edit</a>){% endif %}
@ -204,6 +228,42 @@
{% endfor %}
</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>

View File

@ -401,6 +401,8 @@
<th scope="col">Select</th>
<th scope="col">Title</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">Lifecycle Status</th>
<th scope="col">Allocated Status</th>
@ -442,6 +444,12 @@
{{ dev.devicehub_id }}
</a>
</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>
{% for t in dev.tags | sort(attribute="id") %}
<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 '' }}"
data-manufacturer="{{ dev.manufacturer or '' }}"
data-model="{{ dev.model or '' }}"
data-tags="{{ dev.list_tags() }}"
data-phid="{{ dev.phid() }}"
data-sid="{{ dev.sid or '' }}">{{ dev.devicehub_id }}</b>
</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>
<b>{{ dev.sid or '' }}</b>
</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="col">
<div>
@ -125,10 +143,18 @@
<input class="form-check-input" name="dhid" type="checkbox" id="dhidCheck" checked="">
<label class="form-check-label" for="dhidCheck">Dhid</label>
</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">
<input class="form-check-input" name="sid" type="checkbox" id="sidCheck">
<label class="form-check-label" for="sidCheck">Sid</label>
</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">
<input class="form-check-input" name="serial_number" type="checkbox" id="serialNumberCheck">
<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)"
"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/lot/{lot_id}/upload-placeholder/',
'/inventory/placeholder-logs/',
'/inventory/unbinding/{phid}/',
'/inventory/binding/{dhid}/{phid}/',
'/labels/',
'/labels/add/',
'/labels/print',

View File

@ -347,7 +347,7 @@ def test_sync_execute_register_tag_linked_same_device():
pc.tags.add(Tag(id='foo'))
db_pc = Sync().execute_register(pc)
assert db_pc.id == orig_pc.id
assert len(db_pc.tags) == 2
assert len(db_pc.tags) == 1
for tag in db_pc.tags:
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)
assert res.status_code == 200
assert res2.status_code == 200
assert len(devices['items']) == 1
assert len(devices['items']) == 2
assert len(devices2['items']) == 0
@ -515,13 +515,13 @@ def test_get_devices_unassigned(user: UserClient):
devices, res = user.get(url, None)
assert res.status_code == 200
assert len(devices['items']) == 1
assert len(devices['items']) == 2
url = '/devices/?filter={"type":["Computer"]}&unassign=1'
devices, res = user.get(url, None)
assert res.status_code == 200
assert len(devices['items']) == 1
assert len(devices['items']) == 2
from ereuse_devicehub.resources.lot.models import Lot
device_id = devices['items'][0]['id']
@ -537,13 +537,13 @@ def test_get_devices_unassigned(user: UserClient):
devices, res = user.get(url, None)
assert res.status_code == 200
assert len(devices['items']) == 1
assert len(devices['items']) == 2
url = '/devices/?filter={"type":["Computer"]}&unassign=1'
devices, res = user.get(url, None)
assert res.status_code == 200
assert len(devices['items']) == 0
assert len(devices['items']) == 1
@pytest.mark.mvp
@ -580,7 +580,7 @@ def test_manufacturer_enforced():
def test_device_properties_format(app: Devicehub, user: UserClient):
user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
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, 't') == 'Netbook 1000h'
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.processor_model == 'intel atom cpu n270 @ 1.60ghz'
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'
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'
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, 's') == 'seagate 5SV4TQA6 152 GB'
@ -702,7 +702,7 @@ def test_hid_with_2networkadapters(app: Devicehub, user: UserClient):
laptop = devices['items'][0]
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
@ -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)
laptop = devices['items'][0]
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
@ -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)
devices, _ = user.get(res=d.Device)
laptops = [c for c in devices['items'] if c['type'] == 'Laptop']
assert len(laptops) == 2
hids = [h['hid'] for h in laptops]
assert len(laptops) == 4
hids = [laptops[0]['hid'], laptops[2]['hid']]
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']
assert all([h in proof_hid for h in hids])
# we drop all network cards
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)
devices, _ = user.get(res=d.Device)
laptops = [c for c in devices['items'] if c['type'] == 'Laptop']
assert len(laptops) == 3
hids = [h['hid'] for h in laptops]
assert len(laptops) == 4
hids = [laptops[0]['hid'], laptops[2]['hid']]
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']
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')
assert len(pc['actions']) == 3
assert len(pc['components']) == 3
assert pc['tags'][0]['id'] == pc['devicehubID']
@pytest.mark.mvp

View File

@ -508,12 +508,14 @@ def test_report_devices_stock_control(user: UserClient, user2: UserClient):
fixture_csv = list(obj_csv)
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[1] = export_csv[1][0].split(';')
export_csv[2] = export_csv[2][0].split(';')
fixture_csv[0] = fixture_csv[0][0].split(';')
fixture_csv[1] = fixture_csv[1][0].split(';')
fixture_csv[2] = fixture_csv[2][0].split(';')
# assert isinstance(
# 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
fixture_csv[1] = fixture_csv[1][:5] + fixture_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[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

View File

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

View File

@ -14,7 +14,7 @@ from ereuse_devicehub.client import UserClient, UserClientFlask
from ereuse_devicehub.db import db
from ereuse_devicehub.devicehub import Devicehub
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.user.models import User
from tests import conftest
@ -190,7 +190,7 @@ def test_inventory_with_device(user3: UserClientFlask):
assert status == '200 OK'
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
@ -203,7 +203,7 @@ def test_inventory_filter(user3: UserClientFlask):
assert status == '200 OK'
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
@ -310,7 +310,7 @@ def test_label_details(user3: UserClientFlask):
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_link_tag_to_device(user3: UserClientFlask):
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev = snap.device
dev = snap.device.binding.device
uri = '/labels/add/'
user3.get(uri)
@ -331,7 +331,7 @@ def test_link_tag_to_device(user3: UserClientFlask):
uri = '/inventory/tag/devices/add/'
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]
assert "tag1" in tags
@ -341,7 +341,7 @@ def test_link_tag_to_device(user3: UserClientFlask):
def test_unlink_tag_to_device(user3: UserClientFlask):
# create device
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev = snap.device
dev = snap.device.binding.device
# create tag
uri = '/labels/add/'
@ -379,9 +379,7 @@ def test_unlink_tag_to_device(user3: UserClientFlask):
}
user3.post(uri, data=data)
assert len(list(dev.tags)) == 1
tag = list(dev.tags)[0]
assert not tag.id == "tag1"
assert len(list(dev.tags)) == 0
@pytest.mark.mvp
@ -389,7 +387,7 @@ def test_unlink_tag_to_device(user3: UserClientFlask):
def test_print_labels(user3: UserClientFlask):
# create device
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
dev = snap.device
dev = snap.device.binding.device
# create tag
uri = '/labels/add/'
@ -411,7 +409,7 @@ def test_print_labels(user3: UserClientFlask):
uri = '/inventory/tag/devices/add/'
user3.post(uri, data=data)
assert len(list(dev.tags)) == 2
assert len(list(dev.tags)) == 1
uri = '/labels/print'
data = {
@ -423,7 +421,7 @@ def test_print_labels(user3: UserClientFlask):
assert status == '200 OK'
path = "/inventory/device/{}/".format(dev.devicehub_id)
assert path in body
assert "tag1" not in body
assert "tag1" in body
@pytest.mark.mvp
@ -708,7 +706,7 @@ def test_action_recycling(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
@ -721,15 +719,15 @@ def test_action_recycling(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Recycling",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -763,15 +761,15 @@ def test_action_use(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Use",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -786,15 +784,15 @@ def test_action_refurbish(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Refurbish",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -809,15 +807,15 @@ def test_action_management(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Management",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -832,7 +830,7 @@ def test_action_allocate(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01',
'end_time': '2000-06-01',
'end_users': 2,
@ -841,9 +839,9 @@ def test_action_allocate(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -858,18 +856,18 @@ def test_action_allocate_error_required(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Trade",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/allocate/add/'
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 = {
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/allocate/add/'
@ -891,7 +889,7 @@ def test_action_allocate_error_dates(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-06-01',
'end_time': '2000-01-01',
'end_users': 2,
@ -902,7 +900,7 @@ def test_action_allocate_error_dates(user3: UserClientFlask):
assert status == '200 OK'
assert 'Action Allocate error' 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
@ -919,7 +917,7 @@ def test_action_allocate_error_future_dates(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': start_time,
'end_time': end_time,
'end_users': 2,
@ -930,7 +928,7 @@ def test_action_allocate_error_future_dates(user3: UserClientFlask):
assert status == '200 OK'
assert 'Action Allocate error' 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
@ -945,7 +943,7 @@ def test_action_deallocate(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01',
'end_time': '2000-06-01',
'end_users': 2,
@ -954,22 +952,22 @@ def test_action_deallocate(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data)
assert dev.allocated_status.type == 'Allocate'
assert dev.binding.device.allocated_status.type == 'Allocate'
data = {
'csrf_token': generate_csrf(),
'type': "Deallocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01',
'end_time': '2000-06-01',
'end_users': 2,
}
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -984,7 +982,7 @@ def test_action_deallocate_error(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-05-01',
'end_time': '2000-06-01',
'end_users': 2,
@ -993,20 +991,20 @@ def test_action_deallocate_error(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data)
assert dev.allocated_status.type == 'Allocate'
assert dev.binding.device.allocated_status.type == 'Allocate'
data = {
'csrf_token': generate_csrf(),
'type': "Deallocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01',
'end_time': '2000-02-01',
'end_users': 2,
}
body, status = user3.post(uri, data=data)
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 '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(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-01',
'end_time': '2000-01-01',
'end_users': 2,
@ -1032,36 +1030,36 @@ def test_action_allocate_deallocate_error(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data)
assert dev.allocated_status.type == 'Allocate'
assert len(dev.actions) == 11
assert dev.binding.device.allocated_status.type == 'Allocate'
assert len(dev.binding.device.actions) == 1
data = {
'csrf_token': generate_csrf(),
'type': "Deallocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-02-01',
'end_time': '2000-02-01',
'end_users': 2,
}
body, status = user3.post(uri, data=data)
assert status == '200 OK'
assert dev.allocated_status.type == 'Deallocate'
assert len(dev.actions) == 12
assert dev.binding.device.allocated_status.type == 'Deallocate'
assert len(dev.binding.device.actions) == 2
# is not possible to do an allocate between an allocate and an deallocate
data = {
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-15',
'end_time': '2000-01-15',
'end_users': 2,
}
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 '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(),
'type': "Deallocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-15',
'end_time': '2000-01-15',
'end_users': 2,
}
user3.post(uri, data=data)
assert len(dev.actions) == 12
assert len(dev.binding.device.actions) == 2
@pytest.mark.mvp
@ -1091,7 +1089,7 @@ def test_action_allocate_deallocate_error2(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-10',
'end_users': 2,
}
@ -1099,25 +1097,25 @@ def test_action_allocate_deallocate_error2(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data)
assert len(dev.actions) == 11
assert len(dev.binding.device.actions) == 1
data = {
'csrf_token': generate_csrf(),
'type': "Deallocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-20',
'end_users': 2,
}
body, status = user3.post(uri, data=data)
assert status == '200 OK'
assert len(dev.actions) == 12
assert len(dev.binding.device.actions) == 2
data = {
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-02-10',
'end_users': 2,
}
@ -1125,40 +1123,40 @@ def test_action_allocate_deallocate_error2(user3: UserClientFlask):
uri = '/inventory/action/allocate/add/'
user3.post(uri, data=data)
assert len(dev.actions) == 13
assert len(dev.binding.device.actions) == 3
data = {
'csrf_token': generate_csrf(),
'type': "Deallocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-02-20',
'end_users': 2,
}
user3.post(uri, data=data)
assert len(dev.actions) == 14
assert len(dev.binding.device.actions) == 4
data = {
'csrf_token': generate_csrf(),
'type': "Allocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-25',
'end_users': 2,
}
user3.post(uri, data=data)
assert len(dev.actions) == 15
assert len(dev.binding.device.actions) == 5
data = {
'csrf_token': generate_csrf(),
'type': "Deallocate",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'start_time': '2000-01-27',
'end_users': 2,
}
user3.post(uri, data=data)
assert len(dev.actions) == 16
assert len(dev.binding.device.actions) == 6
@pytest.mark.mvp
@ -1173,15 +1171,15 @@ def test_action_toprepare(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "ToPrepare",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -1196,15 +1194,15 @@ def test_action_prepare(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Prepare",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -1219,15 +1217,15 @@ def test_action_torepair(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "ToRepair",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -1242,15 +1240,15 @@ def test_action_ready(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "Ready",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
}
uri = '/inventory/action/add/'
body, status = user3.post(uri, data=data)
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -1269,16 +1267,16 @@ def test_action_datawipe(user3: UserClientFlask):
'csrf_token': generate_csrf(),
'type': "DataWipe",
'severity': "Info",
'devices': "{}".format(dev.id),
'devices': "{}".format(dev.binding.device.id),
'document-file_name': file_upload,
}
uri = '/inventory/action/datawipe/add/'
body, status = user3.post(uri, data=data, content_type="multipart/form-data")
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 dev.devicehub_id in body
assert dev.binding.device.devicehub_id in body
@pytest.mark.mvp
@ -1618,7 +1616,6 @@ def test_export_snapshot_json(user3: UserClientFlask):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_add_placeholder_excel(user3: UserClientFlask):
uri = '/inventory/upload-placeholder/'
body, status = user3.get(uri)
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.placeholder.phid == 'ace'
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
@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
def test_snapshot_tag_inner_tag_mismatch_between_tags_and_hid(
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/")
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()
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()
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.system_uuid is None
@ -106,10 +106,14 @@ def test_wb11_to_wb11_with_uuid_api(user: UserClient):
snapshot_11['debug']['lshw']['configuration']['uuid']
== '364ee69c-9c82-9cb1-2111-88ae1da6f3d0'
)
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -123,20 +127,28 @@ def test_wb11_with_uuid_to_wb11_api(user: UserClient):
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
# insert the same computer with wb11 with hid and with uuid, (new version)
snapshot_11 = conftest.file_json('system_uuid3.json')
assert 'debug' not in snapshot_11
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -150,10 +162,14 @@ def test_wb11_with_uuid_to_wb11_without_hid_api(user: UserClient):
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
# insert the same computer with wb11 with hid and with uuid, (new version)
snapshot_11 = conftest.file_json('system_uuid3.json')
@ -162,7 +178,7 @@ def test_wb11_with_uuid_to_wb11_without_hid_api(user: UserClient):
snapshot_11['debug'] = {'lshw': snapshot_lite['data']['lshw']}
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1
assert Computer.query.count() == 2
@pytest.mark.mvp
@ -186,7 +202,7 @@ def test_wb11_to_wb11_with_uuid_form(user3: UserClientFlask):
db_snapthot = Snapshot.query.one()
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.system_uuid is None
@ -203,10 +219,14 @@ def test_wb11_to_wb11_with_uuid_form(user3: UserClientFlask):
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -231,10 +251,14 @@ def test_wb11_with_uuid_to_wb11_form(user3: UserClientFlask):
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
# insert the same computer with wb11 with hid and with uuid, (new version)
snapshot = conftest.file_json('system_uuid3.json')
@ -248,10 +272,14 @@ def test_wb11_with_uuid_to_wb11_form(user3: UserClientFlask):
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -276,10 +304,14 @@ def test_wb11_with_uuid_to_wb11_without_hid_form(user3: UserClientFlask):
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
# insert the same computer with wb11 with hid and with uuid, (new version)
snapshot_11 = conftest.file_json('system_uuid3.json')
@ -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")
assert Computer.query.count() == 1
assert Computer.query.count() == 2
@pytest.mark.mvp
@ -305,17 +337,25 @@ def test_wb11_to_wblite_api(user: UserClient):
# insert computer with wb11 with hid and without uuid, (old version)
snapshot_11 = conftest.file_json('system_uuid3.json')
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert device.system_uuid is None
assert Computer.query.count() == 2
for device in Computer.query.all():
if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None
snapshot_lite = conftest.file_json('system_uuid2.json')
user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -324,17 +364,25 @@ def test_wblite_to_wb11_api(user: UserClient):
snapshot_lite = conftest.file_json('system_uuid2.json')
user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
snapshot_11 = conftest.file_json('system_uuid3.json')
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -354,10 +402,14 @@ def test_wb11_to_wblite_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert device.system_uuid is None
assert Computer.query.count() == 2
for device in Computer.query.all():
if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None
file_name = 'system_uuid2.json'
snapshot_lite = conftest.file_json(file_name)
@ -369,10 +421,14 @@ def test_wb11_to_wblite_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -392,10 +448,14 @@ def test_wblite_to_wb11_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
file_name = 'system_uuid3.json'
snapshot_11 = conftest.file_json(file_name)
@ -407,10 +467,14 @@ def test_wblite_to_wb11_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -419,18 +483,26 @@ def test_wblite_to_wblite_api(user: UserClient):
snapshot_lite = conftest.file_json('system_uuid2.json')
user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
snapshot_lite = conftest.file_json('system_uuid2.json')
snapshot_lite['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -450,10 +522,14 @@ def test_wblite_to_wblite_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
file_name = 'system_uuid2.json'
snapshot_lite = conftest.file_json(file_name)
@ -466,10 +542,14 @@ def test_wblite_to_wblite_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -479,17 +559,21 @@ def test_wb11_to_wb11_duplicity_api(user: UserClient):
# insert computer with wb11 with hid and without uuid, (old version)
snapshot_11 = conftest.file_json('system_uuid3.json')
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert device.system_uuid is None
assert Computer.query.count() == 2
for device in Computer.query.all():
if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None
snapshot_11 = conftest.file_json('system_uuid3.json')
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
components = [x for x in snapshot_11['components'] if x['type'] != 'NetworkAdapter']
snapshot_11['components'] = components
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 2
assert Computer.query.count() == 4
for c in Computer.query.all():
assert 'laptop-acer-aohappy-lusea0d010038879a01601' in c.hid
assert c.system_uuid is None
@ -512,10 +596,14 @@ def test_wb11_to_wb11_duplicity_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert device.system_uuid is None
assert Computer.query.count() == 2
for device in Computer.query.all():
if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None
snapshot_11 = conftest.file_json('system_uuid3.json')
snapshot_11['uuid'] = '0973fda0-589a-11eb-ae93-0242ac130003'
@ -530,10 +618,12 @@ def test_wb11_to_wb11_duplicity_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 2
for c in Computer.query.all():
assert 'laptop-acer-aohappy-lusea0d010038879a01601' in c.hid
assert c.system_uuid is None
assert Computer.query.count() == 4
for device in Computer.query.all():
if device.binding:
assert 'laptop-acer-aohappy-lusea0d010038879a01601' in device.hid
assert device.system_uuid is None
@pytest.mark.mvp
@ -543,10 +633,14 @@ def test_wb11_smbios_2_5_api(user: UserClient):
# insert computer with wb11 with hid and without uuid, (old version)
snapshot_11 = conftest.file_json('system_uuid4.json')
user.post(snapshot_11, res=Snapshot)
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert device.system_uuid is None
assert Computer.query.count() == 2
for device in Computer.query.all():
if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None
@pytest.mark.mvp
@ -566,10 +660,14 @@ def test_wb11_smbios_2_5_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert device.system_uuid is None
assert Computer.query.count() == 2
for device in Computer.query.all():
if device.binding:
assert (
device.hid
== 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
)
assert device.system_uuid is None
@pytest.mark.mvp
@ -580,10 +678,14 @@ def test_wblite_smbios_2_5_api(user: UserClient):
snapshot_lite = conftest.file_json('system_uuid2.json')
snapshot_lite['data']['lshw']['capabilities']['smbios-3.0'] = 'SMBIOS version 2.5'
user.post(snapshot_lite, uri="/api/inventory/")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'
@pytest.mark.mvp
@ -604,7 +706,11 @@ def test_wblite_smbios_2_5_form(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Computer.query.count() == 1
device = Computer.query.one()
assert device.hid == 'laptop-acer-aohappy-lusea0d010038879a01601-88:ae:1d:a6:f3:d0'
assert str(device.system_uuid) == '9ce64e36-829c-b19c-2111-88ae1da6f3d0'
assert Computer.query.count() == 2
for device in Computer.query.all():
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'

View File

@ -2,26 +2,29 @@ import pathlib
import pytest
import requests_mock
from flask import g
from boltons.urlutils import URL
from ereuse_utils.session import DevicehubClient
from flask import g
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 ereuse_devicehub.client import UserClient, Client
from ereuse_devicehub.client import Client, UserClient
from ereuse_devicehub.db import db
from ereuse_devicehub.devicehub import Devicehub
from ereuse_devicehub.resources.user.models import User
from ereuse_devicehub.resources.action.models import Snapshot
from ereuse_devicehub.resources.agent.models import Organization
from ereuse_devicehub.resources.device.models import Desktop, Device
from ereuse_devicehub.resources.enums import ComputerChassis
from ereuse_devicehub.resources.tag import Tag
from ereuse_devicehub.resources.tag.view import CannotCreateETag, LinkedToAnotherDevice, \
TagNotLinked
from ereuse_devicehub.resources.tag.view import (
CannotCreateETag,
LinkedToAnotherDevice,
TagNotLinked,
)
from ereuse_devicehub.resources.user.models import User
from tests import conftest
from tests.conftest import yaml2json, json_encode
from tests.conftest import json_encode, yaml2json
@pytest.mark.mvp
@ -29,7 +32,9 @@ from tests.conftest import yaml2json, json_encode
def test_create_tag(user: UserClient):
"""Creates a tag specifying a custom organization."""
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.commit()
tag = Tag.query.one()
@ -44,7 +49,9 @@ def test_create_tag(user: UserClient):
def test_create_tag_with_device(user: UserClient):
"""Creates a tag specifying linked with one device."""
g.user = User.query.one()
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
pc = Desktop(
serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']
)
db.session.add(pc)
db.session.commit()
tag = Tag(id='bar', owner_id=user.user['id'])
@ -64,7 +71,9 @@ def test_delete_tags(user: UserClient, client: Client):
"""Delete a named tag."""
# Delete Tag Named
g.user = User.query.one()
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
pc = Desktop(
serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']
)
db.session.add(pc)
db.session.commit()
tag = Tag(id='bar', owner_id=user.user['id'], device_id=pc.id)
@ -89,7 +98,9 @@ def test_delete_tags(user: UserClient, client: Client):
# Delete Tag UnNamed
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.commit()
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):
"""Creates a tag using the default organization."""
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
# object, from transient to new (added to session)
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
g.user = User.query.one()
tag = Tag(id='foo-bar', owner_id=user.user['id'])
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
pc = Desktop(
serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']
)
pc.tags.add(tag)
db.session.add(pc)
db.session.commit()
@ -213,7 +228,9 @@ def test_tag_get_device_from_tag_endpoint_no_tag(user: UserClient):
@pytest.mark.mvp
@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
system should not return any of both (to be deterministic) so
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()
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'])
pc2 = Desktop(serial_number='sn2', chassis=ComputerChassis.Tower, owner_id=user2.user['id'])
pc1 = Desktop(
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)
pc2.tags.add(tag2)
db.session.add(pc1)
@ -266,7 +287,17 @@ def test_tag_create_etags_cli(app: Devicehub, user: UserClient):
# todo what happens to organization?
owner_id = user.user['id']
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)
with app.app_context():
tag = Tag.query.one() # type: Tag
@ -284,7 +315,11 @@ def test_tag_manual_link_search(app: Devicehub, user: UserClient):
with app.app_context():
g.user = User.query.one()
db.session.add(Tag('foo-bar', secondary='foo-sec', owner_id=user.user['id']))
desktop = Desktop(serial_number='foo', chassis=ComputerChassis.AllInOne, owner_id=user.user['id'])
desktop = Desktop(
serial_number='foo',
chassis=ComputerChassis.AllInOne,
owner_id=user.user['id'],
)
db.session.add(desktop)
db.session.commit()
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'}]
snapshot, _ = user.post(json_encode(s), res=Snapshot)
device, _ = user.get(res=Device, item=snapshot['device']['devicehubID'])
assert 'foo' in [x['id'] for x in device['tags']]
assert 'bar' in [x.get('secondary') for x in device['tags']]
desktop = Device.query.filter_by(
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
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
@ -359,19 +402,24 @@ def test_tag_multiple_secondary_org(user: UserClient):
@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
returns IDs. These tags are printable.
"""
requests_mock.post('https://example.com/',
# request
request_headers={
'Authorization': 'Basic {}'.format(DevicehubClient.encode_token(
'52dacef0-6bcb-4919-bfed-f10d2c96ecee'))
},
# response
json=['tag1id', 'tag2id'],
status_code=201)
requests_mock.post(
'https://example.com/',
# request
request_headers={
'Authorization': 'Basic {}'.format(
DevicehubClient.encode_token('52dacef0-6bcb-4919-bfed-f10d2c96ecee')
)
},
# response
json=['tag1id', 'tag2id'],
status_code=201,
)
data, _ = user.post({}, res=Tag, query=[('num', 2)])
assert data['items'][0]['id'] == 'tag1id'
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
def test_get_tags_endpoint(user: UserClient, app: Devicehub,
requests_mock: requests_mock.mocker.Mocker):
def test_get_tags_endpoint(
user: UserClient, app: Devicehub, requests_mock: requests_mock.mocker.Mocker
):
"""Performs GET /tags after creating 3 tags, 2 printable and one
not. Only the printable ones are returned.
"""
# Prepare test
with app.app_context():
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.commit()
assert not tag.printable
requests_mock.post('https://example.com/',
# request
request_headers={
'Authorization': 'Basic {}'.format(DevicehubClient.encode_token(
'52dacef0-6bcb-4919-bfed-f10d2c96ecee'))
},
# response
json=['tag1id', 'tag2id'],
status_code=201)
requests_mock.post(
'https://example.com/',
# request
request_headers={
'Authorization': 'Basic {}'.format(
DevicehubClient.encode_token('52dacef0-6bcb-4919-bfed-f10d2c96ecee')
)
},
# response
json=['tag1id', 'tag2id'],
status_code=201,
)
user.post({}, res=Tag, query=[('num', 2)])
# Test itself
@ -421,7 +478,9 @@ def test_get_tag_permissions(app: Devicehub, user: UserClient, user2: UserClient
# Create a pc with a tag
g.user = User.query.all()[0]
tag = Tag(id='foo-bar', owner_id=user.user['id'])
pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id'])
pc = Desktop(
serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']
)
pc.tags.add(tag)
db.session.add(pc)
db.session.commit()
@ -432,5 +491,5 @@ def test_get_tag_permissions(app: Devicehub, user: UserClient, user2: UserClient
computer2, res2 = user2.get(url, None)
assert res.status_code == 200
assert res2.status_code == 200
assert len(computer['items']) == 2
assert len(computer['items']) == 1
assert len(computer2['items']) == 0

View File

@ -1,17 +1,21 @@
"""Tests that emulates the behaviour of a WorkbenchServer."""
import json
import math
import pathlib
import math
import pytest
from ereuse_devicehub.client import UserClient
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.models import Device
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
@ -22,10 +26,9 @@ def test_workbench_server_condensed(user: UserClient):
"""
s = yaml2json('workbench-server-1.snapshot')
s['device']['actions'].append(yaml2json('workbench-server-2.stress-test'))
s['components'][4]['actions'].extend((
yaml2json('workbench-server-3.erase'),
yaml2json('workbench-server-4.install')
))
s['components'][4]['actions'].extend(
(yaml2json('workbench-server-3.erase'), yaml2json('workbench-server-4.install'))
)
s['components'][5]['actions'].append(yaml2json('workbench-server-3.erase'))
# Create 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))
pc_id = snapshot['device']['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']
actions = snapshot['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['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?
assert any([ac['type'] in [BenchmarkProcessor.t, BenchmarkRamSysbench.t] for ac in device['actions']])
assert 'tag1' in [x['id'] for x in device['tags']]
assert any(
[
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.')
@ -136,7 +144,10 @@ def test_real_hp_11(user: UserClient):
s = file('real-hp.snapshot.11')
snapshot, _ = user.post(res=em.Snapshot, data=s)
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 set(e['type'] for e in snapshot['actions']) == {
'BenchmarkDataStorage',
@ -146,7 +157,7 @@ def test_real_hp_11(user: UserClient):
'BenchmarkRamSysbench',
'StressTest',
'TestBios',
'VisualTest'
'VisualTest',
}
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,
actions and their relationships of a real pc.
"""
# import pdb; pdb.set_trace()
s = file('real-eee-1001pxd.snapshot.11')
snapshot, _ = user.post(res=em.Snapshot, data=s)
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['serialNumber'] == 'b8oaas048286'
assert pc['manufacturer'] == 'asustek computer inc.'
assert pc['hid'] == 'laptop-asustek_computer_inc-1001pxd-b8oaas048286-14:da:e9:42:f6:7c'
assert len(pc['tags']) == 1
assert pc['networkSpeeds'] == [100, 0], 'Although it has WiFi we do not know the speed'
assert (
pc['hid']
== '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]['functionalityRange'] == 'B'
# TODO add appearance and functionality Range in device[rate]
components = snapshot['components']
wifi = components[0]
assert wifi['hid'] == 'networkadapter-qualcomm_atheros-' \
'ar9285_wireless_network_adapter-74_2f_68_8b_fd_c8'
assert (
wifi['hid'] == 'networkadapter-qualcomm_atheros-'
'ar9285_wireless_network_adapter-74_2f_68_8b_fd_c8'
)
assert wifi['serialNumber'] == '74:2f:68:8b:fd:c8'
assert wifi['wireless']
eth = components[1]
assert eth['hid'] == 'networkadapter-qualcomm_atheros-' \
'ar8152_v2_0_fast_ethernet-14_da_e9_42_f6_7c'
assert (
eth['hid'] == 'networkadapter-qualcomm_atheros-'
'ar8152_v2_0_fast_ethernet-14_da_e9_42_f6_7c'
)
assert eth['speed'] == 100
assert not eth['wireless']
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 len(actions) == 6
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['memory'] == 256
gpu, _ = user.get(res=Device, item=gpu['devicehubID'])
@ -285,25 +308,26 @@ SNAPSHOTS_NEED_ID = {
'nox.snapshot.json',
'ecs-computers.snapshot.json',
'custom.snapshot.json',
'ecs-2.snapshot.json'
'ecs-2.snapshot.json',
}
"""Snapshots that do not generate HID requiring a custom ID."""
@pytest.mark.mvp
@pytest.mark.parametrize('file',
(pytest.param(f, id=f.name)
for f in pathlib.Path(__file__).parent.joinpath('workbench_files').iterdir())
)
@pytest.mark.parametrize(
'file',
(
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):
"""Uploads the Snapshot files Workbench tests generate.
Keep this files up to date with the Workbench version.
"""
s = json.load(file.open())
user.post(res=em.Snapshot,
data=json_encode(s),
status=201)
user.post(res=em.Snapshot, data=json_encode(s), status=201)
@pytest.mark.mvp