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/views.py b/ereuse_devicehub/inventory/views.py index 1d34a7b3..147ba4d5 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -118,16 +118,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', 5)) + 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 @@ -138,8 +158,17 @@ class DeviceListView(DeviceListMixin): class AllDeviceListView(DeviceListMixin): + template_name = 'inventory/all_device_list.html' + def dispatch_request(self): self.get_context(all_devices=True) + # import pdb; pdb.set_trace() + page = int(request.args.get('page', 1)) + per_page = int(request.args.get('per_page', 5)) + devices = self.context['devices'].paginate(page=page, per_page=per_page) + devices.first = per_page * devices.page - per_page + 1 + devices.last = len(devices.items) + devices.first - 1 + self.context['devices'] = devices return flask.render_template(self.template_name, **self.context) 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/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/templates/inventory/all_device_list.html b/ereuse_devicehub/templates/inventory/all_device_list.html new file mode 100644 index 00000000..79ff81cd --- /dev/null +++ b/ereuse_devicehub/templates/inventory/all_device_list.html @@ -0,0 +1,671 @@ +{% extends "ereuse_devicehub/base_site.html" %} +{% block main %} + +
+

Inventory

+ +
+ +
+
+ +
+ +
+ {% if lot %} +
+ + +
+
+

+ {{ lot.name }} +

+
+ +
+ {% if lot.is_temporary or not lot.transfer.closed %} + + {% if lot and lot.is_temporary %} + + Create Outgoing Lot + + + Create Incoming Lot + + {% endif %} + + Delete Lot + + + {% endif %} +
+
+
+ {% endif %} +
+ + {% if lot %} + + {% endif %} +
+
+ + + + + + + + + + + + + + +
+
+
+ {% for f in form_filter %} + {{ f }} + {% endfor %} + +
+
+ +

+ Displaying devices of type + {{ form_filter.filter.data or "Computer" }} +

+ +
+
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + {% for dev in devices.items %} + {% if dev.placeholder and (not dev.parent_id or dev.parent.placeholder.kangaroo) %} + + + + + + + + + + + + + + + {% endif %} + {% endfor %} + +
SelectTitleDHIDPHIDTypeUnique IdentifiersLifecycle StatusAllocated StatusPhysical StatusUpdated inRegistered in
+ + + + {% if dev.get_type_logo() %} + + {% endif %} + {{ dev.verbose_name }} + + {% if dev.lots | length > 0 %} +
+ {% for lot in dev.get_lots_for_template() %} + {{ lot }} + {% endfor %} +
+ {% endif %} +
+ + {{ dev.devicehub_id }} + + + {{ dev.binding and dev.binding.phid or dev.placeholder and dev.placeholder.phid or '' }} + + {{ dev.is_abstract() }} + + {% for t in dev.tags | sort(attribute="id") %} + {{ t.id }} + {% if not loop.last %},{% endif %} + {% endfor %} + {% if dev.status %}{{ dev.status.type }}{% endif %}{% if dev.allocated_status %}{{ dev.allocated_status.type }}{% endif %}{% if dev.physical_status %}{{ dev.physical_status.type }}{% endif %}{{ dev.get_updated.strftime('%Y-%m-%d %H:%M:%S')}}{{ dev.created.strftime('%Y-%m-%d %H:%M:%S')}} + + + +
+
+
+ Showing {{ devices.first }} to {{ devices.last }} of {{ devices.total }} entries +
+ +
+
+ +
+
+ {% if lot and not lot.is_temporary %} +
+ + +
Documents
+ + + + + + + + + {% for doc in lot.documents %} + + + + + {% endfor %} + {% for doc in lot.trade.documents %} + + + + + {% endfor %} + +
FileUploaded on
+ {% if doc.get_url() %} + {{ doc.file_name}} + {% else %} + {{ doc.file_name}} + {% endif %} + + {{ doc.created.strftime('%Y-%m-%d %H:%M')}} +
+ {% if doc.get_url() %} + {{ doc.file_name}} + {% else %} + {{ doc.file_name}} + {% endif %} + + {{ doc.created.strftime('%Y-%m-%d %H:%M')}} +
+
+
+
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 %} + +
+
+
+
+ +
+ +
+ +
+{% 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 %} + +{% endif %} + + +{% endblock main %} diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 8f14446a..98894fe5 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -7,11 +7,7 @@