diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6bc1402..35bfc9bf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index a306f99f..658141b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index a1d38854..7947a85b 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -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 diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 7bf53d4f..969df88c 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -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 diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index d8dd80bc..b286bf0f 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -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') +) diff --git a/ereuse_devicehub/migrations/versions/2b90b41a556a_add_owner_to_placeholder.py b/ereuse_devicehub/migrations/versions/2b90b41a556a_add_owner_to_placeholder.py new file mode 100644 index 00000000..c8a80e5a --- /dev/null +++ b/ereuse_devicehub/migrations/versions/2b90b41a556a_add_owner_to_placeholder.py @@ -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()}') diff --git a/ereuse_devicehub/migrations/versions/d7ea9a3b2da1_create_placeholders.py b/ereuse_devicehub/migrations/versions/d7ea9a3b2da1_create_placeholders.py new file mode 100644 index 00000000..9e4a9dbe --- /dev/null +++ b/ereuse_devicehub/migrations/versions/d7ea9a3b2da1_create_placeholders.py @@ -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() diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 9dadab2c..56165555 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -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): diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 56fd6de9..3b27b327 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -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) diff --git a/ereuse_devicehub/resources/device/sync.py b/ereuse_devicehub/resources/device/sync.py index dedaf499..3f284e71 100644 --- a/ereuse_devicehub/resources/device/sync.py +++ b/ereuse_devicehub/resources/device/sync.py @@ -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: diff --git a/ereuse_devicehub/resources/device/views.py b/ereuse_devicehub/resources/device/views.py index 8bab2a3c..238284e1 100644 --- a/ereuse_devicehub/resources/device/views.py +++ b/ereuse_devicehub/resources/device/views.py @@ -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 ) ) diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index c2309224..461e7c69 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -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): diff --git a/ereuse_devicehub/static/js/print.pdf.js b/ereuse_devicehub/static/js/print.pdf.js index e0109d33..33ead3fe 100644 --- a/ereuse_devicehub/static/js/print.pdf.js +++ b/ereuse_devicehub/static/js/print.pdf.js @@ -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); diff --git a/ereuse_devicehub/templates/inventory/binding.html b/ereuse_devicehub/templates/inventory/binding.html new file mode 100644 index 00000000..e26fb1bb --- /dev/null +++ b/ereuse_devicehub/templates/inventory/binding.html @@ -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 %} diff --git a/ereuse_devicehub/templates/inventory/device_detail.html b/ereuse_devicehub/templates/inventory/device_detail.html index 2da9cd31..bade9c63 100644 --- a/ereuse_devicehub/templates/inventory/device_detail.html +++ b/ereuse_devicehub/templates/inventory/device_detail.html @@ -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> diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index a511d397..cd8f35a2 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -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> diff --git a/ereuse_devicehub/templates/inventory/unbinding.html b/ereuse_devicehub/templates/inventory/unbinding.html new file mode 100644 index 00000000..457b4a74 --- /dev/null +++ b/ereuse_devicehub/templates/inventory/unbinding.html @@ -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 %} diff --git a/ereuse_devicehub/templates/labels/print_labels.html b/ereuse_devicehub/templates/labels/print_labels.html index 252ef18d..77e2048b 100644 --- a/ereuse_devicehub/templates/labels/print_labels.html +++ b/ereuse_devicehub/templates/labels/print_labels.html @@ -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> diff --git a/tests/files/basic-stock.csv b/tests/files/basic-stock.csv index 2ef3b4e6..e96b1c7f 100644 --- a/tests/files/basic-stock.csv +++ b/tests/files/basic-stock.csv @@ -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" diff --git a/tests/files/basic.csv b/tests/files/basic.csv index 6246c28e..84663832 100644 --- a/tests/files/basic.csv +++ b/tests/files/basic.csv @@ -1,2 +1,2 @@ "DHID";"DocumentID";"Public Link";"Lots";"Tag 1 Type";"Tag 1 ID";"Tag 1 Organization";"Tag 2 Type";"Tag 2 ID";"Tag 2 Organization";"Tag 3 Type";"Tag 3 ID";"Tag 3 Organization";"Device Hardware ID";"Device Type";"Device Chassis";"Device Serial Number";"Device Model";"Device Manufacturer";"Registered in";"Registered (process)";"Updated in (software)";"Updated in (web)";"Physical state";"Allocate state";"Lifecycle state";"Processor";"RAM (MB)";"Data Storage Size (MB)";"Processor 1";"Processor 1 Manufacturer";"Processor 1 Model";"Processor 1 Serial Number";"Processor 1 Number of cores";"Processor 1 Speed (GHz)";"Benchmark Processor 1 (points)";"Benchmark ProcessorSysbench Processor 1 (points)";"Processor 2";"Processor 2 Manufacturer";"Processor 2 Model";"Processor 2 Serial Number";"Processor 2 Number of cores";"Processor 2 Speed (GHz)";"Benchmark Processor 2 (points)";"Benchmark ProcessorSysbench Processor 2 (points)";"RamModule 1";"RamModule 1 Manufacturer";"RamModule 1 Model";"RamModule 1 Serial Number";"RamModule 1 Size (MB)";"RamModule 1 Speed (MHz)";"RamModule 2";"RamModule 2 Manufacturer";"RamModule 2 Model";"RamModule 2 Serial Number";"RamModule 2 Size (MB)";"RamModule 2 Speed (MHz)";"RamModule 3";"RamModule 3 Manufacturer";"RamModule 3 Model";"RamModule 3 Serial Number";"RamModule 3 Size (MB)";"RamModule 3 Speed (MHz)";"RamModule 4";"RamModule 4 Manufacturer";"RamModule 4 Model";"RamModule 4 Serial Number";"RamModule 4 Size (MB)";"RamModule 4 Speed (MHz)";"DataStorage 1";"DataStorage 1 Manufacturer";"DataStorage 1 Model";"DataStorage 1 Serial Number";"DataStorage 1 Size (MB)";"Erasure DataStorage 1";"Erasure DataStorage 1 Serial Number";"Erasure DataStorage 1 Size (MB)";"Erasure DataStorage 1 Software";"Erasure DataStorage 1 Result";"Erasure DataStorage 1 Certificate URL";"Erasure DataStorage 1 Type";"Erasure DataStorage 1 Method";"Erasure DataStorage 1 Elapsed (hours)";"Erasure DataStorage 1 Date";"Erasure DataStorage 1 Steps";"Erasure DataStorage 1 Steps Start Time";"Erasure DataStorage 1 Steps End Time";"Benchmark DataStorage 1 Read Speed (MB/s)";"Benchmark DataStorage 1 Writing speed (MB/s)";"Test DataStorage 1 Software";"Test DataStorage 1 Type";"Test DataStorage 1 Result";"Test DataStorage 1 Power cycle count";"Test DataStorage 1 Lifetime (days)";"Test DataStorage 1 Power on hours";"DataStorage 2";"DataStorage 2 Manufacturer";"DataStorage 2 Model";"DataStorage 2 Serial Number";"DataStorage 2 Size (MB)";"Erasure DataStorage 2";"Erasure DataStorage 2 Serial Number";"Erasure DataStorage 2 Size (MB)";"Erasure DataStorage 2 Software";"Erasure DataStorage 2 Result";"Erasure DataStorage 2 Certificate URL";"Erasure DataStorage 2 Type";"Erasure DataStorage 2 Method";"Erasure DataStorage 2 Elapsed (hours)";"Erasure DataStorage 2 Date";"Erasure DataStorage 2 Steps";"Erasure DataStorage 2 Steps Start Time";"Erasure DataStorage 2 Steps End Time";"Benchmark DataStorage 2 Read Speed (MB/s)";"Benchmark DataStorage 2 Writing speed (MB/s)";"Test DataStorage 2 Software";"Test DataStorage 2 Type";"Test DataStorage 2 Result";"Test DataStorage 2 Power cycle count";"Test DataStorage 2 Lifetime (days)";"Test DataStorage 2 Power on hours";"DataStorage 3";"DataStorage 3 Manufacturer";"DataStorage 3 Model";"DataStorage 3 Serial Number";"DataStorage 3 Size (MB)";"Erasure DataStorage 3";"Erasure DataStorage 3 Serial Number";"Erasure DataStorage 3 Size (MB)";"Erasure DataStorage 3 Software";"Erasure DataStorage 3 Result";"Erasure DataStorage 3 Certificate URL";"Erasure DataStorage 3 Type";"Erasure DataStorage 3 Method";"Erasure DataStorage 3 Elapsed (hours)";"Erasure DataStorage 3 Date";"Erasure DataStorage 3 Steps";"Erasure DataStorage 3 Steps Start Time";"Erasure DataStorage 3 Steps End Time";"Benchmark DataStorage 3 Read Speed (MB/s)";"Benchmark DataStorage 3 Writing speed (MB/s)";"Test DataStorage 3 Software";"Test DataStorage 3 Type";"Test DataStorage 3 Result";"Test DataStorage 3 Power cycle count";"Test DataStorage 3 Lifetime (days)";"Test DataStorage 3 Power on hours";"DataStorage 4";"DataStorage 4 Manufacturer";"DataStorage 4 Model";"DataStorage 4 Serial Number";"DataStorage 4 Size (MB)";"Erasure DataStorage 4";"Erasure DataStorage 4 Serial Number";"Erasure DataStorage 4 Size (MB)";"Erasure DataStorage 4 Software";"Erasure DataStorage 4 Result";"Erasure DataStorage 4 Certificate URL";"Erasure DataStorage 4 Type";"Erasure DataStorage 4 Method";"Erasure DataStorage 4 Elapsed (hours)";"Erasure DataStorage 4 Date";"Erasure DataStorage 4 Steps";"Erasure DataStorage 4 Steps Start Time";"Erasure DataStorage 4 Steps End Time";"Benchmark DataStorage 4 Read Speed (MB/s)";"Benchmark DataStorage 4 Writing speed (MB/s)";"Test DataStorage 4 Software";"Test DataStorage 4 Type";"Test DataStorage 4 Result";"Test DataStorage 4 Power cycle count";"Test DataStorage 4 Lifetime (days)";"Test DataStorage 4 Power on hours";"Motherboard 1";"Motherboard 1 Manufacturer";"Motherboard 1 Model";"Motherboard 1 Serial Number";"Display 1";"Display 1 Manufacturer";"Display 1 Model";"Display 1 Serial Number";"GraphicCard 1";"GraphicCard 1 Manufacturer";"GraphicCard 1 Model";"GraphicCard 1 Serial Number";"GraphicCard 1 Memory (MB)";"GraphicCard 2";"GraphicCard 2 Manufacturer";"GraphicCard 2 Model";"GraphicCard 2 Serial Number";"GraphicCard 2 Memory (MB)";"NetworkAdapter 1";"NetworkAdapter 1 Manufacturer";"NetworkAdapter 1 Model";"NetworkAdapter 1 Serial Number";"NetworkAdapter 2";"NetworkAdapter 2 Manufacturer";"NetworkAdapter 2 Model";"NetworkAdapter 2 Serial Number";"SoundCard 1";"SoundCard 1 Manufacturer";"SoundCard 1 Model";"SoundCard 1 Serial Number";"SoundCard 2";"SoundCard 2 Manufacturer";"SoundCard 2 Model";"SoundCard 2 Serial Number";"Device Rate";"Device Range";"Processor Rate";"Processor Range";"RAM Rate";"RAM Range";"Data Storage Rate";"Data Storage Range";"Benchmark RamSysbench (points)" -"O48N2";"";"http://localhost/devices/O48N2";"";"named";"O48N2";"FooOrg";"";"";"";"";"";"";"desktop-d1mr-d1ml-d1s";"Desktop";"Microtower";"d1s";"d1ml";"d1mr";"Mon Aug 1 20:23:53 2022";"Workbench 11.0";"2022-08-01 20:23:53.835857+02:00";"";"";"";"";"p1ml";"0";"0";"Processor 6: model p1ml, S/N p1s";"p1mr";"p1ml";"p1s";"";"1.6";"2410.0";"";"";"";"";"";"";"";"";"";"RamModule 5: model rm1ml, S/N rm1s";"rm1mr";"rm1ml";"rm1s";"";"1333";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"GraphicCard 4: model gc1ml, S/N gc1s";"gc1mr";"gc1ml";"gc1s";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"" +"O48N2";"";"http://localhost/devices/O48N2";"";"";"";"";"";"";"";"";"";"";"desktop-d1mr-d1ml-d1s";"Desktop";"Microtower";"d1s";"d1ml";"d1mr";"Tue Aug 2 12:56:55 2022";"Workbench 11.0";"2022-08-02 12:56:55.324600+02:00";"";"";"";"";"p1ml";"0";"0";"Processor 7: model p1ml, S/N p1s";"p1mr";"p1ml";"p1s";"";"1.6";"2410.0";"";"";"";"";"";"";"";"";"";"RamModule 6: model rm1ml, S/N rm1s";"rm1mr";"rm1ml";"rm1s";"";"1333";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"GraphicCard 5: model gc1ml, S/N gc1s";"gc1mr";"gc1ml";"gc1s";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"" diff --git a/tests/files/export_devices.csv b/tests/files/export_devices.csv index eef0402d..53d39fe3 100644 --- a/tests/files/export_devices.csv +++ b/tests/files/export_devices.csv @@ -1,2 +1,2 @@ "DHID";"DocumentID";"Public Link";"Lots";"Tag 1 Type";"Tag 1 ID";"Tag 1 Organization";"Tag 2 Type";"Tag 2 ID";"Tag 2 Organization";"Tag 3 Type";"Tag 3 ID";"Tag 3 Organization";"Device Hardware ID";"Device Type";"Device Chassis";"Device Serial Number";"Device Model";"Device Manufacturer";"Registered in";"Registered (process)";"Updated in (software)";"Updated in (web)";"Physical state";"Allocate state";"Lifecycle state";"Processor";"RAM (MB)";"Data Storage Size (MB)";"Processor 1";"Processor 1 Manufacturer";"Processor 1 Model";"Processor 1 Serial Number";"Processor 1 Number of cores";"Processor 1 Speed (GHz)";"Benchmark Processor 1 (points)";"Benchmark ProcessorSysbench Processor 1 (points)";"Processor 2";"Processor 2 Manufacturer";"Processor 2 Model";"Processor 2 Serial Number";"Processor 2 Number of cores";"Processor 2 Speed (GHz)";"Benchmark Processor 2 (points)";"Benchmark ProcessorSysbench Processor 2 (points)";"RamModule 1";"RamModule 1 Manufacturer";"RamModule 1 Model";"RamModule 1 Serial Number";"RamModule 1 Size (MB)";"RamModule 1 Speed (MHz)";"RamModule 2";"RamModule 2 Manufacturer";"RamModule 2 Model";"RamModule 2 Serial Number";"RamModule 2 Size (MB)";"RamModule 2 Speed (MHz)";"RamModule 3";"RamModule 3 Manufacturer";"RamModule 3 Model";"RamModule 3 Serial Number";"RamModule 3 Size (MB)";"RamModule 3 Speed (MHz)";"RamModule 4";"RamModule 4 Manufacturer";"RamModule 4 Model";"RamModule 4 Serial Number";"RamModule 4 Size (MB)";"RamModule 4 Speed (MHz)";"DataStorage 1";"DataStorage 1 Manufacturer";"DataStorage 1 Model";"DataStorage 1 Serial Number";"DataStorage 1 Size (MB)";"Erasure DataStorage 1";"Erasure DataStorage 1 Serial Number";"Erasure DataStorage 1 Size (MB)";"Erasure DataStorage 1 Software";"Erasure DataStorage 1 Result";"Erasure DataStorage 1 Certificate URL";"Erasure DataStorage 1 Type";"Erasure DataStorage 1 Method";"Erasure DataStorage 1 Elapsed (hours)";"Erasure DataStorage 1 Date";"Erasure DataStorage 1 Steps";"Erasure DataStorage 1 Steps Start Time";"Erasure DataStorage 1 Steps End Time";"Benchmark DataStorage 1 Read Speed (MB/s)";"Benchmark DataStorage 1 Writing speed (MB/s)";"Test DataStorage 1 Software";"Test DataStorage 1 Type";"Test DataStorage 1 Result";"Test DataStorage 1 Power cycle count";"Test DataStorage 1 Lifetime (days)";"Test DataStorage 1 Power on hours";"DataStorage 2";"DataStorage 2 Manufacturer";"DataStorage 2 Model";"DataStorage 2 Serial Number";"DataStorage 2 Size (MB)";"Erasure DataStorage 2";"Erasure DataStorage 2 Serial Number";"Erasure DataStorage 2 Size (MB)";"Erasure DataStorage 2 Software";"Erasure DataStorage 2 Result";"Erasure DataStorage 2 Certificate URL";"Erasure DataStorage 2 Type";"Erasure DataStorage 2 Method";"Erasure DataStorage 2 Elapsed (hours)";"Erasure DataStorage 2 Date";"Erasure DataStorage 2 Steps";"Erasure DataStorage 2 Steps Start Time";"Erasure DataStorage 2 Steps End Time";"Benchmark DataStorage 2 Read Speed (MB/s)";"Benchmark DataStorage 2 Writing speed (MB/s)";"Test DataStorage 2 Software";"Test DataStorage 2 Type";"Test DataStorage 2 Result";"Test DataStorage 2 Power cycle count";"Test DataStorage 2 Lifetime (days)";"Test DataStorage 2 Power on hours";"DataStorage 3";"DataStorage 3 Manufacturer";"DataStorage 3 Model";"DataStorage 3 Serial Number";"DataStorage 3 Size (MB)";"Erasure DataStorage 3";"Erasure DataStorage 3 Serial Number";"Erasure DataStorage 3 Size (MB)";"Erasure DataStorage 3 Software";"Erasure DataStorage 3 Result";"Erasure DataStorage 3 Certificate URL";"Erasure DataStorage 3 Type";"Erasure DataStorage 3 Method";"Erasure DataStorage 3 Elapsed (hours)";"Erasure DataStorage 3 Date";"Erasure DataStorage 3 Steps";"Erasure DataStorage 3 Steps Start Time";"Erasure DataStorage 3 Steps End Time";"Benchmark DataStorage 3 Read Speed (MB/s)";"Benchmark DataStorage 3 Writing speed (MB/s)";"Test DataStorage 3 Software";"Test DataStorage 3 Type";"Test DataStorage 3 Result";"Test DataStorage 3 Power cycle count";"Test DataStorage 3 Lifetime (days)";"Test DataStorage 3 Power on hours";"DataStorage 4";"DataStorage 4 Manufacturer";"DataStorage 4 Model";"DataStorage 4 Serial Number";"DataStorage 4 Size (MB)";"Erasure DataStorage 4";"Erasure DataStorage 4 Serial Number";"Erasure DataStorage 4 Size (MB)";"Erasure DataStorage 4 Software";"Erasure DataStorage 4 Result";"Erasure DataStorage 4 Certificate URL";"Erasure DataStorage 4 Type";"Erasure DataStorage 4 Method";"Erasure DataStorage 4 Elapsed (hours)";"Erasure DataStorage 4 Date";"Erasure DataStorage 4 Steps";"Erasure DataStorage 4 Steps Start Time";"Erasure DataStorage 4 Steps End Time";"Benchmark DataStorage 4 Read Speed (MB/s)";"Benchmark DataStorage 4 Writing speed (MB/s)";"Test DataStorage 4 Software";"Test DataStorage 4 Type";"Test DataStorage 4 Result";"Test DataStorage 4 Power cycle count";"Test DataStorage 4 Lifetime (days)";"Test DataStorage 4 Power on hours";"Motherboard 1";"Motherboard 1 Manufacturer";"Motherboard 1 Model";"Motherboard 1 Serial Number";"Display 1";"Display 1 Manufacturer";"Display 1 Model";"Display 1 Serial Number";"GraphicCard 1";"GraphicCard 1 Manufacturer";"GraphicCard 1 Model";"GraphicCard 1 Serial Number";"GraphicCard 1 Memory (MB)";"GraphicCard 2";"GraphicCard 2 Manufacturer";"GraphicCard 2 Model";"GraphicCard 2 Serial Number";"GraphicCard 2 Memory (MB)";"NetworkAdapter 1";"NetworkAdapter 1 Manufacturer";"NetworkAdapter 1 Model";"NetworkAdapter 1 Serial Number";"NetworkAdapter 2";"NetworkAdapter 2 Manufacturer";"NetworkAdapter 2 Model";"NetworkAdapter 2 Serial Number";"SoundCard 1";"SoundCard 1 Manufacturer";"SoundCard 1 Model";"SoundCard 1 Serial Number";"SoundCard 2";"SoundCard 2 Manufacturer";"SoundCard 2 Model";"SoundCard 2 Serial Number";"Device Rate";"Device Range";"Processor Rate";"Processor Range";"RAM Rate";"RAM Range";"Data Storage Rate";"Data Storage Range";"Benchmark RamSysbench (points)" -"O48N2";"";"http://localhost/devices/O48N2";"";"named";"O48N2";"FooOrg";"";"";"";"";"";"";"laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b";"Laptop";"Netbook";"b8oaas048285";"1001pxd";"asustek computer inc.";"Mon Aug 1 19:29:18 2022";"Workbench 11.0a2";"2022-08-01 19:29:18.627632+02:00";"";"";"";"";"intel atom cpu n455 @ 2.66ghz";"1024";"238475";"Processor 6: model intel atom cpu n455 @ 2.66ghz, S/N None";"intel corp.";"intel atom cpu n455 @ 2.66ghz";"";"1";"2.667";"6666.24";"164.0803";"";"";"";"";"";"";"";"";"RamModule 10: model None, S/N None";"";"";"";"1024";"667";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"HardDrive 11: model hts54322, S/N e2024242cv86mm";"hitachi";"hts54322";"e2024242cv86mm";"238475";"harddrive-hitachi-hts54322-e2024242cv86mm";"e2024242cv86mm";"238475";"Workbench 11.0a2";"Success";"";"EraseBasic";"Shred";"1:16:49";"2022-08-01 19:29:18.577378+02:00";"✓ – StepRandom 1:16:49";"2018-07-03 11:15:22.257059+02:00";"2018-07-03 12:32:11.843190+02:00";"66.2";"21.8";"Workbench 11.0a2";"Short";"Failure";"";"";"0";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"Motherboard 12: model 1001pxd, S/N eee0123456720";"asustek computer inc.";"1001pxd";"eee0123456720";"";"";"";"";"GraphicCard 7: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None";"intel corporation";"atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller";"";"256";"";"";"";"";"";"NetworkAdapter 4: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9";"qualcomm atheros";"ar9285 wireless network adapter";"74:2f:68:8b:fd:c9";"NetworkAdapter 5: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b";"qualcomm atheros";"ar8152 v2.0 fast ethernet";"14:da:e9:42:f6:7b";"SoundCard 8: model nm10/ich7 family high definition audio controller, S/N None";"intel corporation";"nm10/ich7 family high definition audio controller";"";"SoundCard 9: model usb 2.0 uvc vga webcam, S/N 0x0001";"azurewave";"usb 2.0 uvc vga webcam";"0x0001";"";"";"";"";"";"";"";"";"15.7188" +"O48N2";"";"http://localhost/devices/O48N2";"";"";"";"";"";"";"";"";"";"";"laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b";"Laptop";"Netbook";"b8oaas048285";"1001pxd";"asustek computer inc.";"Tue Aug 2 12:53:02 2022";"Workbench 11.0a2";"2022-08-02 12:53:02.944678+02:00";"";"";"";"";"intel atom cpu n455 @ 2.66ghz";"1024";"238475";"Processor 7: model intel atom cpu n455 @ 2.66ghz, S/N None";"intel corp.";"intel atom cpu n455 @ 2.66ghz";"";"1";"2.667";"6666.24";"164.0803";"";"";"";"";"";"";"";"";"RamModule 11: model None, S/N None";"";"";"";"1024";"667";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"HardDrive 12: model hts54322, S/N e2024242cv86mm";"hitachi";"hts54322";"e2024242cv86mm";"238475";"harddrive-hitachi-hts54322-e2024242cv86mm";"e2024242cv86mm";"238475";"Workbench 11.0a2";"Success";"";"EraseBasic";"Shred";"1:16:49";"2022-08-02 12:53:02.892789+02:00";"✓ – StepRandom 1:16:49";"2018-07-03 11:15:22.257059+02:00";"2018-07-03 12:32:11.843190+02:00";"66.2";"21.8";"Workbench 11.0a2";"Short";"Failure";"";"";"0";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"Motherboard 13: model 1001pxd, S/N eee0123456720";"asustek computer inc.";"1001pxd";"eee0123456720";"";"";"";"";"GraphicCard 8: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None";"intel corporation";"atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller";"";"256";"";"";"";"";"";"NetworkAdapter 5: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9";"qualcomm atheros";"ar9285 wireless network adapter";"74:2f:68:8b:fd:c9";"NetworkAdapter 6: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b";"qualcomm atheros";"ar8152 v2.0 fast ethernet";"14:da:e9:42:f6:7b";"SoundCard 9: model nm10/ich7 family high definition audio controller, S/N None";"intel corporation";"nm10/ich7 family high definition audio controller";"";"SoundCard 10: model usb 2.0 uvc vga webcam, S/N 0x0001";"azurewave";"usb 2.0 uvc vga webcam";"0x0001";"";"";"";"";"";"";"";"";"15.7188" diff --git a/tests/files/proposal_extended_csv_report.csv b/tests/files/proposal_extended_csv_report.csv index 350d06aa..789e4ee5 100644 --- a/tests/files/proposal_extended_csv_report.csv +++ b/tests/files/proposal_extended_csv_report.csv @@ -1,3 +1,3 @@ "DHID";"DocumentID";"Public Link";"Lots";"Tag 1 Type";"Tag 1 ID";"Tag 1 Organization";"Tag 2 Type";"Tag 2 ID";"Tag 2 Organization";"Tag 3 Type";"Tag 3 ID";"Tag 3 Organization";"Device Hardware ID";"Device Type";"Device Chassis";"Device Serial Number";"Device Model";"Device Manufacturer";"Registered in";"Registered (process)";"Updated in (software)";"Updated in (web)";"Physical state";"Allocate state";"Lifecycle state";"Processor";"RAM (MB)";"Data Storage Size (MB)";"Processor 1";"Processor 1 Manufacturer";"Processor 1 Model";"Processor 1 Serial Number";"Processor 1 Number of cores";"Processor 1 Speed (GHz)";"Benchmark Processor 1 (points)";"Benchmark ProcessorSysbench Processor 1 (points)";"Processor 2";"Processor 2 Manufacturer";"Processor 2 Model";"Processor 2 Serial Number";"Processor 2 Number of cores";"Processor 2 Speed (GHz)";"Benchmark Processor 2 (points)";"Benchmark ProcessorSysbench Processor 2 (points)";"RamModule 1";"RamModule 1 Manufacturer";"RamModule 1 Model";"RamModule 1 Serial Number";"RamModule 1 Size (MB)";"RamModule 1 Speed (MHz)";"RamModule 2";"RamModule 2 Manufacturer";"RamModule 2 Model";"RamModule 2 Serial Number";"RamModule 2 Size (MB)";"RamModule 2 Speed (MHz)";"RamModule 3";"RamModule 3 Manufacturer";"RamModule 3 Model";"RamModule 3 Serial Number";"RamModule 3 Size (MB)";"RamModule 3 Speed (MHz)";"RamModule 4";"RamModule 4 Manufacturer";"RamModule 4 Model";"RamModule 4 Serial Number";"RamModule 4 Size (MB)";"RamModule 4 Speed (MHz)";"DataStorage 1";"DataStorage 1 Manufacturer";"DataStorage 1 Model";"DataStorage 1 Serial Number";"DataStorage 1 Size (MB)";"Erasure DataStorage 1";"Erasure DataStorage 1 Serial Number";"Erasure DataStorage 1 Size (MB)";"Erasure DataStorage 1 Software";"Erasure DataStorage 1 Result";"Erasure DataStorage 1 Certificate URL";"Erasure DataStorage 1 Type";"Erasure DataStorage 1 Method";"Erasure DataStorage 1 Elapsed (hours)";"Erasure DataStorage 1 Date";"Erasure DataStorage 1 Steps";"Erasure DataStorage 1 Steps Start Time";"Erasure DataStorage 1 Steps End Time";"Benchmark DataStorage 1 Read Speed (MB/s)";"Benchmark DataStorage 1 Writing speed (MB/s)";"Test DataStorage 1 Software";"Test DataStorage 1 Type";"Test DataStorage 1 Result";"Test DataStorage 1 Power cycle count";"Test DataStorage 1 Lifetime (days)";"Test DataStorage 1 Power on hours";"DataStorage 2";"DataStorage 2 Manufacturer";"DataStorage 2 Model";"DataStorage 2 Serial Number";"DataStorage 2 Size (MB)";"Erasure DataStorage 2";"Erasure DataStorage 2 Serial Number";"Erasure DataStorage 2 Size (MB)";"Erasure DataStorage 2 Software";"Erasure DataStorage 2 Result";"Erasure DataStorage 2 Certificate URL";"Erasure DataStorage 2 Type";"Erasure DataStorage 2 Method";"Erasure DataStorage 2 Elapsed (hours)";"Erasure DataStorage 2 Date";"Erasure DataStorage 2 Steps";"Erasure DataStorage 2 Steps Start Time";"Erasure DataStorage 2 Steps End Time";"Benchmark DataStorage 2 Read Speed (MB/s)";"Benchmark DataStorage 2 Writing speed (MB/s)";"Test DataStorage 2 Software";"Test DataStorage 2 Type";"Test DataStorage 2 Result";"Test DataStorage 2 Power cycle count";"Test DataStorage 2 Lifetime (days)";"Test DataStorage 2 Power on hours";"DataStorage 3";"DataStorage 3 Manufacturer";"DataStorage 3 Model";"DataStorage 3 Serial Number";"DataStorage 3 Size (MB)";"Erasure DataStorage 3";"Erasure DataStorage 3 Serial Number";"Erasure DataStorage 3 Size (MB)";"Erasure DataStorage 3 Software";"Erasure DataStorage 3 Result";"Erasure DataStorage 3 Certificate URL";"Erasure DataStorage 3 Type";"Erasure DataStorage 3 Method";"Erasure DataStorage 3 Elapsed (hours)";"Erasure DataStorage 3 Date";"Erasure DataStorage 3 Steps";"Erasure DataStorage 3 Steps Start Time";"Erasure DataStorage 3 Steps End Time";"Benchmark DataStorage 3 Read Speed (MB/s)";"Benchmark DataStorage 3 Writing speed (MB/s)";"Test DataStorage 3 Software";"Test DataStorage 3 Type";"Test DataStorage 3 Result";"Test DataStorage 3 Power cycle count";"Test DataStorage 3 Lifetime (days)";"Test DataStorage 3 Power on hours";"DataStorage 4";"DataStorage 4 Manufacturer";"DataStorage 4 Model";"DataStorage 4 Serial Number";"DataStorage 4 Size (MB)";"Erasure DataStorage 4";"Erasure DataStorage 4 Serial Number";"Erasure DataStorage 4 Size (MB)";"Erasure DataStorage 4 Software";"Erasure DataStorage 4 Result";"Erasure DataStorage 4 Certificate URL";"Erasure DataStorage 4 Type";"Erasure DataStorage 4 Method";"Erasure DataStorage 4 Elapsed (hours)";"Erasure DataStorage 4 Date";"Erasure DataStorage 4 Steps";"Erasure DataStorage 4 Steps Start Time";"Erasure DataStorage 4 Steps End Time";"Benchmark DataStorage 4 Read Speed (MB/s)";"Benchmark DataStorage 4 Writing speed (MB/s)";"Test DataStorage 4 Software";"Test DataStorage 4 Type";"Test DataStorage 4 Result";"Test DataStorage 4 Power cycle count";"Test DataStorage 4 Lifetime (days)";"Test DataStorage 4 Power on hours";"Motherboard 1";"Motherboard 1 Manufacturer";"Motherboard 1 Model";"Motherboard 1 Serial Number";"Display 1";"Display 1 Manufacturer";"Display 1 Model";"Display 1 Serial Number";"GraphicCard 1";"GraphicCard 1 Manufacturer";"GraphicCard 1 Model";"GraphicCard 1 Serial Number";"GraphicCard 1 Memory (MB)";"GraphicCard 2";"GraphicCard 2 Manufacturer";"GraphicCard 2 Model";"GraphicCard 2 Serial Number";"GraphicCard 2 Memory (MB)";"NetworkAdapter 1";"NetworkAdapter 1 Manufacturer";"NetworkAdapter 1 Model";"NetworkAdapter 1 Serial Number";"NetworkAdapter 2";"NetworkAdapter 2 Manufacturer";"NetworkAdapter 2 Model";"NetworkAdapter 2 Serial Number";"SoundCard 1";"SoundCard 1 Manufacturer";"SoundCard 1 Model";"SoundCard 1 Serial Number";"SoundCard 2";"SoundCard 2 Manufacturer";"SoundCard 2 Model";"SoundCard 2 Serial Number";"Device Rate";"Device Range";"Processor Rate";"Processor Range";"RAM Rate";"RAM Range";"Data Storage Rate";"Data Storage Range";"Benchmark RamSysbench (points)" -"O48N2";"";"http://localhost/devices/O48N2";"";"named";"O48N2";"FooOrg";"";"";"";"";"";"";"laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b";"Laptop";"Netbook";"b8oaas048285";"1001pxd";"asustek computer inc.";"Mon Aug 1 20:18:46 2022";"Workbench 11.0a2";"2022-08-01 20:18:46.209696+02:00";"";"";"";"";"intel atom cpu n455 @ 2.66ghz";"1024";"238475";"Processor 6: model intel atom cpu n455 @ 2.66ghz, S/N None";"intel corp.";"intel atom cpu n455 @ 2.66ghz";"";"1";"2.667";"6666.24";"164.0803";"";"";"";"";"";"";"";"";"RamModule 10: model None, S/N None";"";"";"";"1024";"667";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"HardDrive 11: model hts54322, S/N e2024242cv86mm";"hitachi";"hts54322";"e2024242cv86mm";"238475";"harddrive-hitachi-hts54322-e2024242cv86mm";"e2024242cv86mm";"238475";"Workbench 11.0a2";"Success";"";"EraseBasic";"Shred";"1:16:49";"2022-08-01 20:18:46.167257+02:00";"✓ – StepRandom 1:16:49";"2018-07-03 11:15:22.257059+02:00";"2018-07-03 12:32:11.843190+02:00";"66.2";"21.8";"Workbench 11.0a2";"Short";"Failure";"";"";"0";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"Motherboard 12: model 1001pxd, S/N eee0123456720";"asustek computer inc.";"1001pxd";"eee0123456720";"";"";"";"";"GraphicCard 7: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None";"intel corporation";"atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller";"";"256";"";"";"";"";"";"NetworkAdapter 4: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9";"qualcomm atheros";"ar9285 wireless network adapter";"74:2f:68:8b:fd:c9";"NetworkAdapter 5: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b";"qualcomm atheros";"ar8152 v2.0 fast ethernet";"14:da:e9:42:f6:7b";"SoundCard 8: model nm10/ich7 family high definition audio controller, S/N None";"intel corporation";"nm10/ich7 family high definition audio controller";"";"SoundCard 9: model usb 2.0 uvc vga webcam, S/N 0x0001";"azurewave";"usb 2.0 uvc vga webcam";"0x0001";"";"";"";"";"";"";"";"";"15.7188" -"J2MA2";"";"http://localhost/devices/J2MA2";"";"named";"J2MA2";"FooOrg";"";"";"";"";"";"";"laptop-asustek_computer_inc-1001pxd-b8oaas048287-14:da:e9:42:f6:7c";"Laptop";"Netbook";"b8oaas048287";"1001pxd";"asustek computer inc.";"Mon Aug 1 20:18:46 2022";"Workbench 11.0b11";"2022-08-01 20:18:46.453306+02:00";"";"";"";"";"intel atom cpu n455 @ 1.66ghz";"2048";"558558";"Processor 17: model intel atom cpu n455 @ 1.66ghz, S/N None";"intel corp.";"intel atom cpu n455 @ 1.66ghz";"";"1";"1.667";"6666.24";"164.0803";"";"";"";"";"";"";"";"";"RamModule 21: model None, S/N None";"";"";"";"1024";"667";"RamModule 22: model 48594d503131325336344350362d53362020, S/N 4f43487b";"hynix semiconductor";"48594d503131325336344350362d53362020";"4f43487b";"1024";"667";"";"";"";"";"";"";"";"";"";"";"";"";"HardDrive 23: model hts54322, S/N e2024242cv86hj";"hitachi";"hts54322";"e2024242cv86hj";"238475";"harddrive-hitachi-hts54322-e2024242cv86hj";"e2024242cv86hj";"238475";"Workbench 11.0b11";"Success";"";"EraseBasic";"Shred";"1:16:49";"2022-08-01 20:18:46.408888+02:00";"✓ – StepRandom 1:16:49";"2018-07-03 11:15:22.257059+02:00";"2018-07-03 12:32:11.843190+02:00";"66.2";"21.8";"Workbench 11.0b11";"Extended";"Failure";"";"";"0";"DataStorage 24: model wdc wd1600bevt-2, S/N wd-wx11a80w7430";"western digital";"wdc wd1600bevt-2";"wd-wx11a80w7430";"160041";"datastorage-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430";"wd-wx11a80w7430";"160041";"Workbench 11.0b11";"Failure";"";"EraseBasic";"Shred";"0:45:36";"2022-08-01 20:18:46.410949+02:00";"✓ – StepRandom 0:45:36";"2019-10-23 09:49:54.410830+02:00";"2019-10-23 10:35:31.400587+02:00";"41.6";"17.3";"Workbench 11.0b11";"Short";"Success";"5293";"195 days, 12:00:00";"4692";"SolidStateDrive 25: model wdc wd1600bevt-2, S/N wd-wx11a80w7430";"western digital";"wdc wd1600bevt-2";"wd-wx11a80w7430";"160042";"solidstatedrive-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430";"wd-wx11a80w7430";"160042";"Workbench 11.0b11";"Success";"";"EraseSectors";"Badblocks";"1:46:03";"2022-08-01 20:18:46.415788+02:00";"✓ – StepRandom 0:46:03,✓ – StepZero 1:00:00";"2019-08-19 18:48:19.690458+02:00,2019-08-19 19:34:22.690458+02:00";"2019-08-19 19:34:22.930562+02:00,2019-08-19 20:34:22.930562+02:00";"41.1";"17.1";"Workbench 11.0b11";"Short";"Success";"5231";"194 days, 17:00:00";"4673";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"Motherboard 26: model 1001pxd, S/N eee0123456789";"asustek computer inc.";"1001pxd";"eee0123456789";"";"auo ""auo""";"auo lcd monitor";"";"GraphicCard 18: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None";"intel corporation";"atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller";"";"256";"";"";"";"";"";"NetworkAdapter 15: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8";"qualcomm atheros";"ar9285 wireless network adapter";"74:2f:68:8b:fd:c8";"NetworkAdapter 16: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c";"qualcomm atheros";"ar8152 v2.0 fast ethernet";"14:da:e9:42:f6:7c";"SoundCard 19: model nm10/ich7 family high definition audio controller, S/N None";"intel corporation";"nm10/ich7 family high definition audio controller";"";"SoundCard 20: model usb 2.0 uvc vga webcam, S/N 0x0001";"azurewave";"usb 2.0 uvc vga webcam";"0x0001";"";"";"";"";"";"";"";"";"15.7188" +"O48N2";"";"http://localhost/devices/O48N2";"";"";"";"";"";"";"";"";"";"";"laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b";"Laptop";"Netbook";"b8oaas048285";"1001pxd";"asustek computer inc.";"Tue Aug 2 12:56:30 2022";"Workbench 11.0a2";"2022-08-02 12:56:30.154343+02:00";"";"";"";"";"intel atom cpu n455 @ 2.66ghz";"1024";"238475";"Processor 7: model intel atom cpu n455 @ 2.66ghz, S/N None";"intel corp.";"intel atom cpu n455 @ 2.66ghz";"";"1";"2.667";"6666.24";"164.0803";"";"";"";"";"";"";"";"";"RamModule 11: model None, S/N None";"";"";"";"1024";"667";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"HardDrive 12: model hts54322, S/N e2024242cv86mm";"hitachi";"hts54322";"e2024242cv86mm";"238475";"harddrive-hitachi-hts54322-e2024242cv86mm";"e2024242cv86mm";"238475";"Workbench 11.0a2";"Success";"";"EraseBasic";"Shred";"1:16:49";"2022-08-02 12:56:30.100462+02:00";"✓ – StepRandom 1:16:49";"2018-07-03 11:15:22.257059+02:00";"2018-07-03 12:32:11.843190+02:00";"66.2";"21.8";"Workbench 11.0a2";"Short";"Failure";"";"";"0";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"Motherboard 13: model 1001pxd, S/N eee0123456720";"asustek computer inc.";"1001pxd";"eee0123456720";"";"";"";"";"GraphicCard 8: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None";"intel corporation";"atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller";"";"256";"";"";"";"";"";"NetworkAdapter 5: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9";"qualcomm atheros";"ar9285 wireless network adapter";"74:2f:68:8b:fd:c9";"NetworkAdapter 6: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b";"qualcomm atheros";"ar8152 v2.0 fast ethernet";"14:da:e9:42:f6:7b";"SoundCard 9: model nm10/ich7 family high definition audio controller, S/N None";"intel corporation";"nm10/ich7 family high definition audio controller";"";"SoundCard 10: model usb 2.0 uvc vga webcam, S/N 0x0001";"azurewave";"usb 2.0 uvc vga webcam";"0x0001";"";"";"";"";"";"";"";"";"15.7188" +"X4ZV3";"";"http://localhost/devices/X4ZV3";"";"";"";"";"";"";"";"";"";"";"laptop-asustek_computer_inc-1001pxd-b8oaas048287-14:da:e9:42:f6:7c";"Laptop";"Netbook";"b8oaas048287";"1001pxd";"asustek computer inc.";"Tue Aug 2 12:56:30 2022";"Workbench 11.0b11";"2022-08-02 12:56:30.459041+02:00";"";"";"";"";"intel atom cpu n455 @ 1.66ghz";"2048";"558558";"Processor 28: model intel atom cpu n455 @ 1.66ghz, S/N None";"intel corp.";"intel atom cpu n455 @ 1.66ghz";"";"1";"1.667";"6666.24";"164.0803";"";"";"";"";"";"";"";"";"RamModule 32: model None, S/N None";"";"";"";"1024";"667";"RamModule 33: model 48594d503131325336344350362d53362020, S/N 4f43487b";"hynix semiconductor";"48594d503131325336344350362d53362020";"4f43487b";"1024";"667";"";"";"";"";"";"";"";"";"";"";"";"";"HardDrive 34: model hts54322, S/N e2024242cv86hj";"hitachi";"hts54322";"e2024242cv86hj";"238475";"harddrive-hitachi-hts54322-e2024242cv86hj";"e2024242cv86hj";"238475";"Workbench 11.0b11";"Success";"";"EraseBasic";"Shred";"1:16:49";"2022-08-02 12:56:30.400450+02:00";"✓ – StepRandom 1:16:49";"2018-07-03 11:15:22.257059+02:00";"2018-07-03 12:32:11.843190+02:00";"66.2";"21.8";"Workbench 11.0b11";"Extended";"Failure";"";"";"0";"DataStorage 35: model wdc wd1600bevt-2, S/N wd-wx11a80w7430";"western digital";"wdc wd1600bevt-2";"wd-wx11a80w7430";"160041";"datastorage-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430";"wd-wx11a80w7430";"160041";"Workbench 11.0b11";"Failure";"";"EraseBasic";"Shred";"0:45:36";"2022-08-02 12:56:30.402600+02:00";"✓ – StepRandom 0:45:36";"2019-10-23 09:49:54.410830+02:00";"2019-10-23 10:35:31.400587+02:00";"41.6";"17.3";"Workbench 11.0b11";"Short";"Success";"5293";"195 days, 12:00:00";"4692";"SolidStateDrive 36: model wdc wd1600bevt-2, S/N wd-wx11a80w7430";"western digital";"wdc wd1600bevt-2";"wd-wx11a80w7430";"160042";"solidstatedrive-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430";"wd-wx11a80w7430";"160042";"Workbench 11.0b11";"Success";"";"EraseSectors";"Badblocks";"1:46:03";"2022-08-02 12:56:30.406477+02:00";"✓ – StepRandom 0:46:03,✓ – StepZero 1:00:00";"2019-08-19 18:48:19.690458+02:00,2019-08-19 19:34:22.690458+02:00";"2019-08-19 19:34:22.930562+02:00,2019-08-19 20:34:22.930562+02:00";"41.1";"17.1";"Workbench 11.0b11";"Short";"Success";"5231";"194 days, 17:00:00";"4673";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"Motherboard 37: model 1001pxd, S/N eee0123456789";"asustek computer inc.";"1001pxd";"eee0123456789";"";"auo ""auo""";"auo lcd monitor";"";"GraphicCard 29: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None";"intel corporation";"atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller";"";"256";"";"";"";"";"";"NetworkAdapter 26: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8";"qualcomm atheros";"ar9285 wireless network adapter";"74:2f:68:8b:fd:c8";"NetworkAdapter 27: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c";"qualcomm atheros";"ar8152 v2.0 fast ethernet";"14:da:e9:42:f6:7c";"SoundCard 30: model nm10/ich7 family high definition audio controller, S/N None";"intel corporation";"nm10/ich7 family high definition audio controller";"";"SoundCard 31: model usb 2.0 uvc vga webcam, S/N 0x0001";"azurewave";"usb 2.0 uvc vga webcam";"0x0001";"";"";"";"";"";"";"";"";"15.7188" diff --git a/tests/test_basic.py b/tests/test_basic.py index 6b890ea3..39b2ca25 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -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', diff --git a/tests/test_device.py b/tests/test_device.py index 67d94d6a..9f3b200d 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -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]) - diff --git a/tests/test_device_find.py b/tests/test_device_find.py index 7d18a991..646a3bb0 100644 --- a/tests/test_device_find.py +++ b/tests/test_device_find.py @@ -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 diff --git a/tests/test_documents.py b/tests/test_documents.py index 5a4fa35c..bc0ae40e 100644 --- a/tests/test_documents.py +++ b/tests/test_documents.py @@ -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 diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 51546a56..d881d524 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -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' diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index bcefe28b..49c151ab 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -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 "Recycling" 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 "Use" 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 "Refurbish" 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 "Management" 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 "Allocate" 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 "Deallocate" 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 "ToPrepare" 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 "Prepare" 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 "ToRepair" 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 "Ready" 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 "DataWipe" 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 "{dhid}" 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 "sid" 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() diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index a3183533..5888b05c 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -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 diff --git a/tests/test_system_uuid.py b/tests/test_system_uuid.py index e8f62956..71eae0e7 100644 --- a/tests/test_system_uuid.py +++ b/tests/test_system_uuid.py @@ -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' diff --git a/tests/test_tag.py b/tests/test_tag.py index f852a206..b30cf592 100644 --- a/tests/test_tag.py +++ b/tests/test_tag.py @@ -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 diff --git a/tests/test_workbench.py b/tests/test_workbench.py index e91a4107..e7ea80b8 100644 --- a/tests/test_workbench.py +++ b/tests/test_workbench.py @@ -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