diff --git a/docs/conf.py b/docs/conf.py index a1b4e42e..73d3ccc9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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 diff --git a/ereuse_devicehub/config.py b/ereuse_devicehub/config.py index 79a9b75b..a41c9640 100644 --- a/ereuse_devicehub/config.py +++ b/ereuse_devicehub/config.py @@ -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') diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index 7947a85b..ee7f7c15 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -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: diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index a46c4dd5..3801a07d 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -91,7 +91,7 @@ DEVICES = { ], "Drives & Storage": [ "All DataStorage", - "HardDrives", + "HardDrive", "SolidStageDrive", ], "Accessories": [ diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 1d34a7b3..fe1c37e7 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -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 diff --git a/ereuse_devicehub/parser/computer.py b/ereuse_devicehub/parser/computer.py index afd2e17d..b41ce833 100644 --- a/ereuse_devicehub/parser/computer.py +++ b/ereuse_devicehub/parser/computer.py @@ -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: diff --git a/ereuse_devicehub/parser/models.py b/ereuse_devicehub/parser/models.py index 4d298af0..bdd13f73 100644 --- a/ereuse_devicehub/parser/models.py +++ b/ereuse_devicehub/parser/models.py @@ -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.""" diff --git a/ereuse_devicehub/parser/snapshot.py b/ereuse_devicehub/parser/snapshot.py index 559a7f48..490797af 100644 --- a/ereuse_devicehub/parser/snapshot.py +++ b/ereuse_devicehub/parser/snapshot.py @@ -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): diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 0f398737..35e4d559 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -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: diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 8f14446a..78c3e39d 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -335,6 +335,8 @@ {% for f in form_filter %} {{ f }} {% endfor %} + + @@ -344,6 +346,38 @@ {{ form_filter.filter.data or "Computer" }}

+
+
+ +
+ +
+
@@ -362,7 +396,7 @@ - {% for dev in devices %} + {% for dev in devices.items %} {% if dev.placeholder and (not dev.parent_id or dev.parent.placeholder.kangaroo) %}
@@ -421,6 +455,57 @@ {% endfor %}
+
+
+ Showing {{ devices.first }} to {{ devices.last }} of {{ devices.total }} entries +
+ +
+
@@ -592,10 +677,25 @@ {% include "inventory/alert_lots_changes.html" %} + {% if config['DEBUG'] %} diff --git a/ereuse_devicehub/templates/inventory/erasure_list.html b/ereuse_devicehub/templates/inventory/erasure_list.html index adaec94f..2f36de71 100644 --- a/ereuse_devicehub/templates/inventory/erasure_list.html +++ b/ereuse_devicehub/templates/inventory/erasure_list.html @@ -22,7 +22,7 @@ @@ -109,7 +109,43 @@ {% endif %} + +
+
+
+ +
+ +
+
@@ -125,10 +161,10 @@ - {% for ac in erasure %} + {% for ac in erasure.items %}
- 0 %} + {% if ac.device.my_partner.lots | length > 0 %}
- {% for lot in ac.device.get_lots_for_template() %} + {% for lot in ac.device.my_partner.get_lots_for_template() %} {{ lot }} {% endfor %}
@@ -170,7 +206,7 @@
- {{ ac.snapshot.uuid }} + {{ ac.snapshot.uuid }} @@ -194,135 +230,54 @@ {% endfor %}
- +
+
+
+ Showing {{ erasure.first }} to {{ erasure.last }} of {{ erasure.total }} entries +
+
- {% if lot and not lot.is_temporary %} -
-
Documents
- - - - - - - - - {% for doc in lot.trade.documents %} - - - - - {% endfor %} - -
FileUploaded on
- {% if doc.url %} - {{ doc.file_name}} - {% else %} - {{ doc.file_name}} - {% endif %} - - {{ doc.created.strftime('%H:%M %d-%m-%Y')}} -
-
-
-
Transfer
-
- {{ form_transfer.csrf_token }} - - {% for field in form_transfer %} - {% if field != form_transfer.csrf_token %} -
- {% if field != form_transfer.type %} - {{ field.label(class_="form-label") }} - {% if field == form_transfer.code %} - * - {% endif %} - {{ field }} - {{ field.description }} - {% if field.errors %} -

- {% for error in field.errors %} - {{ error }}
- {% endfor %} -

- {% endif %} - {% endif %} -
- {% endif %} - {% endfor %} - -
- Cancel - -
-
-
-
-
Delivery Note
-
- {{ form_delivery.csrf_token }} - - {% for field in form_delivery %} - {% if field != form_delivery.csrf_token %} -
- {% if field != form_delivery.type %} - {{ field.label(class_="form-label") }} - {{ field }} - {{ field.description }} - {% if field.errors %} -

- {% for error in field.errors %} - {{ error }}
- {% endfor %} -

- {% endif %} - {% endif %} -
- {% endif %} - {% endfor %} - - {% if lot.transfer and form_receiver.is_editable() %} -
- Cancel - -
- {% endif %} -
-
-
-
Receiver Note
-
- {{ form_receiver.csrf_token }} - - {% for field in form_receiver %} - {% if field != form_receiver.csrf_token %} -
- {% if field != form_receiver.type %} - {{ field.label(class_="form-label") }} - {{ field }} - {{ field.description }} - {% if field.errors %} -

- {% for error in field.errors %} - {{ error }}
- {% endfor %} -

- {% endif %} - {% endif %} -
- {% endif %} - {% endfor %} - - {% if lot.transfer and form_receiver.is_editable() %} -
- Cancel - -
- {% endif %} -
-
- {% endif %} @@ -334,21 +289,32 @@ -{% 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" %} + + {% if config['DEBUG'] %} {% else %} diff --git a/ereuse_devicehub/templates/inventory/placeholder_log_list.html b/ereuse_devicehub/templates/inventory/placeholder_log_list.html index aaff827d..bc16ccab 100644 --- a/ereuse_devicehub/templates/inventory/placeholder_log_list.html +++ b/ereuse_devicehub/templates/inventory/placeholder_log_list.html @@ -22,6 +22,38 @@
+
+
+ +
+ +
+
@@ -34,7 +66,7 @@ - {% for log in placeholders_log %} + {% for log in placeholders_log.items %}
{{ log.phid }} @@ -58,6 +90,38 @@ {% endfor %}
+
+
+ Showing {{ placeholders_log.first }} to {{ placeholders_log.last }} of {{ placeholders_log.total }} entries +
+ +
@@ -75,6 +139,18 @@ + {% endblock main %} diff --git a/ereuse_devicehub/templates/inventory/snapshots_list.html b/ereuse_devicehub/templates/inventory/snapshots_list.html index 4c35f44e..1804f364 100644 --- a/ereuse_devicehub/templates/inventory/snapshots_list.html +++ b/ereuse_devicehub/templates/inventory/snapshots_list.html @@ -22,6 +22,38 @@
+
+
+ +
+ +
+
@@ -39,7 +71,7 @@ - {% for snap in snapshots_log %} + {% for snap in snapshots_log.items %}
{% if snap.sid and snap.snapshot_uuid %} @@ -59,26 +91,26 @@ {{ snap.version }} - {% if snap.device %} + {% if snap.get_device() %} - {{ snap.device }} + {{ snap.get_device() }} {% endif %} - {{ snap.system_uuid }} + {{ snap.get_system_uuid() }} - {{ snap.status }} + {{ snap.get_status() }} - {{ snap.new_device }} + {{ snap.get_new_device() }} - {{ snap.type_device }} + {{ snap.get_type_device() }} - {{ snap.original_dhid }} + {{ snap.get_original_dhid() }} {{ snap.created.strftime('%Y-%m-%d %H:%M') }} @@ -93,6 +125,38 @@
+
+
+ Showing {{ snapshots_log.first }} to {{ snapshots_log.last }} of {{ snapshots_log.total }} entries +
+ +
@@ -109,6 +173,18 @@ + {% endblock main %} diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 0a95eee1..fbd437d9 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -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)