Merge branch 'testing' into feature/4094-add-new-fields

This commit is contained in:
Cayo Puigdefabregas 2022-11-29 10:09:10 +01:00
commit b84f379468
14 changed files with 472 additions and 219 deletions

View File

@ -30,7 +30,6 @@ from teal.enums import Country, Currency, Layouts, Subdivision
from teal.marshmallow import EnumField from teal.marshmallow import EnumField
from ereuse_devicehub.marshmallow import NestedOn from ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources.schemas import Thing
project = 'Devicehub' project = 'Devicehub'
copyright = '2020, eReuse.org team' copyright = '2020, eReuse.org team'
@ -56,7 +55,7 @@ extensions = [
'sphinx.ext.viewcode', 'sphinx.ext.viewcode',
'sphinxcontrib.plantuml', 'sphinxcontrib.plantuml',
'sphinx.ext.autosectionlabel', 'sphinx.ext.autosectionlabel',
'sphinx.ext.autodoc' 'sphinx.ext.autodoc',
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
@ -126,15 +125,12 @@ latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
# #
# 'papersize': 'letterpaper', # 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
# #
# 'pointsize': '10pt', # 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
# #
# 'preamble': '', # 'preamble': '',
# Latex figure (float) alignment # Latex figure (float) alignment
# #
# 'figure_align': 'htbp', # 'figure_align': 'htbp',
@ -144,18 +140,20 @@ latex_elements = {
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
(master_doc, 'Devicehub.tex', 'Devicehub Documentation', (
'eReuse.org team', 'manual'), master_doc,
'Devicehub.tex',
'Devicehub Documentation',
'eReuse.org team',
'manual',
),
] ]
# -- Options for manual page output ------------------------------------------ # -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [(master_doc, 'devicehub', 'Devicehub Documentation', [author], 1)]
(master_doc, 'devicehub', 'Devicehub Documentation',
[author], 1)
]
# -- Options for Texinfo output ---------------------------------------------- # -- Options for Texinfo output ----------------------------------------------
@ -163,9 +161,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
(master_doc, 'Devicehub', 'Devicehub Documentation', (
author, 'Devicehub', 'One line description of project.', master_doc,
'Miscellaneous'), 'Devicehub',
'Devicehub Documentation',
author,
'Devicehub',
'One line description of project.',
'Miscellaneous',
),
] ]
# -- Extension configuration ------------------------------------------------- # -- Extension configuration -------------------------------------------------
@ -199,6 +203,7 @@ class DhlistDirective(Directive):
This requires :py:class:`ereuse_devicehub.resources.schemas.SchemaMeta`. This requires :py:class:`ereuse_devicehub.resources.schemas.SchemaMeta`.
You will find in that module more information. You will find in that module more information.
""" """
has_content = False has_content = False
# Definition of passed-in options # Definition of passed-in options
@ -216,7 +221,7 @@ class DhlistDirective(Directive):
sections = [] sections = []
sections.append(self.links(things)) # Make index sections.append(self.links(things)) # Make index
for thng in things: # type: Thing for thng in things:
# Generate a section for each class, with a title, # Generate a section for each class, with a title,
# fields description and a paragraph # fields description and a paragraph
section = n.section(ids=[self._id(thng)]) section = n.section(ids=[self._id(thng)])
@ -228,7 +233,9 @@ class DhlistDirective(Directive):
for key, f in thng._own: for key, f in thng._own:
name = n.field_name(text=f.data_key or key) name = n.field_name(text=f.data_key or key)
body = [ body = [
self.parse('{} {}'.format(self.type(f), f.metadata.get('description', ''))) self.parse(
'{} {}'.format(self.type(f), f.metadata.get('description', ''))
)
] ]
if isinstance(f, EnumField): if isinstance(f, EnumField):
body.append(self._parse_enum_field(f)) body.append(self._parse_enum_field(f))
@ -244,6 +251,7 @@ class DhlistDirective(Directive):
def _parse_enum_field(self, f): def _parse_enum_field(self, f):
from ereuse_devicehub.resources.device import states from ereuse_devicehub.resources.device import states
if issubclass(f.enum, (Subdivision, Currency, Country, Layouts, states.State)): if issubclass(f.enum, (Subdivision, Currency, Country, Layouts, states.State)):
return self.parse(f.enum.__doc__) return self.parse(f.enum.__doc__)
else: else:

View File

@ -1,6 +1,5 @@
from distutils.version import StrictVersion from distutils.version import StrictVersion
from itertools import chain from itertools import chain
from typing import Set
from decouple import config from decouple import config
from teal.auth import TokenAuth from teal.auth import TokenAuth
@ -44,7 +43,7 @@ class DevicehubConfig(Config):
import_resource(metric_def), import_resource(metric_def),
), ),
) )
PASSWORD_SCHEMES = {'pbkdf2_sha256'} # type: Set[str] PASSWORD_SCHEMES = {'pbkdf2_sha256'}
SECRET_KEY = config('SECRET_KEY') SECRET_KEY = config('SECRET_KEY')
DB_USER = config('DB_USER', 'dhub') DB_USER = config('DB_USER', 'dhub')
DB_PASSWORD = config('DB_PASSWORD', 'ereuse') DB_PASSWORD = config('DB_PASSWORD', 'ereuse')

View File

@ -1,7 +1,6 @@
import itertools import itertools
import json import json
from pathlib import Path from pathlib import Path
from typing import Set
import click import click
import click_spinner import click_spinner
@ -109,7 +108,7 @@ class Dummy:
files = tuple(Path(__file__).parent.joinpath('files').iterdir()) files = tuple(Path(__file__).parent.joinpath('files').iterdir())
print('done.') print('done.')
sample_pc = None # We treat this one as a special sample for demonstrations sample_pc = None # We treat this one as a special sample for demonstrations
pcs = set() # type: Set[int] pcs = set()
with click.progressbar(files, label='Creating devices...'.ljust(28)) as bar: with click.progressbar(files, label='Creating devices...'.ljust(28)) as bar:
for path in bar: for path in bar:
with path.open() as f: with path.open() as f:

View File

@ -91,7 +91,7 @@ DEVICES = {
], ],
"Drives & Storage": [ "Drives & Storage": [
"All DataStorage", "All DataStorage",
"HardDrives", "HardDrive",
"SolidStageDrive", "SolidStageDrive",
], ],
"Accessories": [ "Accessories": [

View File

@ -55,15 +55,26 @@ devices = Blueprint('inventory', __name__, url_prefix='/inventory')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
PER_PAGE = 20
class DeviceListMixin(GenericMixin): class DeviceListMixin(GenericMixin):
template_name = 'inventory/device_list.html' template_name = 'inventory/device_list.html'
def get_context(self, lot_id=None, all_devices=False): def get_context(self, lot_id=None, all_devices=False):
super().get_context() super().get_context()
page = int(request.args.get('page', 1))
per_page = int(request.args.get('per_page', PER_PAGE))
filter = request.args.get('filter', "All+Computers")
# import pdb; pdb.set_trace()
lots = self.context['lots'] lots = self.context['lots']
form_filter = FilterForm(lots, lot_id, all_devices=all_devices) form_filter = FilterForm(lots, lot_id, all_devices=all_devices)
devices = form_filter.search() devices = form_filter.search().paginate(page=page, per_page=per_page)
devices.first = per_page * devices.page - per_page + 1
devices.last = len(devices.items) + devices.first - 1
lot = None lot = None
form_transfer = '' form_transfer = ''
form_delivery = '' form_delivery = ''
@ -92,6 +103,7 @@ class DeviceListMixin(GenericMixin):
'tags': self.get_user_tags(), 'tags': self.get_user_tags(),
'list_devices': self.get_selected_devices(form_new_action), 'list_devices': self.get_selected_devices(form_new_action),
'all_devices': all_devices, 'all_devices': all_devices,
'filter': filter,
} }
) )
@ -118,16 +130,36 @@ class ErasureListView(DeviceListMixin):
def dispatch_request(self, orphans=0): def dispatch_request(self, orphans=0):
self.get_context() self.get_context()
self.get_devices(orphans) self.get_devices(orphans)
if orphans:
self.context['orphans'] = True
return flask.render_template(self.template_name, **self.context) return flask.render_template(self.template_name, **self.context)
def get_devices(self, orphans): def get_devices(self, orphans):
page = int(request.args.get('page', 1))
per_page = int(request.args.get('per_page', PER_PAGE))
erasure = EraseBasic.query.filter_by(author=g.user).order_by( erasure = EraseBasic.query.filter_by(author=g.user).order_by(
EraseBasic.created.desc() EraseBasic.created.desc()
) )
if orphans: if orphans:
erasure = [e for e in erasure if e.device.orphan] schema = app.config.get('SCHEMA')
sql = f"""
select action.id from {schema}.action as action
inner join {schema}.erase_basic as erase
on action.id=erase.id
inner join {schema}.device as device
on device.id=action.parent_id
inner join {schema}.placeholder as placeholder
on placeholder.binding_id=device.id
where action.parent_id is null or placeholder.kangaroo=true
"""
ids = (e[0] for e in db.session.execute(sql))
erasure = EraseBasic.query.filter(EraseBasic.id.in_(ids)).order_by(
EraseBasic.created.desc()
)
self.context['orphans'] = True
erasure = erasure.paginate(page=page, per_page=per_page)
erasure.first = per_page * erasure.page - per_page + 1
erasure.last = len(erasure.items) + erasure.first - 1
self.context['erasure'] = erasure self.context['erasure'] = erasure
@ -1178,43 +1210,17 @@ class SnapshotListView(GenericMixin):
return flask.render_template(self.template_name, **self.context) return flask.render_template(self.template_name, **self.context)
def get_snapshots_log(self): def get_snapshots_log(self):
page = int(request.args.get('page', 1))
per_page = int(request.args.get('per_page', PER_PAGE))
snapshots_log = SnapshotsLog.query.filter( snapshots_log = SnapshotsLog.query.filter(
SnapshotsLog.owner == g.user SnapshotsLog.owner == g.user
).order_by(SnapshotsLog.created.desc()) ).order_by(SnapshotsLog.created.desc())
logs = {}
for snap in snapshots_log:
try:
system_uuid = snap.snapshot.device.system_uuid or ''
except AttributeError:
system_uuid = ''
if snap.snapshot_uuid not in logs: snapshots_log = snapshots_log.paginate(page=page, per_page=per_page)
logs[snap.snapshot_uuid] = { snapshots_log.first = per_page * snapshots_log.page - per_page + 1
'sid': snap.sid, snapshots_log.last = len(snapshots_log.items) + snapshots_log.first - 1
'snapshot_uuid': snap.snapshot_uuid, return snapshots_log
'version': snap.version,
'device': snap.get_device(),
'system_uuid': system_uuid,
'status': snap.get_status(),
'severity': snap.severity,
'created': snap.created,
'type_device': snap.get_type_device(),
'original_dhid': snap.get_original_dhid(),
'new_device': snap.get_new_device(),
}
continue
if snap.created > logs[snap.snapshot_uuid]['created']:
logs[snap.snapshot_uuid]['created'] = snap.created
if snap.severity > logs[snap.snapshot_uuid]['severity']:
logs[snap.snapshot_uuid]['severity'] = snap.severity
logs[snap.snapshot_uuid]['status'] = snap.get_status()
result = sorted(logs.values(), key=lambda d: d['created'])
result.reverse()
return result
class SnapshotDetailView(GenericMixin): class SnapshotDetailView(GenericMixin):
@ -1344,10 +1350,17 @@ class PlaceholderLogListView(GenericMixin):
return flask.render_template(self.template_name, **self.context) return flask.render_template(self.template_name, **self.context)
def get_placeholders_log(self): def get_placeholders_log(self):
page = int(request.args.get('page', 1))
per_page = int(request.args.get('per_page', PER_PAGE))
placeholder_log = PlaceholdersLog.query.filter( placeholder_log = PlaceholdersLog.query.filter(
PlaceholdersLog.owner == g.user PlaceholdersLog.owner == g.user
).order_by(PlaceholdersLog.created.desc()) ).order_by(PlaceholdersLog.created.desc())
placeholder_log = placeholder_log.paginate(page=page, per_page=per_page)
placeholder_log.first = per_page * placeholder_log.page - per_page + 1
placeholder_log.last = len(placeholder_log.items) + placeholder_log.first - 1
return placeholder_log return placeholder_log

View File

@ -4,7 +4,7 @@ from contextlib import suppress
from datetime import datetime from datetime import datetime
from fractions import Fraction from fractions import Fraction
from math import hypot from math import hypot
from typing import Iterator, List, Optional, Type, TypeVar from typing import Iterator, List, Optional, TypeVar
import dateutil.parser import dateutil.parser
from ereuse_utils import getter, text from ereuse_utils import getter, text
@ -404,7 +404,7 @@ class Computer(Device):
chassis value. chassis value.
""" """
COMPONENTS = list(Component.__subclasses__()) # type: List[Type[Component]] COMPONENTS = list(Component.__subclasses__())
COMPONENTS.remove(Motherboard) COMPONENTS.remove(Motherboard)
def __init__(self, node: dict) -> None: def __init__(self, node: dict) -> None:

View File

@ -78,6 +78,12 @@ class SnapshotsLog(Thing):
snapshots.append(s) snapshots.append(s)
return snapshots and 'Update' or 'New Device' return snapshots and 'Update' or 'New Device'
def get_system_uuid(self):
try:
return self.snapshot.device.system_uuid or ''
except AttributeError:
return ''
class PlaceholdersLog(Thing): class PlaceholdersLog(Thing):
"""A Placeholder log.""" """A Placeholder log."""

View File

@ -1,7 +1,6 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import List
from ereuse_workbench.computer import Component, Computer, DataStorage from ereuse_workbench.computer import Computer, DataStorage
from ereuse_workbench.utils import Dumpeable from ereuse_workbench.utils import Dumpeable
@ -24,8 +23,8 @@ class Snapshot(Dumpeable):
self.endTime = datetime.now(timezone.utc) self.endTime = datetime.now(timezone.utc)
self.closed = False self.closed = False
self.elapsed = None self.elapsed = None
self.device = None # type: Computer self.device = None
self.components = None # type: List[Component] self.components = None
self._storages = None self._storages = None
def computer(self): def computer(self):

View File

@ -632,6 +632,14 @@ class Device(Thing):
return self.binding.device.devicehub_id return self.binding.device.devicehub_id
return self.devicehub_id return self.devicehub_id
@property
def my_partner(self):
if self.placeholder and self.placeholder.binding:
return self.placeholder.binding
if self.binding:
return self.binding.device
return self
@property @property
def get_updated(self): def get_updated(self):
if self.placeholder and self.placeholder.binding: if self.placeholder and self.placeholder.binding:

View File

@ -335,6 +335,8 @@
{% for f in form_filter %} {% for f in form_filter %}
{{ f }} {{ f }}
{% endfor %} {% endfor %}
<input type="hidden" class="d-none" value="1" name="page" />
<input type="hidden" class="d-none" value="{{ devices.per_page }}" name="per_page" />
<input type="submit" class="ms-2 btn btn-primary" value="Filter" /> <input type="submit" class="ms-2 btn btn-primary" value="Filter" />
</div> </div>
</form> </form>
@ -344,6 +346,38 @@
<em>{{ form_filter.filter.data or "Computer" }}</em> <em>{{ form_filter.filter.data or "Computer" }}</em>
</p> </p>
<div class="dataTable-top" style="float: left;">
<div class="dataTable-dropdown">
<label>
<select class="dataTable-selector">
<option value="5"{% if devices.per_page == 5 %} selected="selected"{% endif %}>
5
</option>
<option value="10"{% if devices.per_page == 10 %} selected="selected"{% endif %}>
10
</option>
<option value="15"{% if devices.per_page == 15 %} selected="selected"{% endif %}>
15
</option>
<option value="20"{% if devices.per_page == 20 %} selected="selected"{% endif %}>
20
</option>
<option value="25"{% if devices.per_page == 25 %} selected="selected"{% endif %}>
25
</option>
<option value="50"{% if devices.per_page == 50 %} selected="selected"{% endif %}>
50
</option>
<option value="100"{% if devices.per_page == 100 %} selected="selected"{% endif %}>
100
</option>
</select> entries per page
</label>
</div>
<div class="dataTable-search">
</div>
</div>
<div class="dataTable-container">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
@ -362,7 +396,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for dev in devices %} {% for dev in devices.items %}
{% if dev.placeholder and (not dev.parent_id or dev.parent.placeholder.kangaroo) %} {% if dev.placeholder and (not dev.parent_id or dev.parent.placeholder.kangaroo) %}
<tr> <tr>
<td> <td>
@ -421,6 +455,57 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<div class="dataTable-bottom">
<div class="dataTable-info">
Showing {{ devices.first }} to {{ devices.last }} of {{ devices.total }} entries
</div>
<nav class="dataTable-pagination">
<ul class="dataTable-pagination-list">
{% if devices.has_prev %}
<li class="pager">
{% if all_devices %}
<a href="{{ url_for('inventory.alldevicelist', page=devices.prev_num, per_page=devices.per_page, filter=filter) }}"></a>
{% elif lot %}
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id, page=devices.prev_num, per_page=devices.per_page, filter=filter) }}"></a>
{% else %}
<a href="{{ url_for('inventory.devicelist', page=devices.prev_num, per_page=devices.per_page, filter=filter) }}"></a>
{% endif %}
</li>
{% endif %}
{% for page in devices.iter_pages() %}
{% if page %}
{% if page == devices.page %}
<li class="active"><a href="javascript:void()">{{ page }}</a></li>
{% else %}
<li class="">
{% if all_devices %}
<a href="{{ url_for('inventory.alldevicelist', page=page, per_page=devices.per_page, filter=filter) }}">
{% elif lot %}
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id, page=page, per_page=devices.per_page, filter=filter) }}">
{% else %}
<a href="{{ url_for('inventory.devicelist', page=page, per_page=devices.per_page, filter=filter) }}">
{% endif %}
{{ page }}
</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
{% if devices.has_next %}
<li class="pager">
{% if all_devices %}
<a href="{{ url_for('inventory.alldevicelist', page=devices.next_num, per_page=devices.per_page, filter=filter) }}"></a>
{% elif lot %}
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id, page=devices.next_num, per_page=devices.per_page, filter=filter) }}"></a>
{% else %}
<a href="{{ url_for('inventory.devicelist', page=devices.next_num, per_page=devices.per_page, filter=filter) }}"></a>
{% endif %}
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
</div> </div>
</div> </div>
@ -592,10 +677,25 @@
{% include "inventory/alert_lots_changes.html" %} {% include "inventory/alert_lots_changes.html" %}
<!-- Custom Code --> <!-- Custom Code -->
<script>
$(document).ready(() => {
$(".dataTable-selector").on("change", function() {
const per_page = $('.dataTable-selector').val();
{% if all_devices %}
window.location.href = "{{ url_for('inventory.alldevicelist', page=1) }}&filter={{ filter }}&per_page="+per_page;
{% elif lot %}
window.location.href = "{{ url_for('inventory.lotdevicelist', lot_id=lot.id, page=1) }}&filter={{ filter }}&per_page="+per_page;
{% else %}
window.location.href = "{{ url_for('inventory.devicelist', page=1) }}&filter={{ filter }}&per_page="+per_page;
{% endif %}
});
});
</script>
<script> <script>
let table = new simpleDatatables.DataTable("table", { let table = new simpleDatatables.DataTable("table", {
perPageSelect: [5, 10, 15, 20, 25, 50, 100], footer: false,
perPage: 20 paging: false,
}) })
</script> </script>
{% if config['DEBUG'] %} {% if config['DEBUG'] %}

View File

@ -22,7 +22,7 @@
<li class="nav-item"> <li class="nav-item">
<a href="{{ url_for('inventory.device_erasure_list') }}" class="nav-link{% if not orphans %} active{% endif %}"> <a href="{{ url_for('inventory.device_erasure_list') }}" class="nav-link{% if not orphans %} active{% endif %}">
All hard drivers All hard drives
</a> </a>
</li> </li>
@ -109,7 +109,43 @@
</div> </div>
{% endif %} {% endif %}
<div id="select-devices-info" class="alert alert-info mb-0 mt-3 d-none" role="alert">
If this text is showing is because there are an error
</div>
<div class="tab-content pt-2"> <div class="tab-content pt-2">
<div class="dataTable-top" style="float: left;">
<div class="dataTable-dropdown">
<label>
<select class="dataTable-selector">
<option value="5"{% if erasure.per_page == 5 %} selected="selected"{% endif %}>
5
</option>
<option value="10"{% if erasure.per_page == 10 %} selected="selected"{% endif %}>
10
</option>
<option value="15"{% if erasure.per_page == 15 %} selected="selected"{% endif %}>
15
</option>
<option value="20"{% if erasure.per_page == 20 %} selected="selected"{% endif %}>
20
</option>
<option value="25"{% if erasure.per_page == 25 %} selected="selected"{% endif %}>
25
</option>
<option value="50"{% if erasure.per_page == 50 %} selected="selected"{% endif %}>
50
</option>
<option value="100"{% if erasure.per_page == 100 %} selected="selected"{% endif %}>
100
</option>
</select> entries per page
</label>
</div>
<div class="dataTable-search">
</div>
</div>
<div class="dataTable-container">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
@ -125,10 +161,10 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for ac in erasure %} {% for ac in erasure.items %}
<tr> <tr>
<td> <td>
<input type="checkbox" class="deviceSelect" data="{{ ac.device.id }}" <input type="checkbox" class="deviceSelect" data="{{ ac.device.my_partner.id }}"
data-device-type="{{ ac.device.type }}" data-device-manufacturer="{{ ac.device.manufacturer }}" data-device-type="{{ ac.device.type }}" data-device-manufacturer="{{ ac.device.manufacturer }}"
data-device-dhid="{{ ac.device.dhid }}" data-device-vname="{{ ac.device.verbose_name }}" data-device-dhid="{{ ac.device.dhid }}" data-device-vname="{{ ac.device.verbose_name }}"
data-action-erasure="{{ ac.id }}" data-action-erasure="{{ ac.id }}"
@ -151,9 +187,9 @@
{% endif %} {% endif %}
{{ ac.device.serial_number.upper() }} {{ ac.device.serial_number.upper() }}
{% endif %} {% endif %}
{% if ac.device.lots | length > 0 %} {% if ac.device.my_partner.lots | length > 0 %}
<h6 class="d-inline"> <h6 class="d-inline">
{% for lot in ac.device.get_lots_for_template() %} {% for lot in ac.device.my_partner.get_lots_for_template() %}
<span class="badge rounded-pill bg-light text-dark">{{ lot }}</span> <span class="badge rounded-pill bg-light text-dark">{{ lot }}</span>
{% endfor %} {% endfor %}
</h6> </h6>
@ -194,135 +230,54 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="dataTable-bottom">
<div class="dataTable-info">
Showing {{ erasure.first }} to {{ erasure.last }} of {{ erasure.total }} entries
</div> </div>
{% if lot and not lot.is_temporary %} <nav class="dataTable-pagination">
<div id="trade-documents-list" class="tab-pane fade trade-documents-list"> <ul class="dataTable-pagination-list">
<h5 class="card-title">Documents</h5> {% if erasure.has_prev %}
<table class="table"> <li class="pager">
<thead> {% if orphans %}
<tr> <a href="{{ url_for('inventory.device_erasure_list_orphans', orphans=1, page=erasure.prev_num, per_page=erasure.per_page) }}"></a>
<th scope="col">File</th>
<th scope="col" data-type="date" data-format="DD-MM-YYYY">Uploaded on</th>
</tr>
</thead>
<tbody>
{% for doc in lot.trade.documents %}
<tr>
<td>
{% if doc.url %}
<a href="{{ doc.url.to_text() }}" target="_blank">{{ doc.file_name}}</a>
{% else %} {% else %}
{{ doc.file_name}} <a href="{{ url_for('inventory.device_erasure_list', page=erasure.prev_num, per_page=erasure.per_page) }}"></a>
{% endif %} {% endif %}
</td> </li>
<td>
{{ doc.created.strftime('%H:%M %d-%m-%Y')}}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div id="edit-transfer" class="tab-pane fade edit-transfer">
<h5 class="card-title">Transfer</h5>
<form method="post" action="{{ url_for('inventory.edit_transfer', lot_id=lot.id) }}" class="row g-3 needs-validation" novalidate>
{{ form_transfer.csrf_token }}
{% for field in form_transfer %}
{% if field != form_transfer.csrf_token %}
<div class="col-12">
{% if field != form_transfer.type %}
{{ field.label(class_="form-label") }}
{% if field == form_transfer.code %}
<span class="text-danger">*</span>
{% endif %} {% endif %}
{{ field }} {% for page in erasure.iter_pages() %}
<small class="text-muted">{{ field.description }}</small> {% if page %}
{% if field.errors %} {% if page == erasure.page %}
<p class="text-danger"> <li class="active"><a href="javascript:void()">{{ page }}</a></li>
{% for error in field.errors %} {% else %}
{{ error }}<br/> <li class="">
{% endfor %} {% if orphans %}
</p> <a href="{{ url_for('inventory.device_erasure_list_orphans', orphans=1, page=page, per_page=erasure.per_page) }}">
{{ page }}
</a>
{% else %}
<a href="{{ url_for('inventory.device_erasure_list', page=page, per_page=erasure.per_page) }}">
{{ page }}
</a>
{% endif %} {% endif %}
</li>
{% endif %} {% endif %}
</div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if erasure.has_next %}
<div> <li class="pager">
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}" class="btn btn-danger">Cancel</a> {% if orphans %}
<button class="btn btn-primary" type="submit">Save</button> <a href="{{ url_for('inventory.device_erasure_list_orphans', orphans=1, page=erasure.next_num, per_page=erasure.per_page) }}"></a>
{% else %}
<a href="{{ url_for('inventory.device_erasure_list', page=erasure.next_num, per_page=erasure.per_page) }}"></a>
{% endif %}
</li>
{% endif %}
</ul>
</nav>
</div> </div>
</form>
</div> </div>
<div id="edit-delivery-note" class="tab-pane fade edit-delivery-note">
<h5 class="card-title">Delivery Note</h5>
<form method="post" action="{{ url_for('inventory.delivery_note', lot_id=lot.id) }}" class="row g-3 needs-validation" novalidate>
{{ form_delivery.csrf_token }}
{% for field in form_delivery %}
{% if field != form_delivery.csrf_token %}
<div class="col-12">
{% if field != form_delivery.type %}
{{ field.label(class_="form-label") }}
{{ field }}
<small class="text-muted">{{ field.description }}</small>
{% if field.errors %}
<p class="text-danger">
{% for error in field.errors %}
{{ error }}<br/>
{% endfor %}
</p>
{% endif %}
{% endif %}
</div>
{% endif %}
{% endfor %}
{% if lot.transfer and form_receiver.is_editable() %}
<div>
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}" class="btn btn-danger">Cancel</a>
<button class="btn btn-primary" type="submit">Save</button>
</div>
{% endif %}
</form>
</div>
<div id="edit-receiver-note" class="tab-pane fade edit-receiver-note">
<h5 class="card-title">Receiver Note</h5>
<form method="post" action="{{ url_for('inventory.receiver_note', lot_id=lot.id) }}" class="row g-3 needs-validation" novalidate>
{{ form_receiver.csrf_token }}
{% for field in form_receiver %}
{% if field != form_receiver.csrf_token %}
<div class="col-12">
{% if field != form_receiver.type %}
{{ field.label(class_="form-label") }}
{{ field }}
<small class="text-muted">{{ field.description }}</small>
{% if field.errors %}
<p class="text-danger">
{% for error in field.errors %}
{{ error }}<br/>
{% endfor %}
</p>
{% endif %}
{% endif %}
</div>
{% endif %}
{% endfor %}
{% if lot.transfer and form_receiver.is_editable() %}
<div>
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}" class="btn btn-danger">Cancel</a>
<button class="btn btn-primary" type="submit">Save</button>
</div>
{% endif %}
</form>
</div>
{% endif %}
</div><!-- End Bordered Tabs --> </div><!-- End Bordered Tabs -->
</div> </div>
@ -334,21 +289,32 @@
</div> </div>
</div> </div>
</section> </section>
{% include "inventory/lot_delete_modal.html" %}
{% include "inventory/actions.html" %}
{% include "inventory/allocate.html" %}
{% include "inventory/data_wipe.html" %}
{% include "inventory/trade.html" %}
{% include "inventory/alert_export_error.html" %} {% include "inventory/alert_export_error.html" %}
{% include "inventory/alert_lots_changes.html" %} {% include "inventory/alert_lots_changes.html" %}
<!-- Custom Code --> <!-- Custom Code -->
<script>
$(document).ready(() => {
$(".dataTable-selector").on("change", function() {
const per_page = $('.dataTable-selector').val();
{% if orphans %}
window.location.href = "{{ url_for('inventory.device_erasure_list_orphans', orphans=1, page=1) }}&per_page="+per_page;
{% else %}
window.location.href = "{{ url_for('inventory.device_erasure_list', page=1) }}&per_page="+per_page;
{% endif %}
});
});
</script>
<script> <script>
let table = new simpleDatatables.DataTable("table", { let table = new simpleDatatables.DataTable("table", {
perPageSelect: [5, 10, 15, 20, 25, 50, 100], //perPageSelect: [5, 10, 15, 20, 25, 50, 100],
perPage: 20 //perPage: 20,
footer: false,
paging: false,
}) })
</script> </script>
{% if config['DEBUG'] %} {% if config['DEBUG'] %}
<script src="{{ url_for('static', filename='js/main_inventory.js') }}"></script> <script src="{{ url_for('static', filename='js/main_inventory.js') }}"></script>
{% else %} {% else %}

View File

@ -22,6 +22,38 @@
<div class="tab-content pt-5"> <div class="tab-content pt-5">
<div id="devices-list" class="tab-pane fade devices-list active show"> <div id="devices-list" class="tab-pane fade devices-list active show">
<div class="tab-content pt-2"> <div class="tab-content pt-2">
<div class="dataTable-top" style="float: left;">
<div class="dataTable-dropdown">
<label>
<select class="dataTable-selector">
<option value="5"{% if placeholders_log.per_page == 5 %} selected="selected"{% endif %}>
5
</option>
<option value="10"{% if placeholders_log.per_page == 10 %} selected="selected"{% endif %}>
10
</option>
<option value="15"{% if placeholders_log.per_page == 15 %} selected="selected"{% endif %}>
15
</option>
<option value="20"{% if placeholders_log.per_page == 20 %} selected="selected"{% endif %}>
20
</option>
<option value="25"{% if placeholders_log.per_page == 25 %} selected="selected"{% endif %}>
25
</option>
<option value="50"{% if placeholders_log.per_page == 50 %} selected="selected"{% endif %}>
50
</option>
<option value="100"{% if placeholders_log.per_page == 100 %} selected="selected"{% endif %}>
100
</option>
</select> entries per page
</label>
</div>
<div class="dataTable-search">
</div>
</div>
<div class="dataTable-container">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
@ -34,7 +66,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for log in placeholders_log %} {% for log in placeholders_log.items %}
<tr> <tr>
<td> <td>
{{ log.phid }} {{ log.phid }}
@ -58,6 +90,38 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<div class="dataTable-bottom">
<div class="dataTable-info">
Showing {{ placeholders_log.first }} to {{ placeholders_log.last }} of {{ placeholders_log.total }} entries
</div>
<nav class="dataTable-pagination">
<ul class="dataTable-pagination-list">
{% if placeholders_log.has_prev %}
<li class="pager">
<a href="{{ url_for('inventory.placeholder_logs', page=placeholders_log.prev_num, per_page=placeholders_log.per_page) }}"></a>
</li>
{% endif %}
{% for page in placeholders_log.iter_pages() %}
{% if page %}
{% if page == placeholders_log.page %}
<li class="active"><a href="javascript:void()">{{ page }}</a></li>
{% else %}
<li class="">
<a href="{{ url_for('inventory.placeholder_logs', page=page, per_page=placeholders_log.per_page) }}">
{{ page }}
</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
{% if placeholders_log.has_next %}
<li class="pager">
<a href="{{ url_for('inventory.placeholder_logs', page=placeholders_log.next_num, per_page=placeholders_log.per_page) }}"></a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div> </div>
</div> </div>
@ -75,6 +139,18 @@
<!-- Custom Code --> <!-- Custom Code -->
<script> <script>
const table = new simpleDatatables.DataTable("table") $(document).ready(() => {
$(".dataTable-selector").on("change", function() {
const per_page = $('.dataTable-selector').val();
window.location.href = "{{ url_for('inventory.placeholder_logs', page=1) }}&per_page="+per_page;
});
});
</script>
<script>
let table = new simpleDatatables.DataTable("table", {
footer: false,
paging: false,
})
</script> </script>
{% endblock main %} {% endblock main %}

View File

@ -22,6 +22,38 @@
<div class="tab-content pt-5"> <div class="tab-content pt-5">
<div id="devices-list" class="tab-pane fade devices-list active show"> <div id="devices-list" class="tab-pane fade devices-list active show">
<div class="tab-content pt-2"> <div class="tab-content pt-2">
<div class="dataTable-top" style="float: left;">
<div class="dataTable-dropdown">
<label>
<select class="dataTable-selector">
<option value="5"{% if snapshots_log.per_page == 5 %} selected="selected"{% endif %}>
5
</option>
<option value="10"{% if snapshots_log.per_page == 10 %} selected="selected"{% endif %}>
10
</option>
<option value="15"{% if snapshots_log.per_page == 15 %} selected="selected"{% endif %}>
15
</option>
<option value="20"{% if snapshots_log.per_page == 20 %} selected="selected"{% endif %}>
20
</option>
<option value="25"{% if snapshots_log.per_page == 25 %} selected="selected"{% endif %}>
25
</option>
<option value="50"{% if snapshots_log.per_page == 50 %} selected="selected"{% endif %}>
50
</option>
<option value="100"{% if snapshots_log.per_page == 100 %} selected="selected"{% endif %}>
100
</option>
</select> entries per page
</label>
</div>
<div class="dataTable-search">
</div>
</div>
<div class="dataTable-container">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
@ -39,7 +71,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for snap in snapshots_log %} {% for snap in snapshots_log.items %}
<tr> <tr>
<td> <td>
{% if snap.sid and snap.snapshot_uuid %} {% if snap.sid and snap.snapshot_uuid %}
@ -59,26 +91,26 @@
{{ snap.version }} {{ snap.version }}
</td> </td>
<td> <td>
{% if snap.device %} {% if snap.get_device() %}
<a href="{{ url_for('inventory.device_details', id=snap.device) }}"> <a href="{{ url_for('inventory.device_details', id=snap.device) }}">
{{ snap.device }} {{ snap.get_device() }}
</a> </a>
{% endif %} {% endif %}
</td> </td>
<td> <td>
{{ snap.system_uuid }} {{ snap.get_system_uuid() }}
</td> </td>
<td> <td>
{{ snap.status }} {{ snap.get_status() }}
</td> </td>
<td> <td>
{{ snap.new_device }} {{ snap.get_new_device() }}
</td> </td>
<td> <td>
{{ snap.type_device }} {{ snap.get_type_device() }}
</td> </td>
<td> <td>
{{ snap.original_dhid }} {{ snap.get_original_dhid() }}
</td> </td>
<td>{{ snap.created.strftime('%Y-%m-%d %H:%M') }}</td> <td>{{ snap.created.strftime('%Y-%m-%d %H:%M') }}</td>
<td> <td>
@ -93,6 +125,38 @@
</tbody> </tbody>
</table> </table>
<div class="dataTable-bottom">
<div class="dataTable-info">
Showing {{ snapshots_log.first }} to {{ snapshots_log.last }} of {{ snapshots_log.total }} entries
</div>
<nav class="dataTable-pagination">
<ul class="dataTable-pagination-list">
{% if snapshots_log.has_prev %}
<li class="pager">
<a href="{{ url_for('inventory.snapshotslist', page=snapshots_log.prev_num, per_page=snapshots_log.per_page) }}"></a>
</li>
{% endif %}
{% for page in snapshots_log.iter_pages() %}
{% if page %}
{% if page == snapshots_log.page %}
<li class="active"><a href="javascript:void()">{{ page }}</a></li>
{% else %}
<li class="">
<a href="{{ url_for('inventory.snapshotslist', page=page, per_page=snapshots_log.per_page) }}">
{{ page }}
</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
{% if snapshots_log.has_next %}
<li class="pager">
<a href="{{ url_for('inventory.snapshotslist', page=snapshots_log.next_num, per_page=snapshots_log.per_page) }}"></a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div> </div>
</div> </div>
@ -109,6 +173,18 @@
<!-- Custom Code --> <!-- Custom Code -->
<script> <script>
const table = new simpleDatatables.DataTable("table") $(document).ready(() => {
$(".dataTable-selector").on("change", function() {
const per_page = $('.dataTable-selector').val();
window.location.href = "{{ url_for('inventory.snapshotslist', page=1) }}&per_page="+per_page;
});
});
</script>
<script>
let table = new simpleDatatables.DataTable("table", {
footer: false,
paging: false,
})
</script> </script>
{% endblock main %} {% endblock main %}

View File

@ -2368,6 +2368,9 @@ def test_upload_snapshot_smartphone(user3: UserClientFlask):
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_list_erasures(user3: UserClientFlask): def test_list_erasures(user3: UserClientFlask):
from flask import current_app as app
app.config['SCHEMA'] = 'test'
uri = '/inventory/upload-snapshot/' uri = '/inventory/upload-snapshot/'
file_name = 'erase-sectors-2-hdd.snapshot.yaml' file_name = 'erase-sectors-2-hdd.snapshot.yaml'
body, status = user3.get(uri) body, status = user3.get(uri)