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

View File

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

View File

@ -1,7 +1,6 @@
import itertools
import json
from pathlib import Path
from typing import Set
import click
import click_spinner
@ -109,7 +108,7 @@ class Dummy:
files = tuple(Path(__file__).parent.joinpath('files').iterdir())
print('done.')
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:
for path in bar:
with path.open() as f:

View File

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

View File

@ -55,15 +55,26 @@ devices = Blueprint('inventory', __name__, url_prefix='/inventory')
logger = logging.getLogger(__name__)
PER_PAGE = 20
class DeviceListMixin(GenericMixin):
template_name = 'inventory/device_list.html'
def get_context(self, lot_id=None, all_devices=False):
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']
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
form_transfer = ''
form_delivery = ''
@ -92,6 +103,7 @@ class DeviceListMixin(GenericMixin):
'tags': self.get_user_tags(),
'list_devices': self.get_selected_devices(form_new_action),
'all_devices': all_devices,
'filter': filter,
}
)
@ -118,16 +130,36 @@ class ErasureListView(DeviceListMixin):
def dispatch_request(self, orphans=0):
self.get_context()
self.get_devices(orphans)
if orphans:
self.context['orphans'] = True
return flask.render_template(self.template_name, **self.context)
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(
EraseBasic.created.desc()
)
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
@ -1178,43 +1210,17 @@ class SnapshotListView(GenericMixin):
return flask.render_template(self.template_name, **self.context)
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(
SnapshotsLog.owner == g.user
).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:
logs[snap.snapshot_uuid] = {
'sid': snap.sid,
'snapshot_uuid': snap.snapshot_uuid,
'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
snapshots_log = snapshots_log.paginate(page=page, per_page=per_page)
snapshots_log.first = per_page * snapshots_log.page - per_page + 1
snapshots_log.last = len(snapshots_log.items) + snapshots_log.first - 1
return snapshots_log
class SnapshotDetailView(GenericMixin):
@ -1344,10 +1350,17 @@ class PlaceholderLogListView(GenericMixin):
return flask.render_template(self.template_name, **self.context)
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(
PlaceholdersLog.owner == g.user
).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

View File

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

View File

@ -78,6 +78,12 @@ class SnapshotsLog(Thing):
snapshots.append(s)
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):
"""A Placeholder log."""

View File

@ -1,7 +1,6 @@
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
@ -24,8 +23,8 @@ class Snapshot(Dumpeable):
self.endTime = datetime.now(timezone.utc)
self.closed = False
self.elapsed = None
self.device = None # type: Computer
self.components = None # type: List[Component]
self.device = None
self.components = None
self._storages = None
def computer(self):

View File

@ -632,6 +632,14 @@ class Device(Thing):
return self.binding.device.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
def get_updated(self):
if self.placeholder and self.placeholder.binding:

View File

@ -335,6 +335,8 @@
{% for f in form_filter %}
{{ f }}
{% 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" />
</div>
</form>
@ -344,6 +346,38 @@
<em>{{ form_filter.filter.data or "Computer" }}</em>
</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">
<thead>
<tr>
@ -362,7 +396,7 @@
</tr>
</thead>
<tbody>
{% for dev in devices %}
{% for dev in devices.items %}
{% if dev.placeholder and (not dev.parent_id or dev.parent.placeholder.kangaroo) %}
<tr>
<td>
@ -421,6 +455,57 @@
{% endfor %}
</tbody>
</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>
@ -592,10 +677,25 @@
{% include "inventory/alert_lots_changes.html" %}
<!-- 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>
let table = new simpleDatatables.DataTable("table", {
perPageSelect: [5, 10, 15, 20, 25, 50, 100],
perPage: 20
footer: false,
paging: false,
})
</script>
{% if config['DEBUG'] %}

View File

@ -22,7 +22,7 @@
<li class="nav-item">
<a href="{{ url_for('inventory.device_erasure_list') }}" class="nav-link{% if not orphans %} active{% endif %}">
All hard drivers
All hard drives
</a>
</li>
@ -109,7 +109,43 @@
</div>
{% 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="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">
<thead>
<tr>
@ -125,10 +161,10 @@
</tr>
</thead>
<tbody>
{% for ac in erasure %}
{% for ac in erasure.items %}
<tr>
<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-dhid="{{ ac.device.dhid }}" data-device-vname="{{ ac.device.verbose_name }}"
data-action-erasure="{{ ac.id }}"
@ -151,9 +187,9 @@
{% endif %}
{{ ac.device.serial_number.upper() }}
{% endif %}
{% if ac.device.lots | length > 0 %}
{% if ac.device.my_partner.lots | length > 0 %}
<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>
{% endfor %}
</h6>
@ -170,7 +206,7 @@
</td>
<td>
<a href="{{ url_for('inventory.export', export_id='snapshot') }}?id={{ ac.snapshot.uuid }}">
{{ ac.snapshot.uuid }}
{{ ac.snapshot.uuid }}
</a>
</td>
<td>
@ -194,135 +230,54 @@
{% endfor %}
</tbody>
</table>
</div>
<div class="dataTable-bottom">
<div class="dataTable-info">
Showing {{ erasure.first }} to {{ erasure.last }} of {{ erasure.total }} entries
</div>
<nav class="dataTable-pagination">
<ul class="dataTable-pagination-list">
{% if erasure.has_prev %}
<li class="pager">
{% if orphans %}
<a href="{{ url_for('inventory.device_erasure_list_orphans', orphans=1, page=erasure.prev_num, per_page=erasure.per_page) }}"></a>
{% else %}
<a href="{{ url_for('inventory.device_erasure_list', page=erasure.prev_num, per_page=erasure.per_page) }}"></a>
{% endif %}
</li>
{% endif %}
{% for page in erasure.iter_pages() %}
{% if page %}
{% if page == erasure.page %}
<li class="active"><a href="javascript:void()">{{ page }}</a></li>
{% else %}
<li class="">
{% if orphans %}
<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 %}
</li>
{% endif %}
{% endif %}
{% endfor %}
{% if erasure.has_next %}
<li class="pager">
{% if orphans %}
<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>
{% if lot and not lot.is_temporary %}
<div id="trade-documents-list" class="tab-pane fade trade-documents-list">
<h5 class="card-title">Documents</h5>
<table class="table">
<thead>
<tr>
<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 %}
{{ doc.file_name}}
{% endif %}
</td>
<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 %}
{{ 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 %}
<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>
</form>
</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>
@ -334,21 +289,32 @@
</div>
</div>
</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_lots_changes.html" %}
<!-- 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>
let table = new simpleDatatables.DataTable("table", {
perPageSelect: [5, 10, 15, 20, 25, 50, 100],
perPage: 20
//perPageSelect: [5, 10, 15, 20, 25, 50, 100],
//perPage: 20,
footer: false,
paging: false,
})
</script>
{% if config['DEBUG'] %}
<script src="{{ url_for('static', filename='js/main_inventory.js') }}"></script>
{% else %}

View File

@ -22,6 +22,38 @@
<div class="tab-content pt-5">
<div id="devices-list" class="tab-pane fade devices-list active show">
<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">
<thead>
<tr>
@ -34,7 +66,7 @@
</tr>
</thead>
<tbody>
{% for log in placeholders_log %}
{% for log in placeholders_log.items %}
<tr>
<td>
{{ log.phid }}
@ -58,6 +90,38 @@
{% endfor %}
</tbody>
</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>
@ -75,6 +139,18 @@
<!-- Custom Code -->
<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>
{% endblock main %}

View File

@ -22,6 +22,38 @@
<div class="tab-content pt-5">
<div id="devices-list" class="tab-pane fade devices-list active show">
<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">
<thead>
<tr>
@ -39,7 +71,7 @@
</tr>
</thead>
<tbody>
{% for snap in snapshots_log %}
{% for snap in snapshots_log.items %}
<tr>
<td>
{% if snap.sid and snap.snapshot_uuid %}
@ -59,26 +91,26 @@
{{ snap.version }}
</td>
<td>
{% if snap.device %}
{% if snap.get_device() %}
<a href="{{ url_for('inventory.device_details', id=snap.device) }}">
{{ snap.device }}
{{ snap.get_device() }}
</a>
{% endif %}
</td>
<td>
{{ snap.system_uuid }}
{{ snap.get_system_uuid() }}
</td>
<td>
{{ snap.status }}
{{ snap.get_status() }}
</td>
<td>
{{ snap.new_device }}
{{ snap.get_new_device() }}
</td>
<td>
{{ snap.type_device }}
{{ snap.get_type_device() }}
</td>
<td>
{{ snap.original_dhid }}
{{ snap.get_original_dhid() }}
</td>
<td>{{ snap.created.strftime('%Y-%m-%d %H:%M') }}</td>
<td>
@ -93,6 +125,38 @@
</tbody>
</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>
@ -109,6 +173,18 @@
<!-- Custom Code -->
<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>
{% endblock main %}

View File

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