diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d69d4a2..ec1dff26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,35 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
ml).
## testing
+- [added] #414 add new vars in the settings file for wb.
+
+## [2.5.0] - 2022-11-30
+- [added] #407 erasure section with tabs in top.
+- [added] #411 add new generic device as Other.
+- [changed] #409 add backend pagination instead of javascript.
+- [changed] #410 change teh top search for advanced search.
+- [fixed] #412 show in snapshots log, type upload correctly.
+- [fixed] #413 put order in documents.
+- [fixed] #415 put prefix of lot in result of search.
+
+## [2.4.3] - 2022-11-18
+- [added] #386 add registration module.
+- [added] #387 add template settings for Secure Erasure.
+- [added] #397 add obada standard export.
+- [added] #402 add reset password module.
+- [added] #406 add orphans disks page.
+- [changed] #391 add dhid in table and export of Erasure section.
+- [changed] #395 change response for the new api to workbench.
+- [changed] #396 modularize commands.
+- [fixed] #388 lock update different motherboard with the same id.
+- [fixed] #389 some datastorage without placeholder.
+- [fixed] #390 fix image in form edit device.
+- [fixed] #398 placeholder in new components.
+- [fixed] #399 add api_host in config.
+- [fixed] #401 db_host need to be api address.
+- [fixed] #403 change delimiter in obada export.
+- [fixed] #404 javascript select all devices.
+- [fixed] #405 update pillow.
## [2.4.2] - 2022-10-18
- [added] #373 Enhancement - UX Lots.
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/__init__.py b/ereuse_devicehub/__init__.py
index 60be088d..50062f87 100644
--- a/ereuse_devicehub/__init__.py
+++ b/ereuse_devicehub/__init__.py
@@ -1 +1 @@
-__version__ = "2.4.2"
+__version__ = "2.5.0"
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 cb24baa8..0769796d 100644
--- a/ereuse_devicehub/inventory/forms.py
+++ b/ereuse_devicehub/inventory/forms.py
@@ -50,11 +50,15 @@ from ereuse_devicehub.resources.device.models import (
Keyboard,
Laptop,
MemoryCardReader,
+ Monitor,
Mouse,
+ Other,
Placeholder,
+ Projector,
Server,
Smartphone,
Tablet,
+ TelevisionSet,
)
from ereuse_devicehub.resources.documents.models import DataWipeDocument
from ereuse_devicehub.resources.enums import Severity
@@ -81,16 +85,23 @@ DEVICES = {
],
"Mobile, tablet & smartphone": [
"All Mobile",
- "Mobile",
"Tablet",
"Smartphone",
"Cellphone",
],
"Drives & Storage": [
"All DataStorage",
- "HardDrives",
+ "HardDrive",
"SolidStageDrive",
],
+ "Accessories": [
+ "All Accessories",
+ "Mouse",
+ "MemoryCardReader",
+ "SAI",
+ "Keyboard",
+ ],
+ "Other Devices": ["Other"],
}
COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer']
@@ -98,6 +109,8 @@ COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer']
MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"]
MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"]
STORAGE = ["HardDrive", "SolidStateDrive"]
+ACCESSORIES = ["Mouse", "MemoryCardReader", "SAI", "Keyboard"]
+OTHERS = ["Other"]
class AdvancedSearchForm(FlaskForm):
@@ -170,7 +183,7 @@ class FilterForm(FlaskForm):
# Generic Filters
if "All Devices" == self.device_type:
- filter_type = COMPUTERS + MONITORS + MOBILE
+ filter_type = COMPUTERS + MONITORS + MOBILE + OTHERS
elif "All Computers" == self.device_type:
filter_type = COMPUTERS
@@ -184,6 +197,9 @@ class FilterForm(FlaskForm):
elif "All DataStorage" == self.device_type:
filter_type = STORAGE
+ elif "All Accessories" == self.device_type:
+ filter_type = ACCESSORIES
+
if filter_type:
self.devices = self.devices.filter(Device.type.in_(filter_type))
@@ -374,10 +390,14 @@ class NewDeviceForm(FlaskForm):
"Tablet": Tablet,
"Cellphone": Cellphone,
"ComputerMonitor": ComputerMonitor,
+ "Monitor": Monitor,
+ "TelevisionSet": TelevisionSet,
+ "Projector": Projector,
"Mouse": Mouse,
"Keyboard": Keyboard,
"SAI": SAI,
"MemoryCardReader": MemoryCardReader,
+ "Other": Other,
}
def reset_from_obj(self):
diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py
index 93740af5..b9c3a659 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,
}
)
@@ -115,15 +127,43 @@ class DeviceListMixin(GenericMixin):
class ErasureListView(DeviceListMixin):
template_name = 'inventory/erasure_list.html'
- def dispatch_request(self):
+ def dispatch_request(self, orphans=0):
self.get_context()
- self.get_devices()
+ self.get_devices(orphans)
return flask.render_template(self.template_name, **self.context)
- def get_devices(self):
+ 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:
+ schema = app.config.get('SCHEMA')
+ _user = g.user.id
+ 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)
+ and action.author_id='{_user}'
+ """
+ ids = (e[0] for e in db.session.execute(sql))
+ erasure = (
+ EraseBasic.query.filter(EraseBasic.id.in_(ids))
+ .filter_by(author=g.user)
+ .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
@@ -1174,43 +1214,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):
@@ -1340,10 +1354,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
@@ -1452,3 +1473,7 @@ devices.add_url_rule(
devices.add_url_rule(
'/device/erasure/', view_func=ErasureListView.as_view('device_erasure_list')
)
+devices.add_url_rule(
+ '/device/erasure//',
+ view_func=ErasureListView.as_view('device_erasure_list_orphans'),
+)
diff --git a/ereuse_devicehub/migrations/versions/410aadae7652_device_other.py b/ereuse_devicehub/migrations/versions/410aadae7652_device_other.py
new file mode 100644
index 00000000..1d22d1dd
--- /dev/null
+++ b/ereuse_devicehub/migrations/versions/410aadae7652_device_other.py
@@ -0,0 +1,39 @@
+"""device other
+
+Revision ID: 410aadae7652
+Revises: d65745749e34
+Create Date: 2022-11-29 12:00:40.272121
+
+"""
+import sqlalchemy as sa
+from alembic import context, op
+
+# revision identifiers, used by Alembic.
+revision = '410aadae7652'
+down_revision = 'd65745749e34'
+branch_labels = None
+depends_on = None
+
+
+def get_inv():
+ INV = context.get_x_argument(as_dictionary=True).get('inventory')
+ if not INV:
+ raise ValueError("Inventory value is not specified")
+ return INV
+
+
+def upgrade():
+ op.create_table(
+ 'other',
+ sa.Column('id', sa.BigInteger(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ['id'],
+ [f'{get_inv()}.device.id'],
+ ),
+ sa.PrimaryKeyConstraint('id'),
+ schema=f'{get_inv()}',
+ )
+
+
+def downgrade():
+ op.drop_table('other', schema=f'{get_inv()}')
diff --git a/ereuse_devicehub/migrations/versions/af038a8a388c_add_settings_version_to_snapshots.py b/ereuse_devicehub/migrations/versions/af038a8a388c_add_settings_version_to_snapshots.py
new file mode 100644
index 00000000..1b7b5a20
--- /dev/null
+++ b/ereuse_devicehub/migrations/versions/af038a8a388c_add_settings_version_to_snapshots.py
@@ -0,0 +1,35 @@
+"""add settings_version to snapshots
+
+Revision ID: af038a8a388c
+Revises: 410aadae7652
+Create Date: 2022-11-30 16:21:05.768024
+
+"""
+import citext
+import sqlalchemy as sa
+from alembic import context, op
+
+# revision identifiers, used by Alembic.
+revision = 'af038a8a388c'
+down_revision = '410aadae7652'
+branch_labels = None
+depends_on = None
+
+
+def get_inv():
+ INV = context.get_x_argument(as_dictionary=True).get('inventory')
+ if not INV:
+ raise ValueError("Inventory value is not specified")
+ return INV
+
+
+def upgrade():
+ op.add_column(
+ 'snapshot',
+ sa.Column('settings_version', citext.CIText(), nullable=True),
+ schema=f'{get_inv()}',
+ )
+
+
+def downgrade():
+ op.drop_column('snapshot', 'settings_version', schema=f'{get_inv()}')
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..38ddbaf7 100644
--- a/ereuse_devicehub/parser/models.py
+++ b/ereuse_devicehub/parser/models.py
@@ -73,11 +73,25 @@ class SnapshotsLog(Thing):
snapshots = []
for s in self.snapshot.device.actions:
if s == self.snapshot:
- continue
+ break
if s.type == self.snapshot.type:
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 ''
+
+ def get_version(self):
+ if not self.snapshot:
+ return self.version
+ settings_version = self.snapshot.settings_version or ''
+ settings_version = "".join([x[0] for x in settings_version.split(' ') if x])
+
+ return "{} ({})".format(self.version, settings_version)
+
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/action/models.py b/ereuse_devicehub/resources/action/models.py
index a5121748..6060e037 100644
--- a/ereuse_devicehub/resources/action/models.py
+++ b/ereuse_devicehub/resources/action/models.py
@@ -676,6 +676,7 @@ class Snapshot(JoinedWithOneDeviceMixin, ActionWithOneDevice):
of time it took to complete.
"""
sid = Column(CIText(), nullable=True)
+ settings_version = Column(CIText(), nullable=True)
is_server_erase = Column(Boolean(), nullable=True)
def get_last_lifetimes(self):
diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py
index f24e2c83..da70c49a 100644
--- a/ereuse_devicehub/resources/action/schemas.py
+++ b/ereuse_devicehub/resources/action/schemas.py
@@ -453,6 +453,7 @@ class Snapshot(ActionWithOneDevice):
'Order is preserved, so the component num 0 when'
'submitting is the component num 0 when returning it back.',
)
+ settings_version = String(required=False)
@validates_schema
def validate_workbench_version(self, data: dict):
diff --git a/ereuse_devicehub/resources/device/definitions.py b/ereuse_devicehub/resources/device/definitions.py
index cdc99c05..d5678135 100644
--- a/ereuse_devicehub/resources/device/definitions.py
+++ b/ereuse_devicehub/resources/device/definitions.py
@@ -4,7 +4,11 @@ from teal.resource import Converters, Resource
from ereuse_devicehub.resources.device import schemas
from ereuse_devicehub.resources.device.models import Manufacturer
-from ereuse_devicehub.resources.device.views import DeviceView, DeviceMergeView, ManufacturerView
+from ereuse_devicehub.resources.device.views import (
+ DeviceMergeView,
+ DeviceView,
+ ManufacturerView,
+)
class DeviceDef(Resource):
@@ -13,25 +17,42 @@ class DeviceDef(Resource):
ID_CONVERTER = Converters.string
AUTH = False # We manage this at each view
- def __init__(self, app,
- import_name=__name__,
- static_folder='static',
- static_url_path=None,
- template_folder='templates',
- url_prefix=None,
- subdomain=None,
- url_defaults=None,
- root_path=None,
- cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder='static',
+ static_url_path=None,
+ template_folder='templates',
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
- device_merge = DeviceMergeView.as_view('merge-devices', definition=self, auth=app.auth)
+ device_merge = DeviceMergeView.as_view(
+ 'merge-devices', definition=self, auth=app.auth
+ )
if self.AUTH:
device_merge = app.auth.requires_auth(device_merge)
- path = '/<{value}:dev1_id>/merge/<{value}:dev2_id>'.format(value=self.ID_CONVERTER.value)
+ path = '/<{value}:dev1_id>/merge/<{value}:dev2_id>'.format(
+ value=self.ID_CONVERTER.value
+ )
# self.add_url_rule(path, view_func=device_merge, methods={'POST'})
@@ -40,11 +61,31 @@ class ComputerDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Computer
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class DesktopDef(ComputerDef):
@@ -66,11 +107,31 @@ class MonitorDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Monitor
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class ComputerMonitorDef(MonitorDef):
@@ -83,15 +144,40 @@ class TelevisionSetDef(MonitorDef):
SCHEMA = schemas.TelevisionSet
+class ProjectorDef(MonitorDef):
+ VIEW = None
+ SCHEMA = schemas.Projector
+
+
class MobileDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Mobile
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class SmartphoneDef(MobileDef):
@@ -113,11 +199,31 @@ class ComponentDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Component
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class GraphicCardDef(ComponentDef):
@@ -184,11 +290,31 @@ class ComputerAccessoryDef(DeviceDef):
VIEW = None
SCHEMA = schemas.ComputerAccessory
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class MouseDef(ComputerAccessoryDef):
@@ -215,11 +341,31 @@ class NetworkingDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Networking
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class RouterDef(NetworkingDef):
@@ -246,11 +392,31 @@ class PrinterDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Printer
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class LabelPrinterDef(PrinterDef):
@@ -262,11 +428,31 @@ class SoundDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Sound
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class MicrophoneDef(SoundDef):
@@ -278,11 +464,31 @@ class VideoDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Video
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class VideoScalerDef(VideoDef):
@@ -299,11 +505,31 @@ class CookingDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Cooking
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class Mixer(CookingDef):
@@ -315,11 +541,31 @@ class DIYAndGardeningDef(DeviceDef):
VIEW = None
SCHEMA = schemas.DIYAndGardening
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class DrillDef(DIYAndGardeningDef):
@@ -331,22 +577,62 @@ class PackOfScrewdriversDef(DIYAndGardeningDef):
VIEW = None
SCHEMA = schemas.PackOfScrewdrivers
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class HomeDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Home
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class DehumidifierDef(HomeDef):
@@ -363,11 +649,31 @@ class RecreationDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Recreation
- def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
- template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
- root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
- super().__init__(app, import_name, static_folder, static_url_path, template_folder,
- url_prefix, subdomain, url_defaults, root_path, cli_commands)
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
class BikeDef(RecreationDef):
@@ -389,3 +695,35 @@ class ManufacturerDef(Resource):
"""Loads the manufacturers to the database."""
if exclude_schema != 'common':
Manufacturer.add_all_to_session(db.session)
+
+
+class OtherDef(DeviceDef):
+ VIEW = None
+ SCHEMA = schemas.Computer
+ SCHEMA = schemas.Other
+
+ def __init__(
+ self,
+ app,
+ import_name=__name__,
+ static_folder=None,
+ static_url_path=None,
+ template_folder=None,
+ url_prefix=None,
+ subdomain=None,
+ url_defaults=None,
+ root_path=None,
+ cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(),
+ ):
+ super().__init__(
+ app,
+ import_name,
+ static_folder,
+ static_url_path,
+ template_folder,
+ url_prefix,
+ subdomain,
+ url_defaults,
+ root_path,
+ cli_commands,
+ )
diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py
index 3df44688..61e8cae7 100644
--- a/ereuse_devicehub/resources/device/models.py
+++ b/ereuse_devicehub/resources/device/models.py
@@ -635,6 +635,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:
@@ -1394,6 +1402,19 @@ class DataStorage(JoinedComponentTableMixin, Component):
except LookupError:
return None
+ @property
+ def orphan(self):
+ if not self.parent:
+ return True
+
+ if self.parent.placeholder and self.parent.placeholder.kangaroo:
+ return True
+
+ if self.parent.binding and self.parent.binding.kangaroo:
+ return True
+
+ return False
+
class HardDrive(DataStorage):
pass
@@ -1684,3 +1705,11 @@ def create_code_tag(mapper, connection, device):
# from flask_sqlalchemy import event
# event.listen(Device, 'after_insert', create_code_tag, propagate=True)
+
+
+class Other(Device):
+ """
+ Used for put in there all devices than not have actualy a class
+ """
+
+ id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py
index c217b3dd..76a1fee2 100644
--- a/ereuse_devicehub/resources/device/schemas.py
+++ b/ereuse_devicehub/resources/device/schemas.py
@@ -270,6 +270,10 @@ class TelevisionSet(Monitor):
__doc__ = m.TelevisionSet.__doc__
+class Projector(Monitor):
+ __doc__ = m.Projector.__doc__
+
+
class Mobile(Device):
__doc__ = m.Mobile.__doc__
@@ -578,3 +582,7 @@ class Bike(Recreation):
class Racket(Recreation):
pass
+
+
+class Other(Device):
+ pass
diff --git a/ereuse_devicehub/resources/lot/views.py b/ereuse_devicehub/resources/lot/views.py
index c3416db2..80922e47 100644
--- a/ereuse_devicehub/resources/lot/views.py
+++ b/ereuse_devicehub/resources/lot/views.py
@@ -306,7 +306,7 @@ class LotDeviceView(LotBaseChildrenView):
dev_qry = Device.query.filter(Device.id.in_(ids)).filter(Device.owner == g.user)
for dev in dev_qry:
- if isinstance(dev, DataStorage) and dev.parent:
+ if isinstance(dev, DataStorage) and not dev.orphan:
continue
devices.add(dev)
diff --git a/ereuse_devicehub/resources/tradedocument/models.py b/ereuse_devicehub/resources/tradedocument/models.py
index 7f211637..07af62d1 100644
--- a/ereuse_devicehub/resources/tradedocument/models.py
+++ b/ereuse_devicehub/resources/tradedocument/models.py
@@ -172,7 +172,9 @@ class TradeDocument(Thing):
return sorted(ev for ev in actions if ev.severity >= Severity.Warning)
def __lt__(self, other):
- return self.id < other.id
+ if self.id and other.id:
+ return self.id < other.id
+ return False
def __str__(self) -> str:
return '{0.file_name}'.format(self)
diff --git a/ereuse_devicehub/static/js/main.js b/ereuse_devicehub/static/js/main.js
index 474658d5..7bea4a96 100644
--- a/ereuse_devicehub/static/js/main.js
+++ b/ereuse_devicehub/static/js/main.js
@@ -48,12 +48,12 @@
/**
* Search bar toggle
- */
if (select(".search-bar-toggle")) {
on("click", ".search-bar-toggle", (e) => {
select(".search-bar").classList.toggle("search-bar-show")
})
}
+ */
/**
* Navbar links active state on scroll
diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html
index 57e13f3a..f3865433 100644
--- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html
+++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html
@@ -8,6 +8,12 @@
+
+
+
@@ -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 e33bed45..2f36de71 100644
--- a/ereuse_devicehub/templates/inventory/erasure_list.html
+++ b/ereuse_devicehub/templates/inventory/erasure_list.html
@@ -18,9 +18,54 @@
-
+
+
+ {% if orphans %}
+
+
+
+
+
+
+ {% endif %}
+ {% if orphans %}
+
+
+
+
+ {% endif %}
+
+
+ If this text is showing is because there are an error
+
+
+
+
+
+
+
+
+
+
+
+ Showing {{ erasure.first }} to {{ erasure.last }} of {{ erasure.total }} entries
+
+
- {% if lot and not lot.is_temporary %}
-
-
Documents
-
-
-
- File |
- Uploaded on |
-
-
-
- {% for doc in lot.trade.documents %}
-
-
- {% if doc.url %}
- {{ doc.file_name}}
- {% else %}
- {{ doc.file_name}}
- {% endif %}
- |
-
- {{ doc.created.strftime('%H:%M %d-%m-%Y')}}
- |
-
- {% endfor %}
-
-
-
-
-
-
- {% endif %}
@@ -255,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/search.html b/ereuse_devicehub/templates/inventory/search.html
index 9f724586..57f62975 100644
--- a/ereuse_devicehub/templates/inventory/search.html
+++ b/ereuse_devicehub/templates/inventory/search.html
@@ -16,7 +16,7 @@