resolve conflict

This commit is contained in:
Cayo Puigdefabregas 2022-09-07 10:35:50 +02:00
commit e598f8aabe
33 changed files with 948 additions and 626 deletions

View File

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

View File

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

View File

@ -80,7 +80,6 @@ class DeviceListMixin(GenericMixin):
self.context.update( self.context.update(
{ {
'devices': devices, 'devices': devices,
'form_tag_device': TagDeviceForm(),
'form_new_action': form_new_action, 'form_new_action': form_new_action,
'form_new_allocate': AllocateForm(lot=lot_id), 'form_new_allocate': AllocateForm(lot=lot_id),
'form_new_datawipe': DataWipeForm(lot=lot_id), 'form_new_datawipe': DataWipeForm(lot=lot_id),
@ -148,27 +147,51 @@ class DeviceDetailView(GenericMixin):
.one() .one()
) )
form_binding = BindingForm(device=device) form_tags = TagDeviceForm(dhid=id)
self.context.update( self.context.update(
{ {
'device': device, 'device': device,
'placeholder': device.binding or device.placeholder, 'placeholder': device.binding or device.placeholder,
'page_title': 'Device {}'.format(device.devicehub_id), '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, 'form_binding': form_binding,
'active_binding': False, 'device': device,
} }
) )
if form_binding.validate_on_submit(): if form_binding.validate_on_submit():
next_url = url_for( next_url = url_for(
'inventory.binding', 'inventory.binding',
dhid=form_binding.device.devicehub_id, dhid=dhid,
phid=form_binding.placeholder.phid, phid=form_binding.placeholder.phid,
) )
return flask.redirect(next_url) return flask.redirect(next_url)
elif form_binding.phid.data:
self.context['active_binding'] = True
return flask.render_template(self.template_name, **self.context) return flask.render_template(self.template_name, **self.context)
@ -179,71 +202,113 @@ class BindingView(GenericMixin):
template_name = 'inventory/binding.html' template_name = 'inventory/binding.html'
def dispatch_request(self, dhid, phid): 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() self.get_context()
device = ( self.get_objects()
Device.query.filter(Device.owner_id == g.user.id) if self.check_errors():
.filter(Device.devicehub_id == dhid) return flask.redirect(self.next_url)
.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
if request.method == 'POST': if request.method == 'POST':
old_placeholder = device.binding return self.post()
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)
self.context.update( self.context.update(
{ {
'device': device.binding.device, 'new_placeholder': self.new_placeholder,
'placeholder': placeholder, 'old_placeholder': self.old_placeholder,
'page_title': 'Binding confirm', 'page_title': 'Binding confirm',
'actions': list(device.binding.device.actions) 'actions': list(self.old_device.actions)
+ list(placeholder.device.actions), + list(self.new_device.actions),
'tags': list(device.binding.device.tags) 'tags': list(self.old_device.tags) + list(self.new_device.tags),
+ list(placeholder.device.tags), 'dhid': self.dhid,
} }
) )
return flask.render_template(self.template_name, **self.context) 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): class UnBindingView(GenericMixin):
methods = ['GET', 'POST'] methods = ['GET', 'POST']
@ -256,31 +321,33 @@ class UnBindingView(GenericMixin):
.filter(Placeholder.phid == phid) .filter(Placeholder.phid == phid)
.one() .one()
) )
if not placeholder.binding: if not placeholder.binding or placeholder.status != 'Twin':
next_url = url_for( next_url = url_for(
'inventory.device_details', id=placeholder.device.devicehub_id 'inventory.device_details', id=placeholder.device.devicehub_id
) )
return flask.redirect(next_url) return flask.redirect(next_url)
device = placeholder.binding if placeholder.status != 'Twin':
dhid = placeholder.device.devicehub_id
if device.is_abstract() != 'Twin':
dhid = device.devicehub_id
next_url = url_for('inventory.device_details', id=dhid) 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) return flask.redirect(next_url)
self.get_context() self.get_context()
if request.method == 'POST': if request.method == 'POST':
new_device = self.clone_device(device) dhid = placeholder.device.devicehub_id
next_url = url_for('inventory.device_details', id=new_device.devicehub_id) self.clone_device(placeholder.binding)
messages.success('Device "{}" unbind successfully!'.format(phid)) 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) return flask.redirect(next_url)
self.context.update( self.context.update(
{ {
'device': device,
'placeholder': placeholder, 'placeholder': placeholder,
'page_title': 'Unbinding confirm', 'page_title': 'Unbinding confirm',
} }
@ -476,12 +543,15 @@ class TagLinkDeviceView(View):
methods = ['POST'] methods = ['POST']
decorators = [login_required] decorators = [login_required]
def dispatch_request(self): def dispatch_request(self, dhid):
form = TagDeviceForm() form = TagDeviceForm(dhid=dhid)
if form.validate_on_submit(): if form.validate_on_submit():
tag = form.tag.data
form.save() 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): class TagUnlinkDeviceView(GenericMixin):
@ -489,19 +559,20 @@ class TagUnlinkDeviceView(GenericMixin):
decorators = [login_required] decorators = [login_required]
template_name = 'inventory/tag_unlink_device.html' template_name = 'inventory/tag_unlink_device.html'
def dispatch_request(self, id): def dispatch_request(self, dhid):
self.get_context() self.get_context()
form = TagDeviceForm(delete=True, device=id) form = TagDeviceForm(delete=True, dhid=dhid)
if form.validate_on_submit(): if form.validate_on_submit():
form.remove() 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) return flask.redirect(next_url)
self.context.update( self.context.update(
{ {
'form': form, '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') '/device/edit/<string:id>/', view_func=DeviceEditView.as_view('device_edit')
) )
devices.add_url_rule( 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( devices.add_url_rule(
'/tag/devices/<int:id>/del/', '/tag/devices/<string:dhid>/del/',
view_func=TagUnlinkDeviceView.as_view('tag_devices_del'), view_func=TagUnlinkDeviceView.as_view('tag_devices_del'),
) )
devices.add_url_rule( devices.add_url_rule(
@ -1192,3 +1264,7 @@ devices.add_url_rule(
devices.add_url_rule( devices.add_url_rule(
'/unbinding/<string:phid>/', view_func=UnBindingView.as_view('unbinding') '/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": if device.type == "Battery":
device.size device.size
old_devicehub_id = device.devicehub_id
dict_device = copy.copy(device.__dict__) dict_device = copy.copy(device.__dict__)
dict_device.pop('_sa_instance_state') dict_device.pop('_sa_instance_state')
dict_device.pop('id', None) dict_device.pop('id', None)
@ -73,6 +76,8 @@ def clone_device(device):
dict_device.pop('tags', None) dict_device.pop('tags', None)
dict_device.pop('system_uuid', None) dict_device.pop('system_uuid', None)
new_device = device.__class__(**dict_device) new_device = device.__class__(**dict_device)
new_device.devicehub_id = old_devicehub_id
device.devicehub_id = None
db.session.add(new_device) db.session.add(new_device)
if hasattr(device, 'components'): if hasattr(device, 'components'):

View File

@ -330,7 +330,7 @@ class Device(Thing):
@property @property
def url(self) -> urlutils.URL: def url(self) -> urlutils.URL:
"""The URL where to GET this device.""" """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 @property
def rate(self): def rate(self):
@ -615,6 +615,14 @@ class Device(Thing):
model = self.model or '' model = self.model or ''
return f'{type} {manufacturer} {model}' 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 @declared_attr
def __mapper_args__(cls): def __mapper_args__(cls):
"""Defines inheritance. """Defines inheritance.
@ -924,6 +932,25 @@ class Placeholder(Thing):
) )
owner = db.relationship(User, primaryjoin=owner_id == User.id) 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): class Computer(Device):
"""A chassis with components inside that can be processed """A chassis with components inside that can be processed
@ -1022,10 +1049,14 @@ class Computer(Device):
"""Returns the privacy of all ``DataStorage`` components when """Returns the privacy of all ``DataStorage`` components when
it is not None. it is not None.
""" """
components = self.components
if self.placeholder and self.placeholder.binding:
components = self.placeholder.binding.components
return set( return set(
privacy privacy
for privacy in ( 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 if privacy
) )

View File

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

View File

@ -41,8 +41,8 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="page-header col-md-6 col-md-offset-3"> <div class="page-header col-md-6 col-md-offset-3">
<h1>{{ device.__format__('t') }}<br> <h1>{% if abstract %}Real device {% endif %}{{ device.__format__('t') or '' }}<br>
<small>{{ device.__format__('s') }}</small> <small>{{ device.__format__('s') or '' }}</small>
</h1> </h1>
</div> </div>
</div> </div>
@ -52,7 +52,7 @@
<div class="col-md-6"> <div class="col-md-6">
<ul> <ul>
{% for key, value in device.public_properties.items() %} {% for key, value in device.public_properties.items() %}
<li>{{ key }}: {{ value }}</li> <li>{{ key }}: {{ value or '' }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% if isinstance(device, d.Computer) %} {% if isinstance(device, d.Computer) %}
@ -140,17 +140,6 @@
<td></td> <td></td>
</tr> </tr>
{% endif %} {% 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> </tbody>
</table> </table>
</div> </div>
@ -222,6 +211,181 @@
{% endif %} {% endif %}
</div> </div>
</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> </div>
<footer class="container-fluid footer"> <footer class="container-fluid footer">
<div class="row"> <div class="row">

View File

@ -2,6 +2,7 @@ import datetime
import uuid import uuid
from itertools import filterfalse from itertools import filterfalse
import flask
import marshmallow import marshmallow
from flask import Response from flask import Response
from flask import current_app as app from flask import current_app as app
@ -110,9 +111,7 @@ class DeviceView(View):
return super().get(id) return super().get(id)
def patch(self, id): def patch(self, id):
dev = Device.query.filter_by( dev = Device.query.filter_by(id=id, owner_id=g.user.id, active=True).one()
id=id, owner_id=g.user.id, active=True
).one()
if isinstance(dev, Computer): if isinstance(dev, Computer):
resource_def = app.resources['Computer'] resource_def = app.resources['Computer']
# TODO check how to handle the 'actions_one' # TODO check how to handle the 'actions_one'
@ -138,10 +137,17 @@ class DeviceView(View):
return self.one_private(id) return self.one_private(id)
def one_public(self, id: int): def one_public(self, id: int):
device = Device.query.filter_by( device = Device.query.filter_by(devicehub_id=id, active=True).one()
devicehub_id=id, active=True abstract = None
).one() if device.binding:
return render_template('devices/layout.html', device=device, states=states) 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 @auth.Auth.requires_auth
def one_private(self, id: str): def one_private(self, id: str):

View File

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

View File

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

View File

@ -178,8 +178,6 @@ function deviceSelect() {
$("#addingLotModal .btn-primary").hide(); $("#addingLotModal .btn-primary").hide();
$("#removeLotModal .pol").show(); $("#removeLotModal .pol").show();
$("#removeLotModal .btn-primary").hide(); $("#removeLotModal .btn-primary").hide();
$("#addingTagModal .pol").show();
$("#addingTagModal .btn-primary").hide();
$("#actionModal .pol").show(); $("#actionModal .pol").show();
$("#actionModal .btn-primary").hide(); $("#actionModal .btn-primary").hide();
$("#allocateModal .pol").show(); $("#allocateModal .pol").show();
@ -197,8 +195,6 @@ function deviceSelect() {
$("#allocateModal .btn-primary").show(); $("#allocateModal .btn-primary").show();
$("#datawipeModal .pol").hide(); $("#datawipeModal .pol").hide();
$("#datawipeModal .btn-primary").show(); $("#datawipeModal .btn-primary").show();
$("#addingTagModal .pol").hide();
$("#addingTagModal .btn-primary").show();
} }
} }
@ -214,33 +210,6 @@ function removeLot() {
$("#activeRemoveLotModal").click(); $("#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() { function select_shift() {
const chkboxes = $('.deviceSelect'); const chkboxes = $('.deviceSelect');
var lastChecked = null; var lastChecked = null;

View File

@ -163,9 +163,6 @@ function deviceSelect() {
$("#removeLotModal .pol").show(); $("#removeLotModal .pol").show();
$("#removeLotModal .btn-primary").hide(); $("#removeLotModal .btn-primary").hide();
$("#addingTagModal .pol").show();
$("#addingTagModal .btn-primary").hide();
$("#actionModal .pol").show(); $("#actionModal .pol").show();
$("#actionModal .btn-primary").hide(); $("#actionModal .btn-primary").hide();
@ -189,9 +186,6 @@ function deviceSelect() {
$("#datawipeModal .pol").hide(); $("#datawipeModal .pol").hide();
$("#datawipeModal .btn-primary").show(); $("#datawipeModal .btn-primary").show();
$("#addingTagModal .pol").hide();
$("#addingTagModal .btn-primary").show();
} }
} }
@ -205,31 +199,6 @@ function removeLot() {
$("#activeRemoveLotModal").click(); $("#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() { function select_shift() {
const chkboxes = $(".deviceSelect"); const chkboxes = $(".deviceSelect");
let lastChecked = null; let lastChecked = null;

View File

@ -256,6 +256,14 @@
</ul> </ul>
</li><!-- End Temporal Lots Nav --> </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> </ul>
</aside><!-- End Sidebar--> </aside><!-- End Sidebar-->

View File

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

View File

@ -20,115 +20,126 @@
<div class="pt-4 pb-2"> <div class="pt-4 pb-2">
<h5 class="card-title text-center pb-0 fs-4">{{ title }}</h5> <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 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> </div>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="text-center"> <tr class="text-center">
<th scope="col">Basic Data</th> <th scope="col">Basic Data</th>
<th scope="col">Info to be Entered</th> <th scope="col">Info Twin device</th>
<th scope="col">Info to be Decoupled</th> <th scope="col">Info Abstract device</th>
</tr> </tr>
</thead> </thead>
<tbody> <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> <tr>
<th scope="row">Manufacturer:</th> <th scope="row">Manufacturer:</th>
<td class="table-success text-right">{{ placeholder.device.manufacturer or '' }}</td> <td class="table-success text-right">{{ new_placeholder.device.manufacturer or '' }}</td>
<td class="table-danger">{{ device.manufacturer or '' }}</td> <td class="table-warning">{{ old_placeholder.device.manufacturer or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Model:</th> <th scope="row">Model:</th>
<td class="table-success">{{ placeholder.device.model or '' }}</td> <td class="table-success">{{ new_placeholder.device.model or '' }}</td>
<td class="table-danger">{{ device.model or '' }}</td> <td class="table-warning">{{ old_placeholder.device.model or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Serial Number:</th> <th scope="row">Serial Number:</th>
<td class="table-success">{{ placeholder.device.serial_number or '' }}</td> <td class="table-success">{{ new_placeholder.device.serial_number or '' }}</td>
<td class="table-danger">{{ device.serial_number or '' }}</td> <td class="table-warning">{{ old_placeholder.device.serial_number or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Brand:</th> <th scope="row">Brand:</th>
<td class="table-success">{{ placeholder.device.brand or '' }}</td> <td class="table-success">{{ new_placeholder.device.brand or '' }}</td>
<td class="table-danger">{{ device.brand or '' }}</td> <td class="table-warning">{{ old_placeholder.device.brand or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Sku:</th> <th scope="row">Sku:</th>
<td class="table-success">{{ placeholder.device.sku or '' }}</td> <td class="table-success">{{ new_placeholder.device.sku or '' }}</td>
<td class="table-danger">{{ device.sku or '' }}</td> <td class="table-warning">{{ old_placeholder.device.sku or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Generation:</th> <th scope="row">Generation:</th>
<td class="table-success">{{ placeholder.device.generation or '' }}</td> <td class="table-success">{{ new_placeholder.device.generation or '' }}</td>
<td class="table-danger">{{ device.generation or '' }}</td> <td class="table-warning">{{ old_placeholder.device.generation or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Version:</th> <th scope="row">Version:</th>
<td class="table-success">{{ placeholder.device.version or '' }}</td> <td class="table-success">{{ new_placeholder.device.version or '' }}</td>
<td class="table-danger">{{ device.version or '' }}</td> <td class="table-warning">{{ old_placeholder.device.version or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Weight:</th> <th scope="row">Weight:</th>
<td class="table-success">{{ placeholder.device.weight or '' }}</td> <td class="table-success">{{ new_placeholder.device.weight or '' }}</td>
<td class="table-danger">{{ device.weight or '' }}</td> <td class="table-warning">{{ old_placeholder.device.weight or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Width:</th> <th scope="row">Width:</th>
<td class="table-success">{{ placeholder.device.width or '' }}</td> <td class="table-success">{{ new_placeholder.device.width or '' }}</td>
<td class="table-danger">{{ device.width or '' }}</td> <td class="table-warning">{{ old_placeholder.device.width or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Height:</th> <th scope="row">Height:</th>
<td class="table-success">{{ placeholder.device.height or '' }}</td> <td class="table-success">{{ new_placeholder.device.height or '' }}</td>
<td class="table-danger">{{ device.height or '' }}</td> <td class="table-warning">{{ old_placeholder.device.height or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Depth:</th> <th scope="row">Depth:</th>
<td class="table-success">{{ placeholder.device.depth or '' }}</td> <td class="table-success">{{ new_placeholder.device.depth or '' }}</td>
<td class="table-danger">{{ device.depth or '' }}</td> <td class="table-warning">{{ old_placeholder.device.depth or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Color:</th> <th scope="row">Color:</th>
<td class="table-success">{{ placeholder.device.color or '' }}</td> <td class="table-success">{{ new_placeholder.device.color or '' }}</td>
<td class="table-danger">{{ device.color or '' }}</td> <td class="table-warning">{{ old_placeholder.device.color or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Production date:</th> <th scope="row">Production date:</th>
<td class="table-success">{{ placeholder.device.production_date or '' }}</td> <td class="table-success">{{ new_placeholder.device.production_date or '' }}</td>
<td class="table-danger">{{ device.production_date or '' }}</td> <td class="table-warning">{{ old_placeholder.device.production_date or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Variant:</th> <th scope="row">Variant:</th>
<td class="table-success">{{ placeholder.device.variant or '' }}</td> <td class="table-success">{{ new_placeholder.device.variant or '' }}</td>
<td class="table-danger">{{ device.variant or '' }}</td> <td class="table-warning">{{ old_placeholder.device.variant or '' }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<br /> <br />
{% if placeholder.device.components or device.components %} {% if new_placeholder.device.components or old_placeholder.device.components %}
<h2>Components</h2> <h2>Components</h2>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="text-center"> <tr class="text-center">
<th scope="col">Info to be Entered</th> <th scope="col">Info Twin device</th>
<th scope="col">Info to be Decoupled</th> <th scope="col">Info Abstract device</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="table-success text-right"> <td class="table-success text-right">
{% for c in placeholder.device.components %} {% for c in new_placeholder.device.components %}
* {{ c.verbose_name }}<br /> * {{ c.verbose_name }}<br />
{% endfor %} {% endfor %}
</td> </td>
<td class="table-danger"> <td class="table-warning">
{% for c in device.components %} {% for c in old_placeholder.device.components %}
* {{ c.verbose_name }}<br /> * {{ c.verbose_name }}<br />
{% endfor %} {% endfor %}
</td> </td>
@ -141,11 +152,14 @@
{% if actions %} {% if actions %}
<h2>Actions</h2> <h2>Actions</h2>
<p>
The actions will become real device and will no longer be in the abstract
</p>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="text-center"> <tr class="text-center">
<th scope="col">Info to be Entered</th> <th scope="col">Info Twin device</th>
<th scope="col">Info to be Decoupled</th> <th scope="col">Info Abstract device</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -166,11 +180,14 @@
{% if tags %} {% if tags %}
<h2>Tags</h2> <h2>Tags</h2>
<p>
The tags will become real device and will no longer be in the abstract
</p>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="text-center"> <tr class="text-center">
<th scope="col">Info to be Entered</th> <th scope="col">Info Twin device</th>
<th scope="col">Info to be Decoupled</th> <th scope="col">Info Abstract device</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -189,7 +206,7 @@
<div> <div>
<form method="post"> <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> <button class="btn btn-primary" type="submit">Confirm</button>
</form> </form>
</div> </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">
<div class="card-body pt-3"> <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 --> <!-- Bordered Tabs -->
<ul class="nav nav-tabs nav-tabs-bordered"> <ul class="nav nav-tabs nav-tabs-bordered">
{% if placeholder %}
<li class="nav-item"> <li class="nav-item">
<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> </li>
{% else %}
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#type">General details</button>
</li>
{% endif %}
{% if placeholder.binding %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('inventory.device_details', id=placeholder.binding.devicehub_id) }}">Workbench device</a>
</li>
{% endif %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ device.public_link }}" target="_blank">Web</a> <a class="nav-link" href="{{ device.public_link }}" target="_blank">Web</a>
@ -50,10 +73,6 @@
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#status">Status</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#status">Status</button>
</li> </li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#rate">Rate</button>
</li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#traceability">Traceability log</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#traceability">Traceability log</button>
</li> </li>
@ -62,65 +81,72 @@
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button>
</li> </li>
{% if device.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> </ul>
<div class="tab-content pt-2"> <div class="tab-content pt-2">
<div class="tab-pane fade {% if active_binding %}profile-overview{% else %}show active{% endif %}" id="type"> <div class="tab-pane fade show active" id="details">
<h5 class="card-title">Details</h5> <h5 class="card-title">Details Real parth</h5>
{% if device.placeholder %}
<div class="row mb-3"> <div class="row mb-3">
<div class="col-lg-3 col-md-4 label "> <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>
<div class="col-lg-9 col-md-8">{{ device.is_abstract() }}</div> <div class="col-lg-9 col-md-8">{{ placeholder.status }}</div>
</div> </div>
{% endif %}
{% if device.placeholder %}
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label ">Phid</div> <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> </div>
{% endif %}
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label ">Type</div> <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>
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Manufacturer</div> <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>
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Model</div> <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>
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Serial Number</div> <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> </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>
<div class="tab-pane fade profile-overview" id="lots"> <div class="tab-pane fade profile-overview" id="lots">
<h5 class="card-title">Incoming Lots</h5> <h5 class="card-title">Incoming Lots</h5>
<div class="row"> <div class="row">
{% for lot in device.lots %} {% for lot in placeholder.device.lots %}
{% if lot.is_incoming %} {% if lot.is_incoming %}
<div class="col"> <div class="col">
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}"> <a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
@ -134,7 +160,7 @@
<h5 class="card-title">Outgoing Lots</h5> <h5 class="card-title">Outgoing Lots</h5>
<div class="row"> <div class="row">
{% for lot in device.lots %} {% for lot in placeholder.device.lots %}
{% if lot.is_outgoing %} {% if lot.is_outgoing %}
<div class="col"> <div class="col">
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}"> <a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
@ -148,7 +174,7 @@
<h5 class="card-title">Temporary Lots</h5> <h5 class="card-title">Temporary Lots</h5>
<div class="row"> <div class="row">
{% for lot in device.lots %} {% for lot in placeholder.device.lots %}
{% if lot.is_temporary %} {% if lot.is_temporary %}
<div class="col"> <div class="col">
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}"> <a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
@ -165,53 +191,33 @@
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Physical State</div> <div class="col-lg-3 col-md-4 label">Physical State</div>
<div class="col-lg-9 col-md-8"> <div class="col-lg-9 col-md-8">
{% if device.physical_status %} {% if placeholder.device.physical_status %}
{{ device.physical_status.type }} {{ placeholder.device.physical_status.type }}
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Lifecycle State</div> <div class="col-lg-3 col-md-4 label">Lifecycle State</div>
<div class="col-lg-9 col-md-8"> <div class="col-lg-9 col-md-8">
{% if device.status %} {% if placeholder.device.status %}
{{ device.status.type }} {{ placeholder.device.status.type }}
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Allocated State</div> <div class="col-lg-3 col-md-4 label">Allocated State</div>
<div class="col-lg-9 col-md-8"> <div class="col-lg-9 col-md-8">
{% if device.allocated_status %} {% if placeholder.device.allocated_status %}
{{ device.allocated_status.type }} {{ placeholder.device.allocated_status.type }}
{% endif %} {% endif %}
</div> </div>
</div> </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"> <div class="tab-pane fade profile-overview" id="traceability">
<h5 class="card-title">Traceability log Details</h5> <h5 class="card-title">Traceability log Details</h5>
<div class="list-group col-6"> <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"> <div class="list-group-item d-flex justify-content-between align-items-center">
{{ action.type }} {{ action.severity }} {{ action.type }} {{ action.severity }}
<small class="text-muted">{{ action.created.strftime('%H:%M %d-%m-%Y') }}</small> <small class="text-muted">{{ action.created.strftime('%H:%M %d-%m-%Y') }}</small>
@ -221,10 +227,15 @@
</div> </div>
<div class="tab-pane fade profile-overview" id="components"> <div class="tab-pane fade profile-overview" id="components">
<h5 class="card-title">Components Details</h5> <h5 class="card-title">Components Real parth</h5>
{% if device.binding %}
<div class="list-group col-6"> <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="list-group-item">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{ component.type }}</h5> <h5 class="mb-1">{{ component.type }}</h5>
@ -242,52 +253,27 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% else %}
<div class="col-6">
{{ device.placeholder.components or '' }}
</div>
{% endif %} {% endif %}
</div> </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>
</div> </div>
</div> </div>
</section> </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 %} {% endblock main %}

View File

@ -268,42 +268,11 @@
</ul> </ul>
</div> </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=""> <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"> <button id="btnTags" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-tag"></i> <i class="bi bi-tag"></i>
Labels Labels
</button> </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"> <ul class="dropdown-menu" aria-labelledby="btnTags">
<li> <li>
<form id="print_labels" method="post" action="{{ url_for('labels.print_labels') }}"> <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=""> <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"> <button id="btnSnapshot" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-laptop"></i> <i class="bi bi-laptop"></i>
New Devices New Devices Real
</button> </button>
<ul class="dropdown-menu" aria-labelledby="btnSnapshot"> <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> <li>
{% if lot %} {% if lot %}
<a href="{{ url_for('inventory.lot_upload_placeholder', lot_id=lot.id) }}" class="dropdown-item"> <a href="{{ url_for('inventory.lot_upload_placeholder', lot_id=lot.id) }}" class="dropdown-item">
@ -358,6 +317,25 @@
</ul> </ul>
</div> </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 %} {% if lot and not lot.is_temporary %}
<div class="btn-group dropdown ml-1" uib-dropdown=""> <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"> <button id="btnSnapshot" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
@ -610,14 +588,12 @@
</div> </div>
</div> </div>
</section> </section>
{% include "inventory/addDevicestag.html" %}
{% include "inventory/lot_delete_modal.html" %} {% include "inventory/lot_delete_modal.html" %}
{% include "inventory/actions.html" %} {% include "inventory/actions.html" %}
{% include "inventory/allocate.html" %} {% include "inventory/allocate.html" %}
{% include "inventory/data_wipe.html" %} {% include "inventory/data_wipe.html" %}
{% include "inventory/trade.html" %} {% include "inventory/trade.html" %}
{% include "inventory/alert_export_error.html" %} {% include "inventory/alert_export_error.html" %}
{% include "inventory/alert_unlink_tag_error.html" %}
{% include "inventory/alert_lots_changes.html" %} {% include "inventory/alert_lots_changes.html" %}
<!-- Custom Code --> <!-- Custom Code -->

View File

@ -40,18 +40,6 @@
<dt>Data storage:</dt> <dt>Data storage:</dt>
<dd>{{ erasure.device.__format__('ts') }}</dd> <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> <dt>Erasure:</dt>
<dd>{{ erasure.__format__('ts') }}</dd> <dd>{{ erasure.__format__('ts') }}</dd>
{% if erasure.steps %} {% if erasure.steps %}

View File

@ -28,7 +28,7 @@
</div> </div>
</div> </div>
</div> </div>
{% if devices %} {% if devices.count() %}
<div class="card"> <div class="card">
<div class="card-body pt-3" style="min-height: 650px;"> <div class="card-body pt-3" style="min-height: 650px;">
<!-- Bordered Tabs --> <!-- Bordered Tabs -->
@ -178,42 +178,11 @@
</ul> </ul>
</div> </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=""> <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"> <button id="btnTags" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-tag"></i> <i class="bi bi-tag"></i>
Labels Labels
</button> </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"> <ul class="dropdown-menu" aria-labelledby="btnTags">
<li> <li>
<form id="print_labels" method="post" action="{{ url_for('labels.print_labels') }}"> <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=""> <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"> <button id="btnSnapshot" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-laptop"></i> <i class="bi bi-laptop"></i>
New Device New Device Real
</button> </button>
<ul class="dropdown-menu" aria-labelledby="btnSnapshot"> <ul class="dropdown-menu" aria-labelledby="btnSnapshot">
<li> <li>
{% if lot %} {% 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 %} {% else %}
<a href="{{ url_for('inventory.upload_snapshot') }}" class="dropdown-item"> <a href="{{ url_for('inventory.upload_placeholder') }}" class="dropdown-item">
{% endif %} {% endif %}
<i class="bi bi-upload"></i> <i class="bi bi-upload"></i>
Upload a new Snapshot Upload Placeholder Spreadsheet
</a> </a>
</li> </li>
<li> <li>
@ -252,7 +221,26 @@
<a href="{{ url_for('inventory.device_add') }}" class="dropdown-item"> <a href="{{ url_for('inventory.device_add') }}" class="dropdown-item">
{% endif %} {% endif %}
<i class="bi bi-plus"></i> <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> </a>
</li> </li>
</ul> </ul>
@ -287,6 +275,8 @@
<th scope="col">Select</th> <th scope="col">Select</th>
<th scope="col">Title</th> <th scope="col">Title</th>
<th scope="col">DHID</th> <th scope="col">DHID</th>
<th scope="col">PHID</th>
<th scope="col">Type</th>
<th scope="col">Unique Identifiers</th> <th scope="col">Unique Identifiers</th>
<th scope="col">Lifecycle Status</th> <th scope="col">Lifecycle Status</th>
<th scope="col">Allocated Status</th> <th scope="col">Allocated Status</th>
@ -328,6 +318,12 @@
{{ dev.devicehub_id }} {{ dev.devicehub_id }}
</a> </a>
</td> </td>
<td>
{{ dev.binding and dev.binding.phid or dev.placeholder and dev.placeholder.phid or '' }}
</td>
<td>
{{ dev.is_abstract() }}
</td>
<td> <td>
{% for t in dev.tags | sort(attribute="id") %} {% for t in dev.tags | sort(attribute="id") %}
<a href="{{ url_for('labels.label_details', id=t.id)}}">{{ t.id }}</a> <a href="{{ url_for('labels.label_details', id=t.id)}}">{{ t.id }}</a>
@ -392,14 +388,12 @@
</div> </div>
</div> </div>
</section> </section>
{% include "inventory/addDevicestag.html" %}
{% include "inventory/lot_delete_modal.html" %} {% include "inventory/lot_delete_modal.html" %}
{% include "inventory/actions.html" %} {% include "inventory/actions.html" %}
{% include "inventory/allocate.html" %} {% include "inventory/allocate.html" %}
{% include "inventory/data_wipe.html" %} {% include "inventory/data_wipe.html" %}
{% include "inventory/trade.html" %} {% include "inventory/trade.html" %}
{% include "inventory/alert_export_error.html" %} {% include "inventory/alert_export_error.html" %}
{% include "inventory/alert_unlink_tag_error.html" %}
{% include "inventory/alert_lots_changes.html" %} {% include "inventory/alert_lots_changes.html" %}
<!-- Custom Code --> <!-- Custom Code -->

View File

@ -49,7 +49,7 @@
<input class="devicesList" type="hidden" name="device" /> <input class="devicesList" type="hidden" name="device" />
<div> <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> <button class="btn btn-primary" type="submit">Unlink</button>
</div> </div>
</form> </form>

View File

@ -26,111 +26,101 @@
<thead> <thead>
<tr class="text-center"> <tr class="text-center">
<th scope="col">Basic Data</th> <th scope="col">Basic Data</th>
<th scope="col">Info to be Entered</th> <th scope="col">Info Abstract device</th>
<th scope="col">Info to be Decoupled</th> <th scope="col">Info Real device</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr>
<th scope="row">PHID:</th>
<td class="table-success"></td>
<td class="table-danger text-right">{{ placeholder.phid or '' }}</td>
</tr>
<tr> <tr>
<th scope="row">Manufacturer:</th> <th scope="row">Manufacturer:</th>
<td class="table-success">{{ device.manufacturer or '' }}</td> <td class="table-success">{{ placeholder.binding.manufacturer or '' }}</td>
<td class="table-danger text-right">{{ placeholder.device.manufacturer or '' }}</td> <td class="table-warning text-right">{{ placeholder.device.manufacturer or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Model:</th> <th scope="row">Model:</th>
<td class="table-success">{{ device.model or '' }}</td> <td class="table-success">{{ placeholder.binding.model or '' }}</td>
<td class="table-danger">{{ placeholder.device.model or '' }}</td> <td class="table-warning">{{ placeholder.device.model or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Serial Number:</th> <th scope="row">Serial Number:</th>
<td class="table-success">{{ device.serial_number or '' }}</td> <td class="table-success">{{ placeholder.binding.serial_number or '' }}</td>
<td class="table-danger">{{ placeholder.device.serial_number or '' }}</td> <td class="table-warning">{{ placeholder.device.serial_number or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Brand:</th> <th scope="row">Brand:</th>
<td class="table-success">{{ device.brand or '' }}</td> <td class="table-success">{{ placeholder.binding.brand or '' }}</td>
<td class="table-danger">{{ placeholder.device.brand or '' }}</td> <td class="table-warning">{{ placeholder.device.brand or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Sku:</th> <th scope="row">Sku:</th>
<td class="table-success">{{ device.sku or '' }}</td> <td class="table-success">{{ placeholder.binding.sku or '' }}</td>
<td class="table-danger">{{ placeholder.device.sku or '' }}</td> <td class="table-warning">{{ placeholder.device.sku or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Generation:</th> <th scope="row">Generation:</th>
<td class="table-success">{{ device.generation or '' }}</td> <td class="table-success">{{ placeholder.binding.generation or '' }}</td>
<td class="table-danger">{{ placeholder.device.generation or '' }}</td> <td class="table-warning">{{ placeholder.device.generation or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Version:</th> <th scope="row">Version:</th>
<td class="table-success">{{ device.version or '' }}</td> <td class="table-success">{{ placeholder.binding.version or '' }}</td>
<td class="table-danger">{{ placeholder.device.version or '' }}</td> <td class="table-warning">{{ placeholder.device.version or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Weight:</th> <th scope="row">Weight:</th>
<td class="table-success">{{ device.weight or '' }}</td> <td class="table-success">{{ placeholder.binding.weight or '' }}</td>
<td class="table-danger">{{ placeholder.device.weight or '' }}</td> <td class="table-warning">{{ placeholder.device.weight or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Width:</th> <th scope="row">Width:</th>
<td class="table-success">{{ device.width or '' }}</td> <td class="table-success">{{ placeholder.binding.width or '' }}</td>
<td class="table-danger">{{ placeholder.device.width or '' }}</td> <td class="table-warning">{{ placeholder.device.width or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Height:</th> <th scope="row">Height:</th>
<td class="table-success">{{ device.height or '' }}</td> <td class="table-success">{{ placeholder.binding.height or '' }}</td>
<td class="table-danger">{{ placeholder.device.height or '' }}</td> <td class="table-warning">{{ placeholder.device.height or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Depth:</th> <th scope="row">Depth:</th>
<td class="table-success">{{ device.depth or '' }}</td> <td class="table-success">{{ placeholder.binding.depth or '' }}</td>
<td class="table-danger">{{ placeholder.device.depth or '' }}</td> <td class="table-warning">{{ placeholder.device.depth or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Color:</th> <th scope="row">Color:</th>
<td class="table-success">{{ device.color or '' }}</td> <td class="table-success">{{ placeholder.binding.color or '' }}</td>
<td class="table-danger">{{ placeholder.device.color or '' }}</td> <td class="table-warning">{{ placeholder.device.color or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Production date:</th> <th scope="row">Production date:</th>
<td class="table-success">{{ device.production_date or '' }}</td> <td class="table-success">{{ placeholder.binding.production_date or '' }}</td>
<td class="table-danger">{{ placeholder.device.production_date or '' }}</td> <td class="table-warning">{{ placeholder.device.production_date or '' }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Variant:</th> <th scope="row">Variant:</th>
<td class="table-success">{{ device.variant or '' }}</td> <td class="table-success">{{ placeholder.binding.variant or '' }}</td>
<td class="table-danger">{{ placeholder.device.variant or '' }}</td> <td class="table-warning">{{ placeholder.device.variant or '' }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<br /> <br />
{% if placeholder.device.components or device.components %} {% if placeholder.components %}
<h2>Components</h2> <h2>Components</h2>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="text-center"> <tr class="text-center">
<th scope="col">Info to be Entered</th> <th scope="col">Info Abstract device</th>
<th scope="col">Info to be Decoupled</th> <th scope="col">Info Real device</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="table-success"> <td class="table-success">
{% for c in device.components %}
* {{ c.verbose_name }}<br />
{% endfor %}
</td> </td>
<td class="table-danger text-right"> <td class="table-warning text-right">
{% for c in placeholder.device.components %} {{ placeholder.components or ''}}
* {{ c.verbose_name }}<br />
{% endfor %}
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -139,19 +129,22 @@
<br /> <br />
{% if placeholder.device.manual_actions or device.manual_actions %} {% if placeholder.device.manual_actions or placeholder.binding.manual_actions %}
<h2>Actions</h2> <h2>Actions</h2>
<p>
The actions will become real device and will no longer be in the abstract
</p>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="text-center"> <tr class="text-center">
<th scope="col">Info to be Entered</th> <th scope="col">Info Abstract device</th>
<th scope="col">Info to be Decoupled</th> <th scope="col">Info Real device</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="table-success"> <td class="table-success">
{% for a in device.manual_actions %} {% for a in placeholder.binding.manual_actions %}
* {{ a.t }}<br /> * {{ a.t }}<br />
{% endfor %} {% endfor %}
</td> </td>
@ -165,6 +158,32 @@
</table> </table>
{% endif %} {% 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> <div>
<form method="post"> <form method="post">
<a href="{{ url_for('inventory.device_details', id=placeholder.device.devicehub_id) }}" class="btn btn-danger">Cancel</a> <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.mvp
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
@pytest.mark.parametrize( @pytest.mark.parametrize(
'action_model_state', 'action_model_state',
( (
@ -264,15 +265,18 @@ def test_update_parent():
), ),
) )
def test_generic_action( 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.""" """Tests POSTing all generic actions."""
user = user2
action_model, state = action_model_state action_model, state = action_model_state
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) 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) action, _ = user.post(action, res=models.Action)
assert action['devices'][0]['id'] == snapshot['device']['id'] assert action['devices'][0]['id'] == real.id
device, _ = user.get(res=Device, item=snapshot['device']['devicehubID']) device, _ = user.get(res=Device, item=real.dhid)
assert device['actions'][-1]['id'] == action['id'] assert device['actions'][-1]['id'] == action['id']
assert device['physical'] == state.name assert device['physical'] == state.name
# Check if the update of device is changed # Check if the update of device is changed
@ -280,6 +284,7 @@ def test_generic_action(
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
@pytest.mark.parametrize( @pytest.mark.parametrize(
'action_model', 'action_model',
( (
@ -288,14 +293,17 @@ def test_generic_action(
), ),
) )
def test_simple_status_actions( def test_simple_status_actions(
action_model: models.Action, user: UserClient, user2: UserClient action_model: models.Action, user2: UserClient
): ):
"""Simple test of status action.""" """Simple test of status action."""
user = user2
snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) 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) 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 device['actions'][-1]['id'] == action['id']
assert action['author']['id'] == user.user['id'] assert action['author']['id'] == user.user['id']
assert action['rol_user']['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.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
@pytest.mark.parametrize( @pytest.mark.parametrize(
'action_model', 'action_model',
( (
@ -528,13 +537,16 @@ def test_recycling_container(user: UserClient):
def test_status_without_lot(action_model: models.Action, user: UserClient): def test_status_without_lot(action_model: models.Action, user: UserClient):
"""Test of status actions for devices without lot.""" """Test of status actions for devices without lot."""
snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) 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) 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'] assert device['actions'][-1]['id'] == action['id']
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
@pytest.mark.parametrize( @pytest.mark.parametrize(
'action_model', '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] 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.""" """Test of status actions for devices in a temporary lot."""
snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) 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({'name': 'MyLotOut'}, res=Lot)
lot, _ = user.post( lot, _ = user.post(
{}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device_id)] {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device_id)]
) )
action = {'type': action_model.t, 'devices': [device_id]} action = {'type': action_model.t, 'devices': [device_id]}
action, _ = user.post(action, res=models.Action) 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'] assert device['actions'][-1]['id'] == action['id']
@ -991,8 +1004,9 @@ def test_licences(client: Client):
def test_allocate(user: UserClient): def test_allocate(user: UserClient):
"""Tests allocate""" """Tests allocate"""
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
device_id = snapshot['device']['id'] abstract = Device.query.filter_by(id=snapshot['device']['id']).one()
devicehub_id = snapshot['device']['devicehubID'] device_id = abstract.binding.device.id
devicehub_id = abstract.dhid
post_request = { post_request = {
"transaction": "ccc", "transaction": "ccc",
"finalUserCode": "aabbcc", "finalUserCode": "aabbcc",
@ -1060,8 +1074,9 @@ def test_allocate_bad_dates(user: UserClient):
def test_deallocate(user: UserClient): def test_deallocate(user: UserClient):
"""Tests deallocate""" """Tests deallocate"""
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
device_id = snapshot['device']['id'] abstract = Device.query.filter_by(id=snapshot['device']['id']).one()
devicehub_id = snapshot['device']['devicehubID'] device_id = abstract.binding.device.id
devicehub_id = abstract.dhid
post_deallocate = { post_deallocate = {
"startTime": "2020-11-01T02:00:00+00:00", "startTime": "2020-11-01T02:00:00+00:00",
"transaction": "ccc", "transaction": "ccc",
@ -1393,27 +1408,6 @@ def test_price_custom():
assert c['price']['id'] == p['id'] 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.mvp
@pytest.mark.usefixtures(conftest.auth_app_context.__name__) @pytest.mark.usefixtures(conftest.auth_app_context.__name__)
def test_erase_physical(): def test_erase_physical():

View File

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

View File

@ -473,14 +473,17 @@ def test_get_devices(app: Devicehub, user: UserClient):
@pytest.mark.mvp @pytest.mark.mvp
def test_get_device_permissions(app: Devicehub, user: UserClient, user2: UserClient, @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_get_device_permissions(app: Devicehub, user: UserClient, user2: UserClient,
client: Client): client: Client):
"""Checks GETting a d.Desktop with its components.""" """Checks GETting a d.Desktop with its components."""
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot) s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
pc, res = user.get(res=d.Device, item=s['device']['devicehubID']) pc, res = user.get(res=d.Device, item=s['device']['devicehubID'])
assert res.status_code == 200 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) html, _ = client.get(res=d.Device, item=s['device']['devicehubID'], accept=ANY)
assert 'intel atom cpu n270 @ 1.60ghz' in html 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.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_hid_with_mac(app: Devicehub, user: UserClient): def test_hid_with_mac(app: Devicehub, user: UserClient):
"""Checks hid with mac.""" """Checks hid with mac."""
snapshot = file('asus-eee-1000h.snapshot.11') snapshot = file('asus-eee-1000h.snapshot.11')
snap, _ = user.post(snapshot, res=m.Snapshot) snap, _ = user.post(snapshot, res=m.Snapshot)
pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID']) 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 @pytest.mark.mvp
@ -706,6 +712,7 @@ def test_hid_with_2networkadapters(app: Devicehub, user: UserClient):
@pytest.mark.mvp @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): 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""" """Checks hid with 2 networks adapters and next drop the network is not used in hid"""
snapshot = yaml2json('asus-eee-1000h.snapshot.11') 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' network['serialNumber'] = 'a0:24:8c:7f:cf:2d'
snap, _ = user.post(json_encode(snapshot), res=m.Snapshot) snap, _ = user.post(json_encode(snapshot), res=m.Snapshot)
pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID']) 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['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abb'
snapshot['components'] = [c for c in snapshot['components'] if c != network] snapshot['components'] = [c for c in snapshot['components'] if c != network]
user.post(json_encode(snapshot), res=m.Snapshot) user.post(json_encode(snapshot), res=m.Snapshot)
devices, _ = user.get(res=d.Device) devices, _ = user.get(res=d.Device)
laptop = devices['items'][0] 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 devices['items'] if c['type'] == 'Laptop']) == 2
assert len([c for c in laptop['components'] if c['type'] == 'NetworkAdapter']) == 1 assert len([c for c in laptop['components'] if c['type'] == 'NetworkAdapter']) == 1
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_hid_with_2network_and_drop_mac_in_hid(app: Devicehub, user: UserClient): 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""" """Checks hid with 2 networks adapters and next drop the network is used in hid"""
# One tipical snapshot with 2 network cards # 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' network['serialNumber'] = 'a0:24:8c:7f:cf:2d'
snap, _ = user.post(json_encode(snapshot), res=m.Snapshot) snap, _ = user.post(json_encode(snapshot), res=m.Snapshot)
pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID']) 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 # we drop the network card then is used for to build the hid
snapshot['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abb' 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.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_device_query_search(user: UserClient): def test_device_query_search(user: UserClient):
# todo improve # todo improve
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot) 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('computer-monitor.snapshot'), res=Snapshot)
user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot) user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
i, _ = user.get(res=Device, query=[('search', 'desktop')]) 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')]) i, _ = user.get(res=Device, query=[('search', 'intel')])
assert len(i['items']) == 1 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 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 assert len(i['items']) == 1

View File

@ -319,16 +319,16 @@ def test_link_tag_to_device(user3: UserClientFlask):
} }
user3.post(uri, data=data) user3.post(uri, data=data)
body, status = user3.get('/inventory/device/') body, status = user3.get('/inventory/device/{}/'.format(dev.dhid))
assert "tag1" in body assert "tag1" in body
data = { data = {
'tag': "tag1", 'tag': "tag1",
'device': dev.id, 'device': dev.dhid,
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
uri = '/inventory/tag/devices/add/' uri = '/inventory/tag/devices/{}/add/'.format(dev.dhid)
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(list(dev.tags)) == 1 assert len(list(dev.tags)) == 1
tags = [tag.id for tag in dev.tags] tags = [tag.id for tag in dev.tags]
@ -405,7 +405,7 @@ def test_print_labels(user3: UserClientFlask):
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
} }
uri = '/inventory/tag/devices/add/' uri = '/inventory/tag/devices/{}/add/'.format(dev.dhid)
user3.post(uri, data=data) user3.post(uri, data=data)
assert len(list(dev.tags)) == 1 assert len(list(dev.tags)) == 1
@ -418,7 +418,7 @@ def test_print_labels(user3: UserClientFlask):
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
path = "/devices/{}".format(dev.devicehub_id) path = "/devices/{}".format(dev.dhid)
assert path in body assert path in body
assert "tag1" in body assert "tag1" in body
@ -2020,7 +2020,6 @@ def test_manual_binding(user3: UserClientFlask):
'model': "LC27T55", 'model': "LC27T55",
'manufacturer': "Samsung", 'manufacturer': "Samsung",
'generation': 1, 'generation': 1,
'weight': 0.1,
'height': 0.1, 'height': 0.1,
'depth': 0.1, 'depth': 0.1,
'id_device_supplier': "b2", 'id_device_supplier': "b2",
@ -2046,17 +2045,24 @@ def test_manual_binding(user3: UserClientFlask):
old_placeholder = dev_wb.binding old_placeholder = dev_wb.binding
# page binding # page binding
dhid = dev_wb.devicehub_id dhid = dev_wb.dhid
uri = f'/inventory/binding/{dhid}/sid/' uri = f'/inventory/binding/{dhid}/sid/'
body, status = user3.get(uri) body, status = user3.get(uri)
assert status == '200 OK' assert status == '200 OK'
assert 'sid' in body assert 'sid' in body
assert 'Confirm' 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 # action binding
body, status = user3.post(uri, data={}) body, status = user3.post(uri, data={})
assert status == '200 OK' 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 # check new structure
assert dev_wb.binding.phid == 'sid' assert dev_wb.binding.phid == 'sid'
@ -2144,7 +2150,7 @@ def test_unbinding(user3: UserClientFlask):
old_placeholder = dev_wb.binding old_placeholder = dev_wb.binding
# page binding # page binding
dhid = dev_wb.devicehub_id dhid = dev_wb.dhid
uri = f'/inventory/binding/{dhid}/sid/' uri = f'/inventory/binding/{dhid}/sid/'
user3.get(uri) user3.get(uri)
@ -2153,11 +2159,14 @@ def test_unbinding(user3: UserClientFlask):
user3.post(uri, data={}) user3.post(uri, data={})
assert dev.placeholder.binding == dev_wb assert dev.placeholder.binding == dev_wb
dhid = dev.dhid
# action unbinding # action unbinding
uri = '/inventory/unbinding/sid/' uri = '/inventory/unbinding/sid/'
body, status = user3.post(uri, data={}) body, status = user3.post(uri, data={})
assert status == '200 OK' 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 # 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.action.views.snapshot import save_json
from ereuse_devicehub.resources.device import models as m from ereuse_devicehub.resources.device import models as m
from ereuse_devicehub.resources.device.exceptions import NeedsId 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 ( from ereuse_devicehub.resources.device.sync import (
MismatchBetweenProperties, MismatchBetweenProperties,
MismatchBetweenTagsAndHid, MismatchBetweenTagsAndHid,
@ -91,6 +91,7 @@ def test_snapshot_schema(app: Devicehub):
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_post(user: UserClient): def test_snapshot_post(user: UserClient):
"""Tests the post snapshot endpoint (validation, etc), data correctness, """Tests the post snapshot endpoint (validation, etc), data correctness,
and relationship correctness. and relationship correctness.
@ -108,11 +109,12 @@ def test_snapshot_post(user: UserClient):
assert snapshot['author']['id'] == user.user['id'] assert snapshot['author']['id'] == user.user['id']
assert 'actions' not in snapshot['device'] assert 'actions' not in snapshot['device']
assert 'author' 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') key = itemgetter('serialNumber')
snapshot['components'].sort(key=key) snapshot['components'].sort(key=key)
device['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']} == { assert {c['type'] for c in snapshot['components']} == {
m.GraphicCard.t, m.GraphicCard.t,
@ -190,7 +192,6 @@ def test_snapshot_power_on_hours(user: UserClient):
) )
errors = SnapshotsLog.query.filter().all() errors = SnapshotsLog.query.filter().all()
snap_log = errors[1]
assert len(errors) == 2 assert len(errors) == 2
assert str(errors[0].snapshot_uuid) == snap['uuid'] assert str(errors[0].snapshot_uuid) == snap['uuid']
assert str(errors[1].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.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_component_add_remove(user: UserClient): def test_snapshot_component_add_remove(user: UserClient):
"""Tests adding and removing components and some don't generate HID. """Tests adding and removing components and some don't generate HID.
All computers generate HID. All computers generate HID.
@ -221,7 +223,8 @@ def test_snapshot_component_add_remove(user: UserClient):
# RateComputer.t), # RateComputer.t),
# perform_second_snapshot=False) # perform_second_snapshot=False)
pc1_id = snapshot1['device']['id'] 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) pc1, _ = user.get(res=m.Device, item=pc1_devicehub_id)
update1_pc1 = pc1['updated'] update1_pc1 = pc1['updated']
# Parent contains components # Parent contains components
@ -229,14 +232,15 @@ def test_snapshot_component_add_remove(user: UserClient):
'p1c1s', 'p1c1s',
'p1c2s', 'p1c2s',
'p1c3s', 'p1c3s',
) ) == tuple(x.serial_number for x in pc1_dev.binding.device.components)
# Components contain parent # Components contain parent
assert all(c['parent'] == pc1_id for c in pc1['components']) assert all(c['parent'] == pc1_id for c in pc1['components'])
# pc has three actions: Snapshot, BenchmarkProcessor and RateComputer # pc has three actions: Snapshot, BenchmarkProcessor and RateComputer
assert len(pc1['actions']) == 2 assert len(pc1['actions']) == 2
assert pc1['actions'][1]['type'] == Snapshot.t assert pc1['actions'][1]['type'] == Snapshot.t
# p1c1s has Snapshot # 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',) assert tuple(e['type'] for e in p1c1s['actions']) == ('Snapshot',)
# We register a new device # 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'), # snapshot2 = snapshot_and_check(user, s2, action_types=('Remove', 'RateComputer'),
# perform_second_snapshot=False) # perform_second_snapshot=False)
pc2_id = snapshot2['device']['id'] 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) pc1, _ = user.get(res=m.Device, item=pc1_devicehub_id)
pc2, _ = user.get(res=m.Device, item=pc2_devicehub_id) pc2, _ = user.get(res=m.Device, item=pc2_devicehub_id)
# Check if the update_timestamp is updated # 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 all(c['parent'] == pc2_id for c in pc2['components'])
assert tuple(e['type'] for e in pc2['actions']) == ('Snapshot',) assert tuple(e['type'] for e in pc2['actions']) == ('Snapshot',)
# p1c2s has two Snapshots, a Remove and an Add # 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']) == ( assert tuple(e['type'] for e in p1c2s['actions']) == (
'BenchmarkProcessor', 'BenchmarkProcessor',
'Snapshot', 'Snapshot',
@ -299,8 +305,8 @@ def test_snapshot_component_add_remove(user: UserClient):
assert tuple(get_actions_info(pc1['actions'])) == ( assert tuple(get_actions_info(pc1['actions'])) == (
# id, type, components, snapshot # id, type, components, snapshot
('BenchmarkProcessor', []), # first BenchmarkProcessor ('BenchmarkProcessor', []), # first BenchmarkProcessor
('Snapshot', ['p1c1s', 'p1c2s', 'p1c3s']), # first Snapshot1 ('Snapshot', ['p1c1s', 'p1c2s', 'p1c3s', 'p1c2s']), # first Snapshot1
('Remove', ['p1c2s']), # Remove Processor in Snapshot2 ('Remove', ['p1c2s', 'p1c2s']), # Remove Processor in Snapshot2
('Snapshot', ['p1c2s', 'p1c3s']), # This Snapshot3 ('Snapshot', ['p1c2s', 'p1c3s']), # This Snapshot3
) )
# PC2 # PC2
@ -311,12 +317,13 @@ def test_snapshot_component_add_remove(user: UserClient):
'Remove', # the processor we added in 2. 'Remove', # the processor we added in 2.
) )
# p1c2s has Snapshot, Remove and Add # 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'])) == ( assert tuple(get_actions_info(p1c2s['actions'])) == (
('BenchmarkProcessor', []), # first BenchmarkProcessor ('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 ('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 ('Snapshot', ['p1c2s', 'p1c3s']), # The third Snapshot to PC1
('Remove', ['p1c2s']), # ...which caused p1c2 to be removed from PC2 ('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.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_erase_privacy_standards_endtime_sort(user: UserClient): def test_erase_privacy_standards_endtime_sort(user: UserClient):
"""Tests a Snapshot with EraseSectors and the resulting privacy """Tests a Snapshot with EraseSectors and the resulting privacy
properties. properties.
@ -520,8 +528,9 @@ def test_erase_privacy_standards_endtime_sort(user: UserClient):
# The actual test # The actual test
storage = next(e for e in snapshot['components'] if e['type'] == SolidStateDrive.t) 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( storage, _ = user.get(
res=m.Device, item=storage['devicehubID'] res=m.Device, item=db_storage.devicehub_id
) # Let's get storage actions too ) # Let's get storage actions too
# order: endTime ascending # order: endTime ascending
# erasure1/2 have an user defined time and others actions endTime = created # 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 'num' not in step2
assert ['HMG_IS5'] == erasure['standards'] assert ['HMG_IS5'] == erasure['standards']
assert storage['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)
# pc, _ = user.get(res=m.Device, item=snapshot['device']['devicehubID'])
assert pc['privacy'] == [storage['privacy']] assert pc['privacy'] == [storage['privacy']]
# Let's try a second erasure with an error # Let's try a second erasure with an error
s['uuid'] = uuid4() s['uuid'] = uuid4()
s['components'][0]['actions'][0]['severity'] = 'Error' s['components'][0]['actions'][0]['severity'] = 'Error'
snapshot, _ = user.post(json_encode(s), res=Snapshot) 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 storage['hid'] == 'solidstatedrive-c1mr-c1ml-c1s'
assert dev.components[0].privacy.type == 'EraseSectors'
assert storage['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']] assert pc['privacy'] == [storage['privacy']]
@ -889,23 +903,26 @@ def test_snapshot_failed_missing_chassis(app: Devicehub, user: UserClient):
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_failed_end_time_bug(app: Devicehub, user: UserClient): 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 """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 and then we get a /devices, this create a crash
""" """
snapshot_file = file('asus-end_time_bug88.snapshot') snapshot_file = file('asus-end_time_bug88.snapshot')
snapshot, _ = user.post(res=Snapshot, data=snapshot_file) 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']] end_times = [x['endTime'] for x in device['actions']]
assert '1970-01-02T00:00:00+00:00' in end_times 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'] tmp_snapshots = app.config['TMP_SNAPSHOTS']
shutil.rmtree(tmp_snapshots) shutil.rmtree(tmp_snapshots)
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_not_failed_end_time_bug(app: Devicehub, user: UserClient): 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 """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 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 = yaml2json('asus-end_time_bug88.snapshot')
snapshot_file['endTime'] = '2001-01-01 00:00:00+00:00' snapshot_file['endTime'] = '2001-01-01 00:00:00+00:00'
snapshot, _ = user.post(res=Snapshot, data=json_encode(snapshot_file)) 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']] end_times = [x['endTime'] for x in device['actions']]
assert not '1970-01-02T00:00:00+00:00' in end_times 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/") body, res = user.post(snapshot, uri="/api/inventory/")
dev = m.Device.query.filter_by(devicehub_id=body['dhid']).one() 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] ssd = [x for x in dev.components if x.type == 'SolidStateDrive'][0]
assert dev.manufacturer == 'lenovo' assert dev.manufacturer == 'lenovo'
@ -1000,6 +1019,7 @@ def test_snapshot_wb_lite_qemu(user: UserClient):
assert res.status == '201 CREATED' assert res.status == '201 CREATED'
dev = m.Device.query.filter_by(devicehub_id=body['dhid']).one() dev = m.Device.query.filter_by(devicehub_id=body['dhid']).one()
dev = dev.placeholder.binding
assert dev.manufacturer == 'qemu' assert dev.manufacturer == 'qemu'
assert dev.model == 'standard' assert dev.model == 'standard'
assert dev.serial_number is None assert dev.serial_number is None
@ -1235,6 +1255,7 @@ def test_snapshot_errors(user: UserClient):
assert len(SnapshotsLog.query.all()) == 1 assert len(SnapshotsLog.query.all()) == 1
bodyLite, res = user.post(snapshot_lite, uri="/api/inventory/") bodyLite, res = user.post(snapshot_lite, uri="/api/inventory/")
dev = m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one() dev = m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one()
dev = dev.placeholder.binding
assert len(SnapshotsLog.query.all()) == 4 assert len(SnapshotsLog.query.all()) == 4
assert body11['device'].get('hid') == dev.hid 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 len(logs) == 1
assert logs[0].description == 'Ok' assert logs[0].description == 'Ok'
dev = m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one() dev = m.Device.query.filter_by(devicehub_id=bodyLite['dhid']).one()
dev = dev.placeholder.binding
assert not dev.model assert not dev.model
assert not dev.manufacturer assert not dev.manufacturer
assert not dev.serial_number 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 = yaml2json('basic.snapshot')
s['device']['tags'] = [{'id': 'foo', 'secondary': 'bar', 'type': 'Tag'}] s['device']['tags'] = [{'id': 'foo', 'secondary': 'bar', 'type': 'Tag'}]
snapshot, _ = user.post(json_encode(s), res=Snapshot) snapshot, _ = user.post(json_encode(s), res=Snapshot)
device, _ = user.get(res=Device, item=snapshot['device']['devicehubID']) dev = Device.query.filter_by(id=snapshot['device']['id']).one()
desktop = Device.query.filter_by( device, _ = user.get(res=Device, item=dev.devicehub_id)
devicehub_id=snapshot['device']['devicehubID'] desktop = dev.binding.device
).one()
assert [] == [x['id'] for x in device['tags']] assert [] == [x['id'] for x in device['tags']]
assert 'foo' in [x.id 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.binding.device.tags] assert 'bar' in [x.secondary for x in desktop.tags]
r, _ = user.get( r, _ = user.get(
res=Device, query=[('search', 'foo'), ('filter', {'type': ['Computer']})] 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.device.models import Device
from ereuse_devicehub.resources.tag.model import Tag from ereuse_devicehub.resources.tag.model import Tag
from tests.conftest import file, file_workbench, json_encode, yaml2json from tests.conftest import file, file_workbench, json_encode, yaml2json
from tests import conftest
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_workbench_server_condensed(user: UserClient): def test_workbench_server_condensed(user: UserClient):
"""As :def:`.test_workbench_server_phases` but all the actions """As :def:`.test_workbench_server_phases` but all the actions
condensed in only one big ``Snapshot`` file, as described 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['closed']
assert snapshot['severity'] == 'Info' 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['dataStorageSize'] == 1100
assert device['chassis'] == 'Tower' assert device['chassis'] == 'Tower'
assert device['hid'] == 'desktop-d1mr-d1ml-d1s-na1-s' 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.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient): def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
"""Checks the values of the device, components, """Checks the values of the device, components,
actions and their relationships of a real pc. actions and their relationships of a real pc.
""" """
s = file('real-eee-1001pxd.snapshot.11') s = file('real-eee-1001pxd.snapshot.11')
snapshot, _ = user.post(res=em.Snapshot, data=s) snapshot, _ = user.post(res=em.Snapshot, data=s)
pc, _ = user.get(res=Device, item=snapshot['device']['devicehubID']) 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['type'] == 'Laptop'
assert pc['chassis'] == 'Netbook' assert pc['chassis'] == 'Netbook'
assert pc['model'] == '1001pxd' assert pc['model'] == '1001pxd'
@ -222,7 +227,8 @@ def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
assert cpu['speed'] == 1.667 assert cpu['speed'] == 1.667
assert 'hid' not in cpu assert 'hid' not in cpu
assert pc['processorModel'] == cpu['model'] == 'intel atom cpu n455 @ 1.66ghz' 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'] actions = cpu['actions']
sysbench = next(e for e in actions if e['type'] == em.BenchmarkProcessorSysbench.t) sysbench = next(e for e in actions if e['type'] == em.BenchmarkProcessorSysbench.t)
assert sysbench['elapsed'] == 164 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['manufacturer'] == 'intel corporation'
assert gpu['memory'] == 256 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']) action_types = tuple(e['type'] for e in gpu['actions'])
assert em.BenchmarkRamSysbench.t in action_types assert em.BenchmarkRamSysbench.t in action_types
assert em.StressTest.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['hid'] == 'harddrive-hitachi-hts54322-e2024242cv86hj'
assert hdd['interface'] == 'ATA' assert hdd['interface'] == 'ATA'
assert hdd['size'] == 238475 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']) action_types = tuple(e['type'] for e in hdd['actions'])
assert em.BenchmarkRamSysbench.t in action_types assert em.BenchmarkRamSysbench.t in action_types
assert em.StressTest.t in action_types assert em.StressTest.t in action_types