Merge pull request #329 from eReuse/feature/3702-update-binding

Feature/3702 update binding
This commit is contained in:
cayop 2022-09-02 17:01:18 +02:00 committed by GitHub
commit ae653f0987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 948 additions and 626 deletions

View File

@ -75,7 +75,7 @@ class InventoryView(LoginMixin, SnapshotMixin):
self.response = jsonify(
{
'url': snapshot.device.url.to_text(),
'dhid': snapshot.device.devicehub_id,
'dhid': snapshot.device.dhid,
'sid': snapshot.sid,
}
)

View File

@ -110,7 +110,13 @@ class AdvancedSearchForm(FlaskForm):
self.search(dhids)
def search(self, dhids):
self.devices = Device.query.filter(Device.devicehub_id.in_(dhids))
query = Device.query.filter(Device.owner_id == g.user.id)
self.devices = query.join(Device.placeholder).filter(
or_(
Device.devicehub_id.in_(dhids),
Placeholder.phid.in_(dhids),
)
)
class FilterForm(FlaskForm):
@ -137,11 +143,11 @@ class FilterForm(FlaskForm):
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)).filter(
Device.binding == None
Device.binding == None # noqa: E711
)
else:
self.devices = Device.query.filter(Device.owner_id == g.user.id).filter(
Device.binding == None
Device.binding == None # noqa: E711
)
if self.only_unassigned:
self.devices = self.devices.filter_by(lots=None)
@ -364,18 +370,6 @@ class NewDeviceForm(FlaskForm):
if not self.generation.data:
self.generation.data = 1
if not self.weight.data:
self.weight.data = 0.1
if not self.height.data:
self.height.data = 0.1
if not self.width.data:
self.width.data = 0.1
if not self.depth.data:
self.depth.data = 0.1
def reset_from_obj(self):
if not self._obj:
return
@ -449,24 +443,28 @@ class NewDeviceForm(FlaskForm):
error = ["Not a correct value"]
is_valid = super().validate(extra_validators)
if self.generation.data < 1:
if self.generation.data and self.generation.data < 1:
self.generation.errors = error
is_valid = False
if self.weight.data < 0.1:
self.weight.errors = error
if self.weight.data and not (0.1 <= self.weight.data <= 5):
txt = ["Supported values between 0.1 and 5"]
self.weight.errors = txt
is_valid = False
if self.height.data < 0.1:
self.height.errors = error
if self.height.data and not (0.1 <= self.height.data <= 5):
txt = ["Supported values between 0.1 and 5"]
self.height.errors = txt
is_valid = False
if self.width.data < 0.1:
self.width.errors = error
if self.width.data and not (0.1 <= self.width.data <= 5):
txt = ["Supported values between 0.1 and 5"]
self.width.errors = txt
is_valid = False
if self.depth.data < 0.1:
self.depth.errors = error
if self.depth.data and not (0.1 <= self.depth.data <= 5):
txt = ["Supported values between 0.1 and 5"]
self.depth.errors = txt
is_valid = False
if self.imei.data and self.amount.data == 1:
@ -655,19 +653,30 @@ class NewDeviceForm(FlaskForm):
class TagDeviceForm(FlaskForm):
tag = SelectField('Tag', choices=[])
device = StringField('Device', [validators.Optional()])
tag = SelectField(
'Tag',
choices=[],
render_kw={
'class': 'form-control selectpicker',
'data-live-search': 'true',
},
)
def __init__(self, *args, **kwargs):
self.delete = kwargs.pop('delete', None)
self.device_id = kwargs.pop('device', None)
self.dhid = kwargs.pop('dhid', None)
self._device = (
Device.query.filter(Device.devicehub_id == self.dhid)
.filter(Device.owner_id == g.user.id)
.one()
)
super().__init__(*args, **kwargs)
if self.delete:
tags = (
Tag.query.filter(Tag.owner_id == g.user.id)
.filter_by(device_id=self.device_id)
.filter_by(device_id=self._device.id)
.order_by(Tag.id)
)
else:
@ -695,20 +704,6 @@ class TagDeviceForm(FlaskForm):
self.tag.errors = [("This tag is actualy in use.")]
return False
if self.device.data:
try:
self.device.data = int(self.device.data.split(',')[-1])
except: # noqa: E722
self.device.data = None
if self.device_id or self.device.data:
self.device_id = self.device_id or self.device.data
self._device = (
Device.query.filter(Device.id == self.device_id)
.filter(Device.owner_id == g.user.id)
.one()
)
return True
def save(self):
@ -1667,8 +1662,8 @@ class BindingForm(FlaskForm):
self.phid.errors = [txt]
return False
if self.device.is_abstract() != 'Abstract':
txt = "This is not a abstract device."
if self.device.is_abstract() not in ['Abstract', 'Real']:
txt = "This is not a Abstract or Real device."
self.phid.errors = [txt]
return False
@ -1682,7 +1677,7 @@ class BindingForm(FlaskForm):
self.phid.errors = [txt]
return False
if self.placeholder.binding:
if self.placeholder.status not in ['Abstract', 'Real']:
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]

View File

@ -80,7 +80,6 @@ class DeviceListMixin(GenericMixin):
self.context.update(
{
'devices': devices,
'form_tag_device': TagDeviceForm(),
'form_new_action': form_new_action,
'form_new_allocate': AllocateForm(lot=lot_id),
'form_new_datawipe': DataWipeForm(lot=lot_id),
@ -148,27 +147,51 @@ class DeviceDetailView(GenericMixin):
.one()
)
form_binding = BindingForm(device=device)
form_tags = TagDeviceForm(dhid=id)
self.context.update(
{
'device': device,
'placeholder': device.binding or device.placeholder,
'page_title': 'Device {}'.format(device.devicehub_id),
'form_tag_device': form_tags,
}
)
return flask.render_template(self.template_name, **self.context)
class BindingSearchView(GenericMixin):
methods = ['GET', 'POST']
decorators = [login_required]
template_name = 'inventory/binding_search.html'
def dispatch_request(self, dhid):
self.get_context()
device = (
Device.query.filter(Device.owner_id == current_user.id)
.filter(Device.devicehub_id == dhid)
.one()
)
form_binding = BindingForm(device=device)
self.context.update(
{
'page_title': 'Search a Device for to do a binding from {}'.format(
device.devicehub_id
),
'form_binding': form_binding,
'active_binding': False,
'device': device,
}
)
if form_binding.validate_on_submit():
next_url = url_for(
'inventory.binding',
dhid=form_binding.device.devicehub_id,
dhid=dhid,
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)
@ -179,71 +202,113 @@ class BindingView(GenericMixin):
template_name = 'inventory/binding.html'
def dispatch_request(self, dhid, phid):
self.phid = phid
self.dhid = dhid
self.next_url = url_for('inventory.device_details', id=dhid)
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 device.is_abstract() != 'Abstract':
next_url = url_for('inventory.device_details', id=dhid)
messages.error('Device "{}" not is a Abstract device!'.format(dhid))
return flask.redirect(next_url)
if device.placeholder:
device = device.placeholder.binding
dhid = device.devicehub_id
self.get_objects()
if self.check_errors():
return flask.redirect(self.next_url)
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)
for ac in old_device_placeholder.actions:
ac.devices.add(placeholder.device)
ac.devices.remove(old_device_placeholder)
for act in ac.actions_device:
if act.device == old_device_placeholder:
db.session.delete(act)
for tag in list(old_device_placeholder.tags):
tag.device = placeholder.device
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)
return self.post()
self.context.update(
{
'device': device.binding.device,
'placeholder': placeholder,
'new_placeholder': self.new_placeholder,
'old_placeholder': self.old_placeholder,
'page_title': 'Binding confirm',
'actions': list(device.binding.device.actions)
+ list(placeholder.device.actions),
'tags': list(device.binding.device.tags)
+ list(placeholder.device.tags),
'actions': list(self.old_device.actions)
+ list(self.new_device.actions),
'tags': list(self.old_device.tags) + list(self.new_device.tags),
'dhid': self.dhid,
}
)
return flask.render_template(self.template_name, **self.context)
def check_errors(self):
if not self.new_placeholder:
messages.error('Device Phid: "{}" not exist!'.format(self.phid))
return True
if self.old_device.placeholder.status != 'Abstract':
messages.error(
'Device Dhid: "{}" is not a Abstract device!'.format(self.dhid)
)
return True
if self.new_placeholder.status == 'Twin':
messages.error('Device Phid: "{}" is a Twin device!'.format(self.phid))
return True
if self.new_placeholder.status == self.old_placeholder.status:
txt = 'Device Phid: "{}" and device Dhid: "{}" have the same status, "{}"!'.format(
self.phid, self.dhid, self.new_placeholder.status
)
messages.error(txt)
return True
def get_objects(self):
self.old_device = (
Device.query.filter(Device.owner_id == g.user.id)
.filter(Device.devicehub_id == self.dhid)
.one()
)
self.new_placeholder = (
Placeholder.query.filter(Placeholder.owner_id == g.user.id)
.filter(Placeholder.phid == self.phid)
.first()
)
if not self.new_placeholder:
return
if self.old_device.placeholder.status == 'Abstract':
self.new_device = self.new_placeholder.device
self.old_placeholder = self.old_device.placeholder
elif self.old_device.placeholder.status == 'Real':
self.new_device = self.old_device
self.old_placeholder = self.new_placeholder
self.old_device = self.old_placeholder.device
self.new_placeholder = self.new_device.placeholder
self.abstract_device = self.old_placeholder.binding
self.real_dhid = self.new_device.devicehub_id
self.real_phid = self.new_placeholder.phid
self.abstract_dhid = self.old_device.devicehub_id
self.abstract_phid = self.old_placeholder.phid
def post(self):
for plog in PlaceholdersLog.query.filter_by(
placeholder_id=self.old_placeholder.id
):
db.session.delete(plog)
for ac in self.old_device.actions:
ac.devices.add(self.new_device)
ac.devices.remove(self.old_device)
for act in ac.actions_device:
if act.device == self.old_device:
db.session.delete(act)
for tag in list(self.old_device.tags):
tag.device = self.new_device
db.session.delete(self.old_device)
self.abstract_device.binding = self.new_placeholder
db.session.commit()
next_url = url_for('inventory.device_details', id=self.real_dhid)
txt = 'Device real with PHID: {} and DHID: {} bind successfully with '
txt += 'device abstract PHID: {} DHID: {}.'
messages.success(
txt.format(
self.real_phid, self.real_dhid, self.abstract_phid, self.abstract_dhid
)
)
return flask.redirect(next_url)
class UnBindingView(GenericMixin):
methods = ['GET', 'POST']
@ -256,31 +321,33 @@ class UnBindingView(GenericMixin):
.filter(Placeholder.phid == phid)
.one()
)
if not placeholder.binding:
if not placeholder.binding or placeholder.status != 'Twin':
next_url = url_for(
'inventory.device_details', id=placeholder.device.devicehub_id
)
return flask.redirect(next_url)
device = placeholder.binding
if device.is_abstract() != 'Twin':
dhid = device.devicehub_id
if placeholder.status != 'Twin':
dhid = placeholder.device.devicehub_id
next_url = url_for('inventory.device_details', id=dhid)
messages.error('Device "{}" not is a Twin device!'.format(dhid))
messages.error('Device Dhid: "{}" not is a Twin device!'.format(dhid))
return flask.redirect(next_url)
self.get_context()
if request.method == 'POST':
new_device = self.clone_device(device)
next_url = url_for('inventory.device_details', id=new_device.devicehub_id)
messages.success('Device "{}" unbind successfully!'.format(phid))
dhid = placeholder.device.devicehub_id
self.clone_device(placeholder.binding)
next_url = url_for('inventory.device_details', id=dhid)
messages.success(
'Device with PHID:"{}" and DHID: {} unbind successfully!'.format(
phid, dhid
)
)
return flask.redirect(next_url)
self.context.update(
{
'device': device,
'placeholder': placeholder,
'page_title': 'Unbinding confirm',
}
@ -476,12 +543,15 @@ class TagLinkDeviceView(View):
methods = ['POST']
decorators = [login_required]
def dispatch_request(self):
form = TagDeviceForm()
def dispatch_request(self, dhid):
form = TagDeviceForm(dhid=dhid)
if form.validate_on_submit():
tag = form.tag.data
form.save()
return flask.redirect(request.referrer)
next_url = url_for('inventory.device_details', id=dhid)
messages.success('Tag {} was linked successfully!'.format(tag))
return flask.redirect(next_url)
class TagUnlinkDeviceView(GenericMixin):
@ -489,19 +559,20 @@ class TagUnlinkDeviceView(GenericMixin):
decorators = [login_required]
template_name = 'inventory/tag_unlink_device.html'
def dispatch_request(self, id):
def dispatch_request(self, dhid):
self.get_context()
form = TagDeviceForm(delete=True, device=id)
form = TagDeviceForm(delete=True, dhid=dhid)
if form.validate_on_submit():
form.remove()
next_url = url_for('inventory.devicelist')
next_url = url_for('inventory.device_details', id=dhid)
messages.success('Tag {} was unlinked successfully!'.format(form.tag.data))
return flask.redirect(next_url)
self.context.update(
{
'form': form,
'referrer': request.referrer,
'dhid': dhid,
}
)
@ -1145,10 +1216,11 @@ devices.add_url_rule(
'/device/edit/<string:id>/', view_func=DeviceEditView.as_view('device_edit')
)
devices.add_url_rule(
'/tag/devices/add/', view_func=TagLinkDeviceView.as_view('tag_devices_add')
'/tag/devices/<string:dhid>/add/',
view_func=TagLinkDeviceView.as_view('tag_devices_add'),
)
devices.add_url_rule(
'/tag/devices/<int:id>/del/',
'/tag/devices/<string:dhid>/del/',
view_func=TagUnlinkDeviceView.as_view('tag_devices_del'),
)
devices.add_url_rule(
@ -1192,3 +1264,7 @@ devices.add_url_rule(
devices.add_url_rule(
'/unbinding/<string:phid>/', view_func=UnBindingView.as_view('unbinding')
)
devices.add_url_rule(
'/device/<string:dhid>/binding/',
view_func=BindingSearchView.as_view('binding_search'),
)

View File

@ -63,6 +63,9 @@ def clone_device(device):
if device.type == "Battery":
device.size
old_devicehub_id = device.devicehub_id
dict_device = copy.copy(device.__dict__)
dict_device.pop('_sa_instance_state')
dict_device.pop('id', None)
@ -73,6 +76,8 @@ def clone_device(device):
dict_device.pop('tags', None)
dict_device.pop('system_uuid', None)
new_device = device.__class__(**dict_device)
new_device.devicehub_id = old_devicehub_id
device.devicehub_id = None
db.session.add(new_device)
if hasattr(device, 'components'):

View File

@ -330,7 +330,7 @@ class Device(Thing):
@property
def url(self) -> urlutils.URL:
"""The URL where to GET this device."""
return urlutils.URL(url_for_resource(Device, item_id=self.devicehub_id))
return urlutils.URL(url_for_resource(Device, item_id=self.dhid))
@property
def rate(self):
@ -615,6 +615,14 @@ class Device(Thing):
model = self.model or ''
return f'{type} {manufacturer} {model}'
@property
def dhid(self):
if self.placeholder:
return self.placeholder.device.devicehub_id
if self.binding:
return self.binding.device.devicehub_id
return self.devicehub_id
@declared_attr
def __mapper_args__(cls):
"""Defines inheritance.
@ -924,6 +932,25 @@ class Placeholder(Thing):
)
owner = db.relationship(User, primaryjoin=owner_id == User.id)
@property
def actions(self):
actions = list(self.device.actions) or []
if self.binding:
actions.extend(list(self.binding.actions))
actions = sorted(actions, key=lambda x: x.created)
actions.reverse()
return actions
@property
def status(self):
if self.is_abstract:
return 'Abstract'
if self.binding:
return 'Twin'
return 'Real'
class Computer(Device):
"""A chassis with components inside that can be processed
@ -1022,10 +1049,14 @@ class Computer(Device):
"""Returns the privacy of all ``DataStorage`` components when
it is not None.
"""
components = self.components
if self.placeholder and self.placeholder.binding:
components = self.placeholder.binding.components
return set(
privacy
for privacy in (
hdd.privacy for hdd in self.components if isinstance(hdd, DataStorage)
hdd.privacy for hdd in components if isinstance(hdd, DataStorage)
)
if privacy
)

View File

@ -114,7 +114,7 @@ class Device(Thing):
sku = SanitizedStr(description=m.Device.sku.comment)
image = URL(description=m.Device.image.comment)
allocated = Boolean(description=m.Device.allocated.comment)
devicehub_id = SanitizedStr(
dhid = SanitizedStr(
data_key='devicehubID', description=m.Device.devicehub_id.comment
)

View File

@ -41,8 +41,8 @@
<div class="container-fluid">
<div class="row">
<div class="page-header col-md-6 col-md-offset-3">
<h1>{{ device.__format__('t') }}<br>
<small>{{ device.__format__('s') }}</small>
<h1>{% if abstract %}Real device {% endif %}{{ device.__format__('t') or '' }}<br>
<small>{{ device.__format__('s') or '' }}</small>
</h1>
</div>
</div>
@ -52,7 +52,7 @@
<div class="col-md-6">
<ul>
{% for key, value in device.public_properties.items() %}
<li>{{ key }}: {{ value }}</li>
<li>{{ key }}: {{ value or '' }}</li>
{% endfor %}
</ul>
{% if isinstance(device, d.Computer) %}
@ -140,17 +140,6 @@
<td></td>
</tr>
{% endif %}
{% if device.rate %}
<tr class="active">
<td class="text-right">
Total rate
</td>
<td>
{{ device.rate.rating_range }}
({{ device.rate.rating }})
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
@ -222,6 +211,181 @@
{% endif %}
</div>
</div>
{% if abstract %}
<div class="row">
<div class="page-header col-md-6 col-md-offset-3">
<hr />
<h1>Abstract device {{ abstract.__format__('t') or '' }}<br>
<small>{{ abstract.__format__('s') or '' }}</small>
</h1>
</div>
</div>
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-6">
<ul>
{% for key, value in abstract.public_properties.items() %}
<li>{{ key }}: {{ value or '' }}</li>
{% endfor %}
</ul>
{% if isinstance(abstract, d.Computer) %}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th>Range</th>
</tr>
</thead>
<tbody>
{% if abstract.processor_model %}
<tr>
<td>
CPU {{ abstract.processor_model }}
</td>
<td>
Processor Rate = {% if abstract.rate %}
{{ abstract.rate.processor_range }}
({{ abstract.rate.processor }})
{% endif %}
</td>
</tr>
{% endif %}
{% if abstract.ram_size %}
<tr>
<td>
RAM {{ abstract.ram_size // 1000 }} GB
{{ macros.component_type(abstract.components, 'RamModule') }}
</td>
<td>
RAM Rate = {% if abstract.rate %}
{{ abstract.rate.ram_range }}
({{ abstract.rate.ram }})
{% endif %}
</td>
</tr>
{% endif %}
{% if abstract.data_storage_size %}
<tr>
<td>
Data Storage {{ abstract.data_storage_size // 1000 }} GB
{{ macros.component_type(abstract.components, 'SolidStateDrive') }}
{{ macros.component_type(abstract.components, 'HardDrive') }}
</td>
<td>
Data Storage Rate = {% if abstract.rate %}
{{ abstract.rate.data_storage_range }}
({{ abstract.rate.data_storage }})
{% endif %}
</td>
</tr>
{% endif %}
{% if abstract.graphic_card_model %}
<tr>
<td>
Graphics {{ abstract.graphic_card_model }}
{{ macros.component_type(abstract.components, 'GraphicCard') }}
</td>
<td></td>
</tr>
{% endif %}
{% if abstract.network_speeds %}
<tr>
<td>
Network
{% if abstract.network_speeds[0] %}
Ethernet
{% if abstract.network_speeds[0] != None %}
max. {{ abstract.network_speeds[0] }} Mbps
{% endif %}
{% endif %}
{% if abstract.network_speeds[0] and abstract.network_speeds[1] %}
+
{% endif %}
{% if abstract.network_speeds[1] %}
WiFi
{% if abstract.network_speeds[1] != None %}
max. {{ abstract.network_speeds[1] }} Mbps
{% endif %}
{% endif %}
{{ macros.component_type(abstract.components, 'NetworkAdapter') }}
</td>
<td></td>
</tr>
{% endif %}
</tbody>
</table>
</div>
<h4>Actual Status</h4>
<ol>
<li>
<strong>
Lifecycle Status
</strong>
{% if abstract.status %}
{{ abstract.status.type }}
{% endif %}
</li>
<li>
<strong>
Allocate Status
</strong>
{% if abstract.allocated_status %}
{{ abstract.allocated_status.type }}
{% endif %}
</li>
<li>
<strong>
Physical Status
</strong>
{% if abstract.physical_status %}
{{ abstract.physical_status.type }}
{% endif %}
</li>
</ol>
<h4>Public traceability log of the abstract</h4>
<div class="text-right">
<small>Latest one.</small>
</div>
<ol>
{% for action in abstract.public_actions %}
<li>
<strong>
{{ abstract.is_status(action) }}
{% if not abstract.is_status(action) %}
{{ action.type }}
{% endif %}
</strong>
{% if abstract.is_status(action) %}
{{ action }} {{ action.type }}
{% else %}
{{ action }}
{% endif %}
<br>
<div class="text-muted">
<small>
{{ action._date_str }}
</small>
</div>
{% if action.certificate %}
<a href="{{ action.certificate.to_text() }}">See the certificate</a>
{% endif %}
</li>
{% endfor %}
</ol>
<div class="text-right">
<small>Oldest one.</small>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
<footer class="container-fluid footer">
<div class="row">

View File

@ -2,6 +2,7 @@ import datetime
import uuid
from itertools import filterfalse
import flask
import marshmallow
from flask import Response
from flask import current_app as app
@ -110,9 +111,7 @@ 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'
@ -138,10 +137,17 @@ 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()
return render_template('devices/layout.html', device=device, states=states)
device = Device.query.filter_by(devicehub_id=id, active=True).one()
abstract = None
if device.binding:
return flask.redirect(device.public_link)
if device.is_abstract() == 'Twin':
abstract = device.placeholder.binding
return render_template(
'devices/layout.html', device=device, states=states, abstract=abstract
)
@auth.Auth.requires_auth
def one_private(self, id: str):

View File

@ -276,10 +276,10 @@ class DeviceRow(BaseDeviceRow):
software=snapshot.software.name, version=snapshot.version
)
# General information about device
self['DHID'] = device.devicehub_id
self['DHID'] = self.placeholder.device.dhid
self['DocumentID'] = self.document_id
self['Public Link'] = '{url}{id}'.format(
url=url_for('Device.main', _external=True), id=device.devicehub_id
url=url_for('Device.main', _external=True), id=device.dhid
)
self['Lots'] = ', '.join([x.name for x in self.device.lots])
for i, tag in zip(range(1, 3), device.tags):

View File

@ -42,13 +42,13 @@
<dt>Computer where was erase:</dt>
<dd>Title: {{ erasure.parent.__format__('ts') }}</dd>
<dd>DevicehubID: {{ erasure.parent.devicehub_id }}</dd>
<dd>DevicehubID: {{ erasure.parent.dhid }}</dd>
<dd>Hid: {{ erasure.parent.hid }}</dd>
<dd>Tags: {{ erasure.parent.tags }}</dd>
<dt>Computer where it resides:</dt>
<dd>Title: {{ erasure.device.parent.__format__('ts') }}</dd>
<dd>DevicehubID: {{ erasure.device.parent.devicehub_id }}</dd>
<dd>DevicehubID: {{ erasure.device.parent.dhid }}</dd>
<dd>Hid: {{ erasure.device.parent.hid }}</dd>
<dd>Tags: {{ erasure.device.parent.tags }}</dd>

View File

@ -178,8 +178,6 @@ function deviceSelect() {
$("#addingLotModal .btn-primary").hide();
$("#removeLotModal .pol").show();
$("#removeLotModal .btn-primary").hide();
$("#addingTagModal .pol").show();
$("#addingTagModal .btn-primary").hide();
$("#actionModal .pol").show();
$("#actionModal .btn-primary").hide();
$("#allocateModal .pol").show();
@ -197,8 +195,6 @@ function deviceSelect() {
$("#allocateModal .btn-primary").show();
$("#datawipeModal .pol").hide();
$("#datawipeModal .btn-primary").show();
$("#addingTagModal .pol").hide();
$("#addingTagModal .btn-primary").show();
}
}
@ -214,33 +210,6 @@ function removeLot() {
$("#activeRemoveLotModal").click();
}
function removeTag() {
const devices = TableController.getSelectedDevices();
const devices_id = devices.map(dev => $(dev).attr('data'));
if (devices_id.length == 1) {
const url = "/inventory/tag/devices/".concat(devices_id[0], "/del/");
window.location.href = url;
} else {
$("#unlinkTagAlertModal").click();
}
}
function addTag() {
const devices = TableController.getSelectedDevices();
const devices_id = devices.map(dev => $(dev).attr('data'));
if (devices_id.length == 1) {
$("#addingTagModal .pol").hide();
$("#addingTagModal .btn-primary").show();
} else {
$("#addingTagModal .pol").show();
$("#addingTagModal .btn-primary").hide();
}
$("#addTagAlertModal").click();
}
function select_shift() {
const chkboxes = $('.deviceSelect');
var lastChecked = null;

View File

@ -163,9 +163,6 @@ function deviceSelect() {
$("#removeLotModal .pol").show();
$("#removeLotModal .btn-primary").hide();
$("#addingTagModal .pol").show();
$("#addingTagModal .btn-primary").hide();
$("#actionModal .pol").show();
$("#actionModal .btn-primary").hide();
@ -189,9 +186,6 @@ function deviceSelect() {
$("#datawipeModal .pol").hide();
$("#datawipeModal .btn-primary").show();
$("#addingTagModal .pol").hide();
$("#addingTagModal .btn-primary").show();
}
}
@ -205,31 +199,6 @@ function removeLot() {
$("#activeRemoveLotModal").click();
}
function removeTag() {
const devices = TableController.getSelectedDevices();
const devices_id = devices.map(dev => $(dev).attr("data"));
if (devices_id.length == 1) {
const url = `/inventory/tag/devices/${devices_id[0]}/del/`;
window.location.href = url;
} else {
$("#unlinkTagAlertModal").click();
}
}
function addTag() {
const devices = TableController.getSelectedDevices();
const devices_id = devices.map(dev => $(dev).attr("data"));
if (devices_id.length == 1) {
$("#addingTagModal .pol").hide();
$("#addingTagModal .btn-primary").show();
} else {
$("#addingTagModal .pol").show();
$("#addingTagModal .btn-primary").hide();
}
$("#addTagAlertModal").click();
}
function select_shift() {
const chkboxes = $(".deviceSelect");
let lastChecked = null;

View File

@ -256,6 +256,14 @@
</ul>
</li><!-- End Temporal Lots Nav -->
<li class="nav-heading">Unique Identifiers (Tags)</li>
<li class="nav-item">
<a class="nav-link collapsed" href="{{ url_for('labels.label_list') }}">
<i class="bi bi-tag"></i><span>UI Management</span>
</a>
</li><!-- End Unique Identifiers -->
</ul>
</aside><!-- End Sidebar-->

View File

@ -7,16 +7,12 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{{ url_for('inventory.tag_devices_add') }}" method="post">
{{ form_tag_device.csrf_token }}
<form action="{{ url_for('inventory.tag_devices_add', dhid=device.devicehub_id) }}" method="post">
<div class="modal-body">
Please write a name of a unique identifier
<select class="form-control selectpicker" id="selectTag" name="tag" data-live-search="true">
{% for tag in tags %}
<option value="{{ tag.id }}">{{ tag.id }}</option>
{% endfor %}
</select>
<input class="devicesList" type="hidden" name="device" />
{% for f in form_tag_device %}
{{ f }}
{% endfor %}
<p class="text-danger pol">
You need select first one device and only one for add this in a unique identifier
</p>

View File

@ -20,115 +20,126 @@
<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>
<p>This is a binding between:&nbsp;
<ul>
{% if new_placeholder.is_abstract %}
<li>1. Device abstract with DHID:<b>{{ new_placeholder.device.devicehub_id }}</b> and PHID: <b>{{ new_placeholder.phid }}</b></li>
<li>2. Device real with DHID: <b>{{ old_placeholder.device.devicehub_id }}</b> and PHID:<b>{{ old_placeholder.phid }}</b></li>
{% else %}
<li>1. Device abstract with DHID:<b>{{ old_placeholder.device.devicehub_id }}</b> and PHID: <b>{{ old_placeholder.phid }}</b></li>
<li>2. Device real with DHID: <b>{{ new_placeholder.device.devicehub_id }}</b> and PHID:<b>{{ new_placeholder.phid }}</b></li>
{% endif %}
</ul>
</p>
<p>The DHID and PHID information of the abstract will be lost.</p>
<p>The information in <span class="text-danger">red colour</span> will be losted and replaced by the information in <span class="text-success">green colour</span>.<br />
The information in <span class="text-warning">orange</span> will be replaced by the information in <span class="text-success">green</span> and you always can recover
it by doing an unbinding action or find this information into device details web.
</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>
<th scope="col">Info Twin device</th>
<th scope="col">Info Abstract device</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>
<td class="table-success text-right">{{ new_placeholder.device.manufacturer or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.model or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.serial_number or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.brand or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.sku or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.generation or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.version or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.weight or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.width or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.height or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.depth or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.color or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.production_date or '' }}</td>
<td class="table-warning">{{ old_placeholder.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>
<td class="table-success">{{ new_placeholder.device.variant or '' }}</td>
<td class="table-warning">{{ old_placeholder.device.variant or '' }}</td>
</tr>
</tbody>
</table>
<br />
{% if placeholder.device.components or device.components %}
{% if new_placeholder.device.components or old_placeholder.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>
<th scope="col">Info Twin device</th>
<th scope="col">Info Abstract device</th>
</tr>
</thead>
<tbody>
<tr>
<td class="table-success text-right">
{% for c in placeholder.device.components %}
{% for c in new_placeholder.device.components %}
* {{ c.verbose_name }}<br />
{% endfor %}
</td>
<td class="table-danger">
{% for c in device.components %}
<td class="table-warning">
{% for c in old_placeholder.device.components %}
* {{ c.verbose_name }}<br />
{% endfor %}
</td>
@ -141,11 +152,14 @@
{% if actions %}
<h2>Actions</h2>
<p>
The actions will become real device and will no longer be in the abstract
</p>
<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>
<th scope="col">Info Twin device</th>
<th scope="col">Info Abstract device</th>
</tr>
</thead>
<tbody>
@ -166,11 +180,14 @@
{% if tags %}
<h2>Tags</h2>
<p>
The tags will become real device and will no longer be in the abstract
</p>
<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>
<th scope="col">Info Twin device</th>
<th scope="col">Info Abstract device</th>
</tr>
</thead>
<tbody>
@ -189,7 +206,7 @@
<div>
<form method="post">
<a href="{{ url_for('inventory.device_details', id=device.placeholder.binding.devicehub_id) }}" class="btn btn-danger">Cancel</a>
<a href="{{ url_for('inventory.device_details', id=dhid) }}" class="btn btn-danger">Cancel</a>
<button class="btn btn-primary" type="submit">Confirm</button>
</form>
</div>

View File

@ -0,0 +1,74 @@
{% extends "ereuse_devicehub/base_site.html" %}
{% block main %}
<div class="pagetitle">
<h1>Inventory</h1>
<nav>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('inventory.devicelist')}}">Inventory</a></li>
<li class="breadcrumb-item active">{{ page_title }}</li>
</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 pt-3">
<h3>{{ device.devicehub_id }}</h3>
<!-- Bordered Tabs -->
<div class="tab-content pt-2">
<div class="tab-pane fade show active">
<h5 class="card-title">Binding</h5>
{% if device.is_abstract() == 'Twin' or not device.placeholder %}
<div class="list-group col-6">
<p>
Device with Dhid: {{ device.devicehub_id }} is a Twin device.<br />
If you want to do a binding with this device, you need todo an Unbinding first.<br />
You can to do this in <a href="{{ url_for('inventory.unbinding', phid=device.placeholder.phid) }}" class="help">here</a>.
</p>
</div>
{% else %}
<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.binding_search', dhid=device.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>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock main %}

View File

@ -18,25 +18,48 @@
<div class="card">
<div class="card-body pt-3">
<h3>{{ device.devicehub_id }}</h3>
<h3>{{ placeholder.device.devicehub_id }}</h3>
<div class="tab-pane active show mb-5">
<div class="btn-group dropdown" uib-dropdown="" style="float: right; margin-right: 15px;">
<button id="btnUniqueID" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-tag"></i>
Unique Identifiers (Tags)
</button>
<span class="d-none" id="unlinkTagAlertModal" data-bs-toggle="modal" data-bs-target="#unlinkTagErrorModal"></span>
<span class="d-none" id="addTagAlertModal" data-bs-toggle="modal" data-bs-target="#addingTagModal"></span>
<ul class="dropdown-menu" aria-labelledby="btnUniqueID">
<li>
<a href="javascript:addTag()" class="dropdown-item">
<i class="bi bi-plus"></i>
Add Unique Identifier
</a>
</li>
<li>
<a href="{{ url_for('inventory.tag_devices_del', dhid=placeholder.device.devicehub_id) }}" class="dropdown-item">
<i class="bi bi-x"></i>
Remove Unique Identifier
</a>
</li>
</ul>
</div>
{% if placeholder.status in ['Abstract', 'Real'] %}
<a type="button" href="{{ url_for('inventory.binding_search', dhid=placeholder.device.devicehub_id) }}" class="btn btn-primary" style="float: right; margin-right: 15px;">
Binding
</a>
{% elif placeholder.status == 'Twin' %}
<a type="button" href="{{ url_for('inventory.unbinding', phid=placeholder.phid) }}" class="btn btn-primary" style="float: right; margin-right: 15px;">
Unbinding
</a>
{% endif %}
<div style="display: block;"></div>
</div>
<!-- Bordered Tabs -->
<ul class="nav nav-tabs nav-tabs-bordered">
{% if placeholder %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('inventory.device_details', id=placeholder.device.devicehub_id) }}">Placeholder device</a>
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#details">General details</button>
</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 +73,6 @@
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#status">Status</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#rate">Rate</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#traceability">Traceability log</button>
</li>
@ -62,65 +81,72 @@
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button>
</li>
{% if device.is_abstract() == 'Abstract' %}
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#binding">Binding</button>
</li>
{% endif %}
{% if device.is_abstract() == 'Twin' %}
<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 {% if active_binding %}profile-overview{% else %}show active{% endif %}" id="type">
<h5 class="card-title">Details</h5>
{% if device.placeholder %}
<div class="tab-pane fade show active" id="details">
<h5 class="card-title">Details Real parth</h5>
<div class="row mb-3">
<div class="col-lg-3 col-md-4 label ">
(<a href="{{ url_for('inventory.device_edit', id=device.devicehub_id)}}">Edit Device</a>)
(<a href="{{ url_for('inventory.device_edit', id=placeholder.device.devicehub_id)}}">Edit Device</a>)
</div>
<div class="col-lg-9 col-md-8">{{ device.is_abstract() }}</div>
<div class="col-lg-9 col-md-8">{{ placeholder.status }}</div>
</div>
{% endif %}
{% if device.placeholder %}
<div class="row">
<div class="col-lg-3 col-md-4 label ">Phid</div>
<div class="col-lg-9 col-md-8">{{ device.placeholder.phid }}</div>
<div class="col-lg-9 col-md-8">{{ placeholder.phid }}</div>
</div>
{% endif %}
<div class="row">
<div class="col-lg-3 col-md-4 label ">Type</div>
<div class="col-lg-9 col-md-8">{{ device.type }}</div>
<div class="col-lg-9 col-md-8">{{ placeholder.device.type }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Manufacturer</div>
<div class="col-lg-9 col-md-8">{{ device.manufacturer or ''}}</div>
<div class="col-lg-9 col-md-8">{{ placeholder.device.manufacturer or ''}}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Model</div>
<div class="col-lg-9 col-md-8">{{ device.model or ''}}</div>
<div class="col-lg-9 col-md-8">{{ placeholder.device.model or ''}}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Serial Number</div>
<div class="col-lg-9 col-md-8">{{ device.serial_number or ''}}</div>
<div class="col-lg-9 col-md-8">{{ placeholder.device.serial_number or ''}}</div>
</div>
{% if placeholder.binding %}
<h5 class="card-title">Details Abstract parth</h5>
<div class="row">
<div class="col-lg-3 col-md-4 label ">Type</div>
<div class="col-lg-9 col-md-8">{{ placeholder.binding.type }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Manufacturer</div>
<div class="col-lg-9 col-md-8">{{ placeholder.binding.manufacturer or ''}}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Model</div>
<div class="col-lg-9 col-md-8">{{ placeholder.binding.model or ''}}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Serial Number</div>
<div class="col-lg-9 col-md-8">{{ placeholder.binding.serial_number or ''}}</div>
</div>
{% endif %}
</div>
<div class="tab-pane fade profile-overview" id="lots">
<h5 class="card-title">Incoming Lots</h5>
<div class="row">
{% for lot in device.lots %}
{% for lot in placeholder.device.lots %}
{% if lot.is_incoming %}
<div class="col">
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
@ -134,7 +160,7 @@
<h5 class="card-title">Outgoing Lots</h5>
<div class="row">
{% for lot in device.lots %}
{% for lot in placeholder.device.lots %}
{% if lot.is_outgoing %}
<div class="col">
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
@ -148,7 +174,7 @@
<h5 class="card-title">Temporary Lots</h5>
<div class="row">
{% for lot in device.lots %}
{% for lot in placeholder.device.lots %}
{% if lot.is_temporary %}
<div class="col">
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
@ -165,53 +191,33 @@
<div class="row">
<div class="col-lg-3 col-md-4 label">Physical State</div>
<div class="col-lg-9 col-md-8">
{% if device.physical_status %}
{{ device.physical_status.type }}
{% if placeholder.device.physical_status %}
{{ placeholder.device.physical_status.type }}
{% endif %}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Lifecycle State</div>
<div class="col-lg-9 col-md-8">
{% if device.status %}
{{ device.status.type }}
{% if placeholder.device.status %}
{{ placeholder.device.status.type }}
{% endif %}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Allocated State</div>
<div class="col-lg-9 col-md-8">
{% if device.allocated_status %}
{{ device.allocated_status.type }}
{% if placeholder.device.allocated_status %}
{{ placeholder.device.allocated_status.type }}
{% endif %}
</div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="rate">
<h5 class="card-title">Rate Details</h5>
<div class="row">
<div class="col-lg-3 col-md-4 label">Rating</div>
<div class="col-lg-9 col-md-8">{{ device.rate or '' }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Processor</div>
<div class="col-lg-9 col-md-8">{{ device.rate.processor or '' }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">RAM</div>
<div class="col-lg-9 col-md-8">{{ device.rate.ram or '' }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Data storage</div>
<div class="col-lg-9 col-md-8">{{ device.rate.data_storage or '' }}</div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="traceability">
<h5 class="card-title">Traceability log Details</h5>
<div class="list-group col-6">
{% for action in device.reverse_actions %}
{% for action in placeholder.actions %}
<div class="list-group-item d-flex justify-content-between align-items-center">
{{ action.type }} {{ action.severity }}
<small class="text-muted">{{ action.created.strftime('%H:%M %d-%m-%Y') }}</small>
@ -221,10 +227,15 @@
</div>
<div class="tab-pane fade profile-overview" id="components">
<h5 class="card-title">Components Details</h5>
{% if device.binding %}
<h5 class="card-title">Components Real parth</h5>
<div class="list-group col-6">
{% for component in device.components|sort(attribute='type') %}
{{ placeholder.components or '' }}
</div>
{% if placeholder.binding %}
<h5 class="card-title">Components Abstract parth</h5>
<div class="list-group col-6">
{% for component in placeholder.binding.components|sort(attribute='type') %}
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{ component.type }}</h5>
@ -242,52 +253,27 @@
</div>
{% endfor %}
</div>
{% else %}
<div class="col-6">
{{ device.placeholder.components or '' }}
</div>
{% endif %}
</div>
{% if device.is_abstract() %}
<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=device.devicehub_id) }}" method="post">
{{ form_binding.csrf_token }}
{% for field in form_binding %}
{% if field != form_binding.csrf_token %}
<div class="col-12">
{{ field.label(class_="form-label") }}:
{{ field }}
{% if field.errors %}
<p class="text-danger">
{% for error in field.errors %}
{{ error }}<br/>
{% endfor %}
</p>
{% endif %}
</div>
{% endif %}
{% endfor %}
<div class="col-12 mt-2">
<input type="submit" class="btn btn-primary" value="Search" />
</div>
</form>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</section>
<script>
function addTag() {
const devices_id = [{{ placeholder.device.id }}];
if (devices_id.length == 1) {
$("#addingTagModal .pol").hide();
$("#addingTagModal .btn-primary").show();
} else {
$("#addingTagModal .pol").show();
$("#addingTagModal .btn-primary").hide();
}
$("#addTagAlertModal").click();
}
</script>
{% include "inventory/addDevicestag.html" %}
{% endblock main %}

View File

@ -268,42 +268,11 @@
</ul>
</div>
<div class="btn-group dropdown m-1" uib-dropdown="">
<button id="btnUniqueID" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-tag"></i>
Unique Identifiers (Tags)
</button>
<span class="d-none" id="unlinkTagAlertModal" data-bs-toggle="modal" data-bs-target="#unlinkTagErrorModal"></span>
<span class="d-none" id="addTagAlertModal" data-bs-toggle="modal" data-bs-target="#addingTagModal"></span>
<ul class="dropdown-menu" aria-labelledby="btnUniqueID">
<li>
<a href="javascript:addTag()" class="dropdown-item">
<i class="bi bi-plus"></i>
Add Unique Identifier to selected Device
</a>
</li>
<li>
<a href="javascript:removeTag()" class="dropdown-item">
<i class="bi bi-x"></i>
Remove Unique Identifier from selected Device
</a>
</li>
<li>
<a class="dropdown-item" href="{{ url_for('labels.label_list')}}">
<i class="bi bi-tools"></i>
Unique Identifier Management
</a>
</li>
</ul>
</div>
<div class="btn-group dropdown m-1" uib-dropdown="">
<button id="btnTags" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-tag"></i>
Labels
</button>
<span class="d-none" id="unlinkTagAlertModal" data-bs-toggle="modal" data-bs-target="#unlinkTagErrorModal"></span>
<span class="d-none" id="addTagAlertModal" data-bs-toggle="modal" data-bs-target="#addingTagModal"></span>
<ul class="dropdown-menu" aria-labelledby="btnTags">
<li>
<form id="print_labels" method="post" action="{{ url_for('labels.print_labels') }}">
@ -322,19 +291,9 @@
<div class="btn-group dropdown m-1" uib-dropdown="">
<button id="btnSnapshot" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-laptop"></i>
New Devices
New Devices Real
</button>
<ul class="dropdown-menu" aria-labelledby="btnSnapshot">
<li>
{% if lot %}
<a href="{{ url_for('inventory.lot_upload_snapshot', lot_id=lot.id) }}" class="dropdown-item">
{% else %}
<a href="{{ url_for('inventory.upload_snapshot') }}" class="dropdown-item">
{% endif %}
<i class="bi bi-upload"></i>
Upload Snapshot files
</a>
</li>
<li>
{% if lot %}
<a href="{{ url_for('inventory.lot_upload_placeholder', lot_id=lot.id) }}" class="dropdown-item">
@ -358,6 +317,25 @@
</ul>
</div>
<div class="btn-group dropdown m-1" uib-dropdown="">
<button id="btnSnapshot" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-laptop"></i>
New Devices Abstract
</button>
<ul class="dropdown-menu" aria-labelledby="btnSnapshot">
<li>
{% if lot %}
<a href="{{ url_for('inventory.lot_upload_snapshot', lot_id=lot.id) }}" class="dropdown-item">
{% else %}
<a href="{{ url_for('inventory.upload_snapshot') }}" class="dropdown-item">
{% endif %}
<i class="bi bi-upload"></i>
Upload Snapshot files
</a>
</li>
</ul>
</div>
{% if lot and not lot.is_temporary %}
<div class="btn-group dropdown ml-1" uib-dropdown="">
<button id="btnSnapshot" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
@ -610,14 +588,12 @@
</div>
</div>
</section>
{% include "inventory/addDevicestag.html" %}
{% include "inventory/lot_delete_modal.html" %}
{% include "inventory/actions.html" %}
{% include "inventory/allocate.html" %}
{% include "inventory/data_wipe.html" %}
{% include "inventory/trade.html" %}
{% include "inventory/alert_export_error.html" %}
{% include "inventory/alert_unlink_tag_error.html" %}
{% include "inventory/alert_lots_changes.html" %}
<!-- Custom Code -->

View File

@ -40,18 +40,6 @@
<dt>Data storage:</dt>
<dd>{{ erasure.device.__format__('ts') }}</dd>
<dt>Computer where was erase:</dt>
<dd>Title: {{ erasure.parent.__format__('ts') }}</dd>
<dd>DevicehubID: {{ erasure.parent.devicehub_id }}</dd>
<dd>Hid: {{ erasure.parent.hid }}</dd>
<dd>Tags: {{ erasure.parent.tags }}</dd>
<dt>Computer where it resides:</dt>
<dd>Title: {{ erasure.device.parent.__format__('ts') }}</dd>
<dd>DevicehubID: {{ erasure.device.parent.devicehub_id }}</dd>
<dd>Hid: {{ erasure.device.parent.hid }}</dd>
<dd>Tags: {{ erasure.device.parent.tags }}</dd>
<dt>Erasure:</dt>
<dd>{{ erasure.__format__('ts') }}</dd>
{% if erasure.steps %}

View File

@ -28,7 +28,7 @@
</div>
</div>
</div>
{% if devices %}
{% if devices.count() %}
<div class="card">
<div class="card-body pt-3" style="min-height: 650px;">
<!-- Bordered Tabs -->
@ -178,42 +178,11 @@
</ul>
</div>
<div class="btn-group dropdown m-1" uib-dropdown="">
<button id="btnUniqueID" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-tag"></i>
Unique Identifiers (Tags)
</button>
<span class="d-none" id="unlinkTagAlertModal" data-bs-toggle="modal" data-bs-target="#unlinkTagErrorModal"></span>
<span class="d-none" id="addTagAlertModal" data-bs-toggle="modal" data-bs-target="#addingTagModal"></span>
<ul class="dropdown-menu" aria-labelledby="btnUniqueID">
<li>
<a href="javascript:addTag()" class="dropdown-item">
<i class="bi bi-plus"></i>
Add Unique Identifier to selected Device
</a>
</li>
<li>
<a href="javascript:removeTag()" class="dropdown-item">
<i class="bi bi-x"></i>
Remove Unique Identifier from selected Device
</a>
</li>
<li>
<a class="dropdown-item" href="{{ url_for('labels.label_list')}}">
<i class="bi bi-tools"></i>
Unique Identifier Management
</a>
</li>
</ul>
</div>
<div class="btn-group dropdown m-1" uib-dropdown="">
<button id="btnTags" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-tag"></i>
Labels
</button>
<span class="d-none" id="unlinkTagAlertModal" data-bs-toggle="modal" data-bs-target="#unlinkTagErrorModal"></span>
<span class="d-none" id="addTagAlertModal" data-bs-toggle="modal" data-bs-target="#addingTagModal"></span>
<ul class="dropdown-menu" aria-labelledby="btnTags">
<li>
<form id="print_labels" method="post" action="{{ url_for('labels.print_labels') }}">
@ -232,17 +201,17 @@
<div class="btn-group dropdown m-1" uib-dropdown="">
<button id="btnSnapshot" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-laptop"></i>
New Device
New Device Real
</button>
<ul class="dropdown-menu" aria-labelledby="btnSnapshot">
<li>
{% if lot %}
<a href="{{ url_for('inventory.lot_upload_snapshot', lot_id=lot.id) }}" class="dropdown-item">
<a href="{{ url_for('inventory.lot_upload_placeholder', lot_id=lot.id) }}" class="dropdown-item">
{% else %}
<a href="{{ url_for('inventory.upload_snapshot') }}" class="dropdown-item">
<a href="{{ url_for('inventory.upload_placeholder') }}" class="dropdown-item">
{% endif %}
<i class="bi bi-upload"></i>
Upload a new Snapshot
Upload Placeholder Spreadsheet
</a>
</li>
<li>
@ -252,7 +221,26 @@
<a href="{{ url_for('inventory.device_add') }}" class="dropdown-item">
{% endif %}
<i class="bi bi-plus"></i>
Create a new Device
Create a new Placeholder
</a>
</li>
</ul>
</div>
<div class="btn-group dropdown m-1" uib-dropdown="">
<button id="btnSnapshot" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-laptop"></i>
New Device Abstract
</button>
<ul class="dropdown-menu" aria-labelledby="btnSnapshot">
<li>
{% if lot %}
<a href="{{ url_for('inventory.lot_upload_snapshot', lot_id=lot.id) }}" class="dropdown-item">
{% else %}
<a href="{{ url_for('inventory.upload_snapshot') }}" class="dropdown-item">
{% endif %}
<i class="bi bi-upload"></i>
Upload Snapshot files
</a>
</li>
</ul>
@ -287,6 +275,8 @@
<th scope="col">Select</th>
<th scope="col">Title</th>
<th scope="col">DHID</th>
<th scope="col">PHID</th>
<th scope="col">Type</th>
<th scope="col">Unique Identifiers</th>
<th scope="col">Lifecycle Status</th>
<th scope="col">Allocated Status</th>
@ -328,6 +318,12 @@
{{ dev.devicehub_id }}
</a>
</td>
<td>
{{ dev.binding and dev.binding.phid or dev.placeholder and dev.placeholder.phid or '' }}
</td>
<td>
{{ dev.is_abstract() }}
</td>
<td>
{% for t in dev.tags | sort(attribute="id") %}
<a href="{{ url_for('labels.label_details', id=t.id)}}">{{ t.id }}</a>
@ -392,14 +388,12 @@
</div>
</div>
</section>
{% include "inventory/addDevicestag.html" %}
{% include "inventory/lot_delete_modal.html" %}
{% include "inventory/actions.html" %}
{% include "inventory/allocate.html" %}
{% include "inventory/data_wipe.html" %}
{% include "inventory/trade.html" %}
{% include "inventory/alert_export_error.html" %}
{% include "inventory/alert_unlink_tag_error.html" %}
{% include "inventory/alert_lots_changes.html" %}
<!-- Custom Code -->

View File

@ -49,7 +49,7 @@
<input class="devicesList" type="hidden" name="device" />
<div>
<a href="{{ referrer }}" class="btn btn-danger">Cancel</a>
<a href="{{ url_for('inventory.device_details', id=dhid) }}" class="btn btn-danger">Cancel</a>
<button class="btn btn-primary" type="submit">Unlink</button>
</div>
</form>

View File

@ -26,111 +26,101 @@
<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>
<th scope="col">Info Abstract device</th>
<th scope="col">Info Real device</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>
<td class="table-success">{{ placeholder.binding.manufacturer or '' }}</td>
<td class="table-warning 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>
<td class="table-success">{{ placeholder.binding.model or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.serial_number or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.brand or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.sku or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.generation or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.version or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.weight or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.width or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.height or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.depth or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.color or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.production_date or '' }}</td>
<td class="table-warning">{{ 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>
<td class="table-success">{{ placeholder.binding.variant or '' }}</td>
<td class="table-warning">{{ placeholder.device.variant or '' }}</td>
</tr>
</tbody>
</table>
<br />
{% if placeholder.device.components or device.components %}
{% if placeholder.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>
<th scope="col">Info Abstract device</th>
<th scope="col">Info Real device</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 class="table-warning text-right">
{{ placeholder.components or ''}}
</td>
</tr>
</tbody>
@ -139,19 +129,22 @@
<br />
{% if placeholder.device.manual_actions or device.manual_actions %}
{% if placeholder.device.manual_actions or placeholder.binding.manual_actions %}
<h2>Actions</h2>
<p>
The actions will become real device and will no longer be in the abstract
</p>
<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>
<th scope="col">Info Abstract device</th>
<th scope="col">Info Real device</th>
</tr>
</thead>
<tbody>
<tr>
<td class="table-success">
{% for a in device.manual_actions %}
{% for a in placeholder.binding.manual_actions %}
* {{ a.t }}<br />
{% endfor %}
</td>
@ -165,6 +158,32 @@
</table>
{% endif %}
{% if placeholder.device.tags %}
<h2>Tags</h2>
<p>
The tags will become real device and will no longer be in the abstract
</p>
<table class="table table-hover">
<thead>
<tr class="text-center">
<th scope="col">Info Abstract device</th>
<th scope="col">Info Real device</th>
</tr>
</thead>
<tbody>
<tr>
<td class="table-success">
</td>
<td class="table-danger text-right">
{% for a in placeholder.device.tags %}
* {{ 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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -251,6 +251,7 @@ def test_update_parent():
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
@pytest.mark.parametrize(
'action_model_state',
(
@ -264,15 +265,18 @@ def test_update_parent():
),
)
def test_generic_action(
action_model_state: Tuple[models.Action, states.Trading], user: UserClient
action_model_state: Tuple[models.ToPrepare, states.Trading], user2: UserClient
):
"""Tests POSTing all generic actions."""
user = user2
action_model, state = action_model_state
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
action = {'type': action_model.t, 'devices': [snapshot['device']['id']]}
abstract = Device.query.filter(Device.id == snapshot['device']['id']).one()
real = abstract.binding.device
action = {'type': action_model.t, 'devices': [real.id]}
action, _ = user.post(action, res=models.Action)
assert action['devices'][0]['id'] == snapshot['device']['id']
device, _ = user.get(res=Device, item=snapshot['device']['devicehubID'])
assert action['devices'][0]['id'] == real.id
device, _ = user.get(res=Device, item=real.dhid)
assert device['actions'][-1]['id'] == action['id']
assert device['physical'] == state.name
# Check if the update of device is changed
@ -280,6 +284,7 @@ def test_generic_action(
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
@pytest.mark.parametrize(
'action_model',
(
@ -288,14 +293,17 @@ def test_generic_action(
),
)
def test_simple_status_actions(
action_model: models.Action, user: UserClient, user2: UserClient
action_model: models.Action, user2: UserClient
):
"""Simple test of status action."""
user = user2
snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
abstract = Device.query.filter(Device.id == snap['device']['id']).one()
real = abstract.binding.device
action = {'type': action_model.t, 'devices': [snap['device']['id']]}
action = {'type': action_model.t, 'devices': [real.id]}
action, _ = user.post(action, res=models.Action)
device, _ = user.get(res=Device, item=snap['device']['devicehubID'])
device, _ = user.get(res=Device, item=real.dhid)
assert device['actions'][-1]['id'] == action['id']
assert action['author']['id'] == user.user['id']
assert action['rol_user']['id'] == user.user['id']
@ -518,6 +526,7 @@ def test_recycling_container(user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
@pytest.mark.parametrize(
'action_model',
(
@ -528,13 +537,16 @@ def test_recycling_container(user: UserClient):
def test_status_without_lot(action_model: models.Action, user: UserClient):
"""Test of status actions for devices without lot."""
snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
action = {'type': action_model.t, 'devices': [snap['device']['id']]}
abstract = Device.query.filter_by(id=snap['device']['id']).first()
device_id = abstract.binding.device.id
action = {'type': action_model.t, 'devices': [device_id]}
action, _ = user.post(action, res=models.Action)
device, _ = user.get(res=Device, item=snap['device']['devicehubID'])
device, _ = user.get(res=Device, item=abstract.dhid)
assert device['actions'][-1]['id'] == action['id']
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
@pytest.mark.parametrize(
'action_model',
(
@ -542,17 +554,18 @@ def test_status_without_lot(action_model: models.Action, user: UserClient):
for ams in [models.Recycling, models.Use, models.Refurbish, models.Management]
),
)
def test_status_in_temporary_lot(action_model: models.Action, user: UserClient):
def test_status_in_temporary_lot(action_model: models.Action, user: UserClient, app: Devicehub):
"""Test of status actions for devices in a temporary lot."""
snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
device_id = snap['device']['id']
abstract = Device.query.filter_by(id=snap['device']['id']).first()
device_id = abstract.binding.device.id
lot, _ = user.post({'name': 'MyLotOut'}, res=Lot)
lot, _ = user.post(
{}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device_id)]
)
action = {'type': action_model.t, 'devices': [device_id]}
action, _ = user.post(action, res=models.Action)
device, _ = user.get(res=Device, item=snap['device']['devicehubID'])
device, _ = user.get(res=Device, item=abstract.dhid)
assert device['actions'][-1]['id'] == action['id']
@ -991,8 +1004,9 @@ def test_licences(client: Client):
def test_allocate(user: UserClient):
"""Tests allocate"""
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
device_id = snapshot['device']['id']
devicehub_id = snapshot['device']['devicehubID']
abstract = Device.query.filter_by(id=snapshot['device']['id']).one()
device_id = abstract.binding.device.id
devicehub_id = abstract.dhid
post_request = {
"transaction": "ccc",
"finalUserCode": "aabbcc",
@ -1060,8 +1074,9 @@ def test_allocate_bad_dates(user: UserClient):
def test_deallocate(user: UserClient):
"""Tests deallocate"""
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
device_id = snapshot['device']['id']
devicehub_id = snapshot['device']['devicehubID']
abstract = Device.query.filter_by(id=snapshot['device']['id']).one()
device_id = abstract.binding.device.id
devicehub_id = abstract.dhid
post_deallocate = {
"startTime": "2020-11-01T02:00:00+00:00",
"transaction": "ccc",
@ -1393,27 +1408,6 @@ def test_price_custom():
assert c['price']['id'] == p['id']
@pytest.mark.mvp
def test_price_custom_client(user: UserClient):
"""As test_price_custom but creating the price through the API."""
s = file('basic.snapshot')
snapshot, _ = user.post(s, res=models.Snapshot)
price, _ = user.post(
{
'type': 'Price',
'price': 25,
'currency': Currency.EUR.name,
'device': snapshot['device']['id'],
},
res=models.Action,
)
assert 25 == price['price']
assert Currency.EUR.name == price['currency']
device, _ = user.get(res=Device, item=price['device']['devicehubID'])
assert 25 == device['price']['price']
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
def test_erase_physical():

View File

@ -54,6 +54,7 @@ def test_api_docs(client: Client):
'/inventory/device/',
'/inventory/device/add/',
'/inventory/device/{id}/',
'/inventory/device/{dhid}/binding/',
'/inventory/export/{export_id}/',
'/inventory/lot/add/',
'/inventory/lot/{id}/',
@ -68,8 +69,8 @@ def test_api_docs(client: Client):
'/inventory/lot/{lot_id}/upload-snapshot/',
'/inventory/snapshots/{snapshot_uuid}/',
'/inventory/snapshots/',
'/inventory/tag/devices/add/',
'/inventory/tag/devices/{id}/del/',
'/inventory/tag/devices/{dhid}/add/',
'/inventory/tag/devices/{dhid}/del/',
'/inventory/upload-snapshot/',
'/inventory/device/edit/{id}/',
'/inventory/upload-placeholder/',

View File

@ -473,6 +473,7 @@ def test_get_devices(app: Devicehub, user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_get_device_permissions(app: Devicehub, user: UserClient, user2: UserClient,
client: Client):
"""Checks GETting a d.Desktop with its components."""
@ -480,7 +481,9 @@ def test_get_device_permissions(app: Devicehub, user: UserClient, user2: UserCli
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
pc, res = user.get(res=d.Device, item=s['device']['devicehubID'])
assert res.status_code == 200
assert len(pc['actions']) == 7
assert len(pc['actions']) == 0
pc = d.Device.query.filter_by(devicehub_id=s['device']['devicehubID']).one()
assert len(pc.placeholder.binding.actions) == 7
html, _ = client.get(res=d.Device, item=s['device']['devicehubID'], accept=ANY)
assert 'intel atom cpu n270 @ 1.60ghz' in html
@ -660,12 +663,15 @@ def test_cooking_mixer_api(user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_hid_with_mac(app: Devicehub, user: UserClient):
"""Checks hid with mac."""
snapshot = file('asus-eee-1000h.snapshot.11')
snap, _ = user.post(snapshot, res=m.Snapshot)
pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID'])
assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'
assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116'
pc = d.Device.query.filter_by(devicehub_id=snap['device']['devicehubID']).one()
assert pc.placeholder.binding.hid == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'
@pytest.mark.mvp
@ -706,6 +712,7 @@ def test_hid_with_2networkadapters(app: Devicehub, user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_hid_with_2network_and_drop_no_mac_in_hid(app: Devicehub, user: UserClient):
"""Checks hid with 2 networks adapters and next drop the network is not used in hid"""
snapshot = yaml2json('asus-eee-1000h.snapshot.11')
@ -715,19 +722,22 @@ def test_hid_with_2network_and_drop_no_mac_in_hid(app: Devicehub, user: UserClie
network['serialNumber'] = 'a0:24:8c:7f:cf:2d'
snap, _ = user.post(json_encode(snapshot), res=m.Snapshot)
pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID'])
assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'
assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116'
pc = d.Device.query.filter_by(devicehub_id=snap['device']['devicehubID']).one()
assert pc.placeholder.binding.hid == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'
snapshot['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abb'
snapshot['components'] = [c for c in snapshot['components'] if c != network]
user.post(json_encode(snapshot), res=m.Snapshot)
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 pc.placeholder.binding.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']) == 2
assert len([c for c in laptop['components'] if c['type'] == 'NetworkAdapter']) == 1
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_hid_with_2network_and_drop_mac_in_hid(app: Devicehub, user: UserClient):
"""Checks hid with 2 networks adapters and next drop the network is used in hid"""
# One tipical snapshot with 2 network cards
@ -738,7 +748,9 @@ def test_hid_with_2network_and_drop_mac_in_hid(app: Devicehub, user: UserClient)
network['serialNumber'] = 'a0:24:8c:7f:cf:2d'
snap, _ = user.post(json_encode(snapshot), res=m.Snapshot)
pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID'])
assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'
assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116'
pc = d.Device.query.filter_by(devicehub_id=snap['device']['devicehubID']).one()
assert pc.placeholder.binding.hid == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'
# we drop the network card then is used for to build the hid
snapshot['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abb'

View File

@ -238,18 +238,22 @@ def test_device_search_regenerate_table(app: DeviceSearch, user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_device_query_search(user: UserClient):
# todo improve
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
dev = Device.query.filter_by(id=snapshot['device']['id']).one()
user.post(file('computer-monitor.snapshot'), res=Snapshot)
user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
i, _ = user.get(res=Device, query=[('search', 'desktop')])
assert i['items'][0]['id'] == snapshot['device']['id']
assert i['items'][0]['id'] == dev.id
i, _ = user.get(res=Device, query=[('search', 'intel')])
assert len(i['items']) == 1
i, _ = user.get(res=Device, query=[('search', i['items'][0]['devicehubID'])])
dev1 = Device.query.filter_by(id=i['items'][0]['id']).one()
i, _ = user.get(res=Device, query=[('search', dev1.devicehub_id)])
assert len(i['items']) == 1
i, _ = user.get(res=Device, query=[('search', snapshot['device']['id'])])
dev2 = Device.query.filter_by(id=i['items'][0]['id']).one()
i, _ = user.get(res=Device, query=[('search', dev2.devicehub_id)])
assert len(i['items']) == 1

View File

@ -320,16 +320,16 @@ def test_link_tag_to_device(user3: UserClientFlask):
}
user3.post(uri, data=data)
body, status = user3.get('/inventory/device/')
body, status = user3.get('/inventory/device/{}/'.format(dev.dhid))
assert "tag1" in body
data = {
'tag': "tag1",
'device': dev.id,
'device': dev.dhid,
'csrf_token': generate_csrf(),
}
uri = '/inventory/tag/devices/add/'
uri = '/inventory/tag/devices/{}/add/'.format(dev.dhid)
user3.post(uri, data=data)
assert len(list(dev.tags)) == 1
tags = [tag.id for tag in dev.tags]
@ -406,7 +406,7 @@ def test_print_labels(user3: UserClientFlask):
'csrf_token': generate_csrf(),
}
uri = '/inventory/tag/devices/add/'
uri = '/inventory/tag/devices/{}/add/'.format(dev.dhid)
user3.post(uri, data=data)
assert len(list(dev.tags)) == 1
@ -419,7 +419,7 @@ def test_print_labels(user3: UserClientFlask):
body, status = user3.post(uri, data=data)
assert status == '200 OK'
path = "/inventory/device/{}/".format(dev.devicehub_id)
path = "/devices/{}".format(dev.dhid)
assert path in body
assert "tag1" in body
@ -2021,7 +2021,6 @@ def test_manual_binding(user3: UserClientFlask):
'model': "LC27T55",
'manufacturer': "Samsung",
'generation': 1,
'weight': 0.1,
'height': 0.1,
'depth': 0.1,
'id_device_supplier': "b2",
@ -2047,17 +2046,24 @@ def test_manual_binding(user3: UserClientFlask):
old_placeholder = dev_wb.binding
# page binding
dhid = dev_wb.devicehub_id
dhid = dev_wb.dhid
uri = f'/inventory/binding/{dhid}/sid/'
body, status = user3.get(uri)
assert status == '200 OK'
assert 'sid' in body
assert 'Confirm' in body
phid_real = dev.placeholder.phid
phid_abstract = dev_wb.binding.phid
dhid_real = dev.dhid
dhid_abstract = dev_wb.dhid
# action binding
body, status = user3.post(uri, data={})
assert status == '200 OK'
assert f"Device &#34;{dhid}&#34; bind successfully with sid!" in body
txt = f"Device real with PHID: {phid_real} and DHID: {dhid_real} "
txt += f"bind successfully with device abstract PHID: {phid_abstract} DHID: {dhid_abstract}."
assert txt in body
# check new structure
assert dev_wb.binding.phid == 'sid'
@ -2145,7 +2151,7 @@ def test_unbinding(user3: UserClientFlask):
old_placeholder = dev_wb.binding
# page binding
dhid = dev_wb.devicehub_id
dhid = dev_wb.dhid
uri = f'/inventory/binding/{dhid}/sid/'
user3.get(uri)
@ -2154,11 +2160,14 @@ def test_unbinding(user3: UserClientFlask):
user3.post(uri, data={})
assert dev.placeholder.binding == dev_wb
dhid = dev.dhid
# action unbinding
uri = '/inventory/unbinding/sid/'
body, status = user3.post(uri, data={})
assert status == '200 OK'
assert 'Device &#34;sid&#34; unbind successfully!' in body
txt = f'Device with PHID:&#34;sid&#34; and DHID: {dhid} unbind successfully!'
assert txt in body
# assert 'Device &#34;sid&#34; unbind successfully!' in body
# check new structure

View File

@ -33,7 +33,7 @@ from ereuse_devicehub.resources.action.models import (
from ereuse_devicehub.resources.action.views.snapshot import save_json
from ereuse_devicehub.resources.device import models as m
from ereuse_devicehub.resources.device.exceptions import NeedsId
from ereuse_devicehub.resources.device.models import SolidStateDrive
from ereuse_devicehub.resources.device.models import SolidStateDrive, Device
from ereuse_devicehub.resources.device.sync import (
MismatchBetweenProperties,
MismatchBetweenTagsAndHid,
@ -91,6 +91,7 @@ def test_snapshot_schema(app: Devicehub):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_post(user: UserClient):
"""Tests the post snapshot endpoint (validation, etc), data correctness,
and relationship correctness.
@ -108,11 +109,12 @@ def test_snapshot_post(user: UserClient):
assert snapshot['author']['id'] == user.user['id']
assert 'actions' not in snapshot['device']
assert 'author' not in snapshot['device']
device, _ = user.get(res=m.Device, item=snapshot['device']['devicehubID'])
dev = m.Device.query.filter_by(id=snapshot['device']['id']).one()
device, _ = user.get(res=m.Device, item=dev.devicehub_id)
key = itemgetter('serialNumber')
snapshot['components'].sort(key=key)
device['components'].sort(key=key)
assert snapshot['components'] == device['components']
assert {(x['id'], x['type']) for x in device['components']} == {(x['id'], x['type']) for x in snapshot['components']}
assert {c['type'] for c in snapshot['components']} == {
m.GraphicCard.t,
@ -190,7 +192,6 @@ def test_snapshot_power_on_hours(user: UserClient):
)
errors = SnapshotsLog.query.filter().all()
snap_log = errors[1]
assert len(errors) == 2
assert str(errors[0].snapshot_uuid) == snap['uuid']
assert str(errors[1].snapshot.uuid) == snap['uuid']
@ -199,6 +200,7 @@ def test_snapshot_power_on_hours(user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_component_add_remove(user: UserClient):
"""Tests adding and removing components and some don't generate HID.
All computers generate HID.
@ -221,7 +223,8 @@ def test_snapshot_component_add_remove(user: UserClient):
# RateComputer.t),
# perform_second_snapshot=False)
pc1_id = snapshot1['device']['id']
pc1_devicehub_id = snapshot1['device']['devicehubID']
pc1_dev = m.Device.query.filter_by(id=pc1_id).one()
pc1_devicehub_id = pc1_dev.devicehub_id
pc1, _ = user.get(res=m.Device, item=pc1_devicehub_id)
update1_pc1 = pc1['updated']
# Parent contains components
@ -229,14 +232,15 @@ def test_snapshot_component_add_remove(user: UserClient):
'p1c1s',
'p1c2s',
'p1c3s',
)
) == tuple(x.serial_number for x in pc1_dev.binding.device.components)
# Components contain parent
assert all(c['parent'] == pc1_id for c in pc1['components'])
# pc has three actions: Snapshot, BenchmarkProcessor and RateComputer
assert len(pc1['actions']) == 2
assert pc1['actions'][1]['type'] == Snapshot.t
# p1c1s has Snapshot
p1c1s, _ = user.get(res=m.Device, item=pc1['components'][0]['devicehubID'])
p1c1s_dev = m.Device.query.filter_by(id=pc1['components'][0]['id']).one()
p1c1s, _ = user.get(res=m.Device, item=p1c1s_dev.devicehub_id)
assert tuple(e['type'] for e in p1c1s['actions']) == ('Snapshot',)
# We register a new device
@ -249,7 +253,8 @@ def test_snapshot_component_add_remove(user: UserClient):
# snapshot2 = snapshot_and_check(user, s2, action_types=('Remove', 'RateComputer'),
# perform_second_snapshot=False)
pc2_id = snapshot2['device']['id']
pc2_devicehub_id = snapshot2['device']['devicehubID']
pc2_dev = m.Device.query.filter_by(id=pc2_id).one()
pc2_devicehub_id = pc2_dev.devicehub_id
pc1, _ = user.get(res=m.Device, item=pc1_devicehub_id)
pc2, _ = user.get(res=m.Device, item=pc2_devicehub_id)
# Check if the update_timestamp is updated
@ -269,7 +274,8 @@ def test_snapshot_component_add_remove(user: UserClient):
assert all(c['parent'] == pc2_id for c in pc2['components'])
assert tuple(e['type'] for e in pc2['actions']) == ('Snapshot',)
# p1c2s has two Snapshots, a Remove and an Add
p1c2s, _ = user.get(res=m.Device, item=pc2['components'][0]['devicehubID'])
p1c2s_dev = m.Device.query.filter_by(id=pc2['components'][0]['id']).one()
p1c2s, _ = user.get(res=m.Device, item=p1c2s_dev.devicehub_id)
assert tuple(e['type'] for e in p1c2s['actions']) == (
'BenchmarkProcessor',
'Snapshot',
@ -299,8 +305,8 @@ def test_snapshot_component_add_remove(user: UserClient):
assert tuple(get_actions_info(pc1['actions'])) == (
# id, type, components, snapshot
('BenchmarkProcessor', []), # first BenchmarkProcessor
('Snapshot', ['p1c1s', 'p1c2s', 'p1c3s']), # first Snapshot1
('Remove', ['p1c2s']), # Remove Processor in Snapshot2
('Snapshot', ['p1c1s', 'p1c2s', 'p1c3s', 'p1c2s']), # first Snapshot1
('Remove', ['p1c2s', 'p1c2s']), # Remove Processor in Snapshot2
('Snapshot', ['p1c2s', 'p1c3s']), # This Snapshot3
)
# PC2
@ -311,12 +317,13 @@ def test_snapshot_component_add_remove(user: UserClient):
'Remove', # the processor we added in 2.
)
# p1c2s has Snapshot, Remove and Add
p1c2s, _ = user.get(res=m.Device, item=pc1['components'][0]['devicehubID'])
p1c2s_dev = m.Device.query.filter_by(id=pc1['components'][0]['id']).one()
p1c2s, _ = user.get(res=m.Device, item=p1c2s_dev.devicehub_id)
assert tuple(get_actions_info(p1c2s['actions'])) == (
('BenchmarkProcessor', []), # first BenchmarkProcessor
('Snapshot', ['p1c1s', 'p1c2s', 'p1c3s']), # First Snapshot to PC1
('Snapshot', ['p1c1s', 'p1c2s', 'p1c3s', 'p1c2s']), # First Snapshot to PC1
('Snapshot', ['p1c2s', 'p2c1s']), # Second Snapshot to PC2
('Remove', ['p1c2s']), # ...which caused p1c2s to be removed form PC1
('Remove', ['p1c2s', 'p1c2s']), # ...which caused p1c2s to be removed form PC1
('Snapshot', ['p1c2s', 'p1c3s']), # The third Snapshot to PC1
('Remove', ['p1c2s']), # ...which caused p1c2 to be removed from PC2
)
@ -482,6 +489,7 @@ def test_not_remove_ram_in_same_computer(user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_erase_privacy_standards_endtime_sort(user: UserClient):
"""Tests a Snapshot with EraseSectors and the resulting privacy
properties.
@ -520,8 +528,9 @@ def test_erase_privacy_standards_endtime_sort(user: UserClient):
# The actual test
storage = next(e for e in snapshot['components'] if e['type'] == SolidStateDrive.t)
db_storage = m.Device.query.filter_by(id=storage['id']).one()
storage, _ = user.get(
res=m.Device, item=storage['devicehubID']
res=m.Device, item=db_storage.devicehub_id
) # Let's get storage actions too
# order: endTime ascending
# erasure1/2 have an user defined time and others actions endTime = created
@ -555,17 +564,22 @@ def test_erase_privacy_standards_endtime_sort(user: UserClient):
assert 'num' not in step2
assert ['HMG_IS5'] == erasure['standards']
assert storage['privacy']['type'] == 'EraseSectors'
pc, _ = user.get(res=m.Device, item=snapshot['device']['devicehubID'])
dev = m.Device.query.filter_by(id=snapshot['device']['id']).one()
pc, _ = user.get(res=m.Device, item=dev.devicehub_id)
# pc, _ = user.get(res=m.Device, item=snapshot['device']['devicehubID'])
assert pc['privacy'] == [storage['privacy']]
# Let's try a second erasure with an error
s['uuid'] = uuid4()
s['components'][0]['actions'][0]['severity'] = 'Error'
snapshot, _ = user.post(json_encode(s), res=Snapshot)
storage, _ = user.get(res=m.Device, item=storage['devicehubID'])
storage, _ = user.get(res=m.Device, item=db_storage.devicehub_id)
assert storage['hid'] == 'solidstatedrive-c1mr-c1ml-c1s'
assert dev.components[0].privacy.type == 'EraseSectors'
assert storage['privacy']['type'] == 'EraseSectors'
pc, _ = user.get(res=m.Device, item=snapshot['device']['devicehubID'])
dev = m.Device.query.filter_by(id=snapshot['device']['id']).one()
pc, _ = user.get(res=m.Device, item=dev.devicehub_id)
# import pdb; pdb.set_trace()
assert pc['privacy'] == [storage['privacy']]
@ -889,23 +903,26 @@ def test_snapshot_failed_missing_chassis(app: Devicehub, user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_failed_end_time_bug(app: Devicehub, user: UserClient):
"""This test check if the end_time = 0001-01-01 00:00:00+00:00
and then we get a /devices, this create a crash
"""
snapshot_file = file('asus-end_time_bug88.snapshot')
snapshot, _ = user.post(res=Snapshot, data=snapshot_file)
device, _ = user.get(res=m.Device, item=snapshot['device']['devicehubID'])
dev = m.Device.query.filter_by(id=snapshot['device']['id']).one()
device, _ = user.get(res=m.Device, item=dev.devicehub_id)
end_times = [x['endTime'] for x in device['actions']]
assert '1970-01-02T00:00:00+00:00' in end_times
assert not '0001-01-01T00:00:00+00:00' in end_times
assert '0001-01-01T00:00:00+00:00' not in end_times
tmp_snapshots = app.config['TMP_SNAPSHOTS']
shutil.rmtree(tmp_snapshots)
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_not_failed_end_time_bug(app: Devicehub, user: UserClient):
"""This test check if the end_time != 0001-01-01 00:00:00+00:00
and then we get a /devices, this create a crash
@ -913,7 +930,8 @@ def test_snapshot_not_failed_end_time_bug(app: Devicehub, user: UserClient):
snapshot_file = yaml2json('asus-end_time_bug88.snapshot')
snapshot_file['endTime'] = '2001-01-01 00:00:00+00:00'
snapshot, _ = user.post(res=Snapshot, data=json_encode(snapshot_file))
device, _ = user.get(res=m.Device, item=snapshot['device']['devicehubID'])
db_dev = Device.query.filter_by(id=snapshot['device']['id']).one()
device, _ = user.get(res=m.Device, item=db_dev.devicehub_id)
end_times = [x['endTime'] for x in device['actions']]
assert not '1970-01-02T00:00:00+00:00' in end_times
@ -974,6 +992,7 @@ def test_snapshot_wb_lite(user: UserClient):
body, res = user.post(snapshot, uri="/api/inventory/")
dev = m.Device.query.filter_by(devicehub_id=body['dhid']).one()
dev = dev.placeholder.binding
ssd = [x for x in dev.components if x.type == 'SolidStateDrive'][0]
assert dev.manufacturer == 'lenovo'
@ -1000,6 +1019,7 @@ def test_snapshot_wb_lite_qemu(user: UserClient):
assert res.status == '201 CREATED'
dev = m.Device.query.filter_by(devicehub_id=body['dhid']).one()
dev = dev.placeholder.binding
assert dev.manufacturer == 'qemu'
assert dev.model == 'standard'
assert dev.serial_number is None
@ -1235,6 +1255,7 @@ def test_snapshot_errors(user: UserClient):
assert len(SnapshotsLog.query.all()) == 1
bodyLite, res = user.post(snapshot_lite, uri="/api/inventory/")
dev = m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one()
dev = dev.placeholder.binding
assert len(SnapshotsLog.query.all()) == 4
assert body11['device'].get('hid') == dev.hid
@ -1276,6 +1297,7 @@ def test_snapshot_errors_no_serial_number(user: UserClient):
assert len(logs) == 1
assert logs[0].description == 'Ok'
dev = m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one()
dev = dev.placeholder.binding
assert not dev.model
assert not dev.manufacturer
assert not dev.serial_number

View File

@ -364,13 +364,12 @@ def test_tag_secondary_workbench_link_find(user: UserClient):
s = yaml2json('basic.snapshot')
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'])
desktop = Device.query.filter_by(
devicehub_id=snapshot['device']['devicehubID']
).one()
dev = Device.query.filter_by(id=snapshot['device']['id']).one()
device, _ = user.get(res=Device, item=dev.devicehub_id)
desktop = dev.binding.device
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]
assert 'foo' in [x.id for x in desktop.tags]
assert 'bar' in [x.secondary for x in desktop.tags]
r, _ = user.get(
res=Device, query=[('search', 'foo'), ('filter', {'type': ['Computer']})]

View File

@ -16,9 +16,11 @@ 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, json_encode, yaml2json
from tests import conftest
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_workbench_server_condensed(user: UserClient):
"""As :def:`.test_workbench_server_phases` but all the actions
condensed in only one big ``Snapshot`` file, as described
@ -54,7 +56,8 @@ def test_workbench_server_condensed(user: UserClient):
}
assert snapshot['closed']
assert snapshot['severity'] == 'Info'
device, _ = user.get(res=Device, item=snapshot['device']['devicehubID'])
db_dev = Device.query.filter_by(id=snapshot['device']['id']).one()
device, _ = user.get(res=Device, item=db_dev.devicehub_id)
assert device['dataStorageSize'] == 1100
assert device['chassis'] == 'Tower'
assert device['hid'] == 'desktop-d1mr-d1ml-d1s-na1-s'
@ -175,13 +178,15 @@ def test_real_toshiba_11(user: UserClient):
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
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.
"""
s = file('real-eee-1001pxd.snapshot.11')
snapshot, _ = user.post(res=em.Snapshot, data=s)
pc, _ = user.get(res=Device, item=snapshot['device']['devicehubID'])
dev = Device.query.filter_by(id=snapshot['device']['id']).one()
pc, _ = user.get(res=Device, item=dev.devicehub_id)
assert pc['type'] == 'Laptop'
assert pc['chassis'] == 'Netbook'
assert pc['model'] == '1001pxd'
@ -222,7 +227,8 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
assert cpu['speed'] == 1.667
assert 'hid' not in cpu
assert pc['processorModel'] == cpu['model'] == 'intel atom cpu n455 @ 1.66ghz'
cpu, _ = user.get(res=Device, item=cpu['devicehubID'])
db_cpu = Device.query.filter_by(id=cpu['id']).one()
cpu, _ = user.get(res=Device, item=db_cpu.devicehub_id)
actions = cpu['actions']
sysbench = next(e for e in actions if e['type'] == em.BenchmarkProcessorSysbench.t)
assert sysbench['elapsed'] == 164
@ -245,7 +251,8 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
)
assert gpu['manufacturer'] == 'intel corporation'
assert gpu['memory'] == 256
gpu, _ = user.get(res=Device, item=gpu['devicehubID'])
db_gpu = Device.query.filter_by(id=gpu['id']).one()
gpu, _ = user.get(res=Device, item=db_gpu.devicehub_id)
action_types = tuple(e['type'] for e in gpu['actions'])
assert em.BenchmarkRamSysbench.t in action_types
assert em.StressTest.t in action_types
@ -264,7 +271,8 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
assert hdd['hid'] == 'harddrive-hitachi-hts54322-e2024242cv86hj'
assert hdd['interface'] == 'ATA'
assert hdd['size'] == 238475
hdd, _ = user.get(res=Device, item=hdd['devicehubID'])
db_hdd = Device.query.filter_by(id=hdd['id']).one()
hdd, _ = user.get(res=Device, item=db_hdd.devicehub_id)
action_types = tuple(e['type'] for e in hdd['actions'])
assert em.BenchmarkRamSysbench.t in action_types
assert em.StressTest.t in action_types