From ab06bd5f44b0d567b63024ad7c3df14d5c962528 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 28 Sep 2022 11:10:34 +0200 Subject: [PATCH 01/62] fix remain_lifetime_percentage --- ereuse_devicehub/resources/action/schemas.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 56165555..45e267b2 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -296,6 +296,14 @@ class TestDataStorage(Test): offline_uncorrectable = Integer(data_key='offlineUncorrectable') remaining_lifetime_percentage = Integer(data_key='remainingLifetimePercentage') + @post_load + def default_remaining_lifetime_percentage(self, data): + if not data.get('remaining_lifetime_percentage'): + return + + if data.get('remaining_lifetime_percentage') > 100: + data['remaining_lifetime_percentage'] = 100 + class StressTest(Test): __doc__ = m.StressTest.__doc__ From 60992a13da480dbd72ad6b3922e9ed1db2c93769 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 28 Sep 2022 11:55:31 +0200 Subject: [PATCH 02/62] change 100 for None --- ereuse_devicehub/resources/action/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 45e267b2..f24e2c83 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -302,7 +302,7 @@ class TestDataStorage(Test): return if data.get('remaining_lifetime_percentage') > 100: - data['remaining_lifetime_percentage'] = 100 + data['remaining_lifetime_percentage'] = None class StressTest(Test): From df73cd5657a9645fb99e6ccc61d7610c278abe7b Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 29 Sep 2022 12:04:05 +0200 Subject: [PATCH 03/62] Fix build-js pre-commit hook Only run when main_inventory is run & discard parameters passed by pre-commit which breaks babel command --- .pre-commit-config.yaml | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 35bfc9bf..8d1307be 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,5 +19,9 @@ repos: hooks: - id: build-js name: build-js + # pre-commit pass as parameters files included on the commit + # so babel command should be wrapped to ignore these files on + # package.json script entry: npm run babel language: node + files: ^ereuse_devicehub/static/js/main_inventory.js diff --git a/package.json b/package.json index 4cb22b4e..21df849b 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "scripts": { "lint:report": "eslint ereuse_devicehub --ext .js --output-file eslint_report.json --format json", "lint:fix": "eslint ereuse_devicehub --ext .js --fix", - "babel": "babel ereuse_devicehub/static/js/main_inventory.js --out-file ereuse_devicehub/static/js/main_inventory.build.js" + "babel": "babel ereuse_devicehub/static/js/main_inventory.js --out-file ereuse_devicehub/static/js/main_inventory.build.js && echo 'Ignoring parameters: '" }, "keywords": [], "author": "", From a85e7c90e115f9619d9db7499c4b07be1049aeec Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 12:24:14 +0200 Subject: [PATCH 04/62] Manage dependencies using pip-tools Add instructions to CONTRIBUTING.md & create requirements.in --- CONTRIBUTING.md | 26 +++++ requirements.in | 37 +++++++ requirements.txt | 265 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 287 insertions(+), 41 deletions(-) create mode 100644 requirements.in diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 142fd954..90d9b598 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,3 +30,29 @@ pre-commit install Do this: `device_detail.html` Don't do this: `DeviceDetail.html`, `Device-detail.html` + + +## Adding a new dependency to the project +This project tracks its packages using pip-tools, it could be installed by running: +``` +pip install pip-tools +``` + +Whenever you need to install a new package using pip install : +1. Put the package name into `requirements.in` instead. +``` +# requirements.in +... +new_package +``` + +2. Compile the requirements +``` +pip-compile requirements.in --output-file=requirements.txt + +``` + +3. Then install upgraded dependencies: +``` +pip install -U -r requirements.txt +``` diff --git a/requirements.in b/requirements.in new file mode 100644 index 00000000..8a9569ca --- /dev/null +++ b/requirements.in @@ -0,0 +1,37 @@ +alembic==1.4.2 +atomicwrites==1.4.0 +click-spinner==0.1.8 +colorama==0.3.9 +colour==0.1.5 +ereuse-utils[naming,test,session,cli]==0.4.0b50 +Flask-Cors==3.0.10 +Flask-Login==0.5.0 +Flask-WTF==1.0.0 +flask-weasyprint==0.4 +hashids==1.2.0 +more-itertools==8.12.0 +passlib==1.7.1 +phonenumbers==8.9.11 +psycopg2-binary==2.8.3 +pyjwt==2.4.0 +python-decouple==3.3 +python-dotenv==0.14.0 +python-stdnum==1.9 +pyyaml==5.4 +requests==2.27.1 +requests-mock==1.5.2 +requests-toolbelt==0.9.1 +sortedcontainers==2.1.0 +sqlalchemy-citext==1.3.post0 +sqlalchemy-utils==0.33.11 +teal==0.2.0a40 +tqdm==4.32.2 + +pint==0.9 +py-dmidecode==0.1.0 +pandas==1.3.5 +numpy==1.22.0 # pandas dependency +odfpy==1.4.1 # pandas dependency +xlrd==2.0.1 # pandas dependency +openpyxl==3.0.10 # pandas dependency +et_xmlfile==1.1.0 # pandas dependency diff --git a/requirements.txt b/requirements.txt index 8ec1e0a4..d482f549 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,53 +1,236 @@ +# +# This file is autogenerated by pip-compile with python 3.8 +# To update, run: +# +# pip-compile --output-file=requirements.txt requirements.in +# alembic==1.4.2 + # via -r requirements.in anytree==2.4.3 -apispec==0.39.0 + # via teal +apispec[yaml]==5.2.2 + # via + # apispec-webframeworks + # teal +apispec-webframeworks==0.5.2 + # via teal +atomicwrites==1.4.0 + # via -r requirements.in boltons==18.0.1 + # via + # ereuse-utils + # teal +cairocffi==1.4.0 + # via + # cairosvg + # weasyprint +cairosvg==2.5.2 + # via weasyprint +certifi==2022.9.24 + # via requests +cffi==1.15.1 + # via + # cairocffi + # weasyprint +charset-normalizer==2.0.12 + # via requests click==6.7 + # via + # ereuse-utils + # flask click-spinner==0.1.8 + # via + # -r requirements.in + # teal colorama==0.3.9 + # via + # -r requirements.in + # ereuse-utils colour==0.1.5 -ereuse-utils[naming,test,session,cli]==0.4.0b50 -Flask==1.0.2 -Flask-Cors==3.0.10 -Flask-Login==0.5.0 -Flask-SQLAlchemy==2.3.2 -Flask-WTF==1.0.0 + # via + # -r requirements.in + # sqlalchemy-utils +cssselect2==0.7.0 + # via + # cairosvg + # weasyprint +defusedxml==0.7.1 + # via + # cairosvg + # odfpy +ereuse-utils[cli,naming,session,test]==0.4.0b50 + # via + # -r requirements.in + # teal +et-xmlfile==1.1.0 + # via + # -r requirements.in + # openpyxl +flask==1.0.2 + # via + # ereuse-utils + # flask-cors + # flask-login + # flask-sqlalchemy + # flask-weasyprint + # flask-wtf + # teal +flask-cors==3.0.10 + # via + # -r requirements.in + # teal +flask-login==0.5.0 + # via -r requirements.in +flask-sqlalchemy==2.5.1 + # via teal +flask-weasyprint==0.4 + # via -r requirements.in +flask-wtf==1.0.0 + # via -r requirements.in hashids==1.2.0 + # via -r requirements.in +html5lib==1.1 + # via weasyprint +idna==3.4 + # via requests inflection==0.3.1 + # via ereuse-utils itsdangerous==2.0.1 -# lock Jinja2 version because it's the latest compatible with Flask 1.0.X -# see related info on https://github.com/pallets/jinja/issues/1628 -Jinja2==3.0.3 -marshmallow==3.0.0b11 + # via + # flask + # flask-wtf +jinja2==3.0.3 + # via flask +mako==1.2.3 + # via alembic +markupsafe==2.1.1 + # via + # jinja2 + # mako + # wtforms +marshmallow==3.18.0 + # via + # marshmallow-enum + # teal + # webargs marshmallow-enum==1.4.1 -passlib==1.7.1 -phonenumbers==8.9.11 -pytest==3.7.2 -pytest-runner==4.2 -python-dateutil==2.7.3 -python-stdnum==1.9 -PyYAML==5.4 -requests[security]==2.27.1 -requests-mock==1.5.2 -SQLAlchemy==1.3.24 -SQLAlchemy-Utils==0.33.11 -teal==0.2.0a38 -webargs==5.5.3 -Werkzeug==0.15.5 -sqlalchemy-citext==1.3.post0 -flask-weasyprint==0.5 -weasyprint==44 -psycopg2-binary==2.8.3 -sortedcontainers==2.1.0 -tqdm==4.32.2 -python-decouple==3.3 -python-dotenv==0.14.0 -pyjwt==2.4.0 -pint==0.9 -py-dmidecode==0.1.0 + # via teal +more-itertools==8.12.0 + # via -r requirements.in +numpy==1.22.0 + # via + # -r requirements.in + # pandas +odfpy==1.4.1 + # via -r requirements.in +openpyxl==3.0.10 + # via -r requirements.in +packaging==21.3 + # via marshmallow pandas==1.3.5 -numpy==1.22.0 # pandas dependency -odfpy==1.4.1 # pandas dependency -xlrd==2.0.1 # pandas dependency -openpyxl==3.0.10 # pandas dependency -et_xmlfile==1.1.0 # pandas dependency + # via -r requirements.in +passlib==1.7.1 + # via + # -r requirements.in + # sqlalchemy-utils +phonenumbers==8.9.11 + # via + # -r requirements.in + # sqlalchemy-utils +pillow==9.2.0 + # via cairosvg +pint==0.9 + # via -r requirements.in +psycopg2-binary==2.8.3 + # via -r requirements.in +py-dmidecode==0.1.0 + # via -r requirements.in +pycparser==2.21 + # via cffi +pyjwt==2.4.0 + # via -r requirements.in +pyparsing==3.0.9 + # via packaging +pyphen==0.13.0 + # via weasyprint +python-dateutil==2.7.3 + # via + # alembic + # pandas +python-decouple==3.3 + # via -r requirements.in +python-dotenv==0.14.0 + # via -r requirements.in +python-editor==1.0.4 + # via alembic +python-stdnum==1.9 + # via -r requirements.in +pytz==2022.2.1 + # via pandas +pyyaml==5.4 + # via + # -r requirements.in + # apispec +requests==2.27.1 + # via + # -r requirements.in + # requests-mock + # requests-toolbelt +requests-mock==1.5.2 + # via -r requirements.in +requests-toolbelt==0.9.1 + # via + # -r requirements.in + # ereuse-utils +six==1.16.0 + # via + # anytree + # flask-cors + # html5lib + # python-dateutil + # requests-mock + # sqlalchemy-utils +sortedcontainers==2.1.0 + # via -r requirements.in +sqlalchemy==1.3.24 + # via + # alembic + # flask-sqlalchemy + # sqlalchemy-citext + # sqlalchemy-utils +sqlalchemy-citext==1.3.post0 + # via -r requirements.in +sqlalchemy-utils[color,password,phone]==0.33.11 + # via + # -r requirements.in + # teal +teal==0.2.0a40 + # via -r requirements.in +tinycss2==1.1.1 + # via + # cairosvg + # cssselect2 + # weasyprint +tqdm==4.32.2 + # via + # -r requirements.in + # ereuse-utils +urllib3==1.26.12 + # via requests +weasyprint==44 + # via flask-weasyprint +webargs==5.5.3 + # via teal +webencodings==0.5.1 + # via + # cssselect2 + # html5lib + # tinycss2 +werkzeug==2.0.3 + # via + # flask + # teal +wtforms==3.0.1 + # via flask-wtf +xlrd==2.0.1 + # via -r requirements.in From 51c42ad94f7b0cb5fc42fc99b987474136e698e8 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 12:44:40 +0200 Subject: [PATCH 05/62] Lock marshmallow version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d482f549..3fe7963d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -108,7 +108,7 @@ markupsafe==2.1.1 # jinja2 # mako # wtforms -marshmallow==3.18.0 +marshmallow==3.0.0 # via # marshmallow-enum # teal From abb675d9d32fd9cf21537ea886c3aedda90e0628 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 12:55:11 +0200 Subject: [PATCH 06/62] Lock --- requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3fe7963d..e4ea1e1e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -108,7 +108,9 @@ markupsafe==2.1.1 # jinja2 # mako # wtforms -marshmallow==3.0.0 +# TODO fix https://github.com/marshmallow-code/marshmallow/issues/1040 +# A ValueError is raised when the missing parameter is passed for required fields (#1040). +marshmallow==3.0.0rc1 # via # marshmallow-enum # teal From cffa950fec154d0d35c629a35947c125fe8146d3 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 13:24:40 +0200 Subject: [PATCH 07/62] Bump to teal 0.2.0a41 which fixes marshmallow version --- requirements.in | 2 +- requirements.txt | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/requirements.in b/requirements.in index 8a9569ca..b76366ef 100644 --- a/requirements.in +++ b/requirements.in @@ -24,7 +24,7 @@ requests-toolbelt==0.9.1 sortedcontainers==2.1.0 sqlalchemy-citext==1.3.post0 sqlalchemy-utils==0.33.11 -teal==0.2.0a40 +teal==0.2.0a41 tqdm==4.32.2 pint==0.9 diff --git a/requirements.txt b/requirements.txt index e4ea1e1e..d0c85640 100644 --- a/requirements.txt +++ b/requirements.txt @@ -108,9 +108,7 @@ markupsafe==2.1.1 # jinja2 # mako # wtforms -# TODO fix https://github.com/marshmallow-code/marshmallow/issues/1040 -# A ValueError is raised when the missing parameter is passed for required fields (#1040). -marshmallow==3.0.0rc1 +marshmallow==3.0.0b11 # via # marshmallow-enum # teal @@ -127,8 +125,6 @@ odfpy==1.4.1 # via -r requirements.in openpyxl==3.0.10 # via -r requirements.in -packaging==21.3 - # via marshmallow pandas==1.3.5 # via -r requirements.in passlib==1.7.1 @@ -151,8 +147,6 @@ pycparser==2.21 # via cffi pyjwt==2.4.0 # via -r requirements.in -pyparsing==3.0.9 - # via packaging pyphen==0.13.0 # via weasyprint python-dateutil==2.7.3 @@ -206,7 +200,7 @@ sqlalchemy-utils[color,password,phone]==0.33.11 # via # -r requirements.in # teal -teal==0.2.0a40 +teal==0.2.0a41 # via -r requirements.in tinycss2==1.1.1 # via From d0c91d5d24864970cad04e5169c321ef7c1a8861 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 13:45:27 +0200 Subject: [PATCH 08/62] Drop apispec-webframeworks requirement --- requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index d0c85640..407b789a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,12 +8,10 @@ alembic==1.4.2 # via -r requirements.in anytree==2.4.3 # via teal -apispec[yaml]==5.2.2 +apispec[yaml]==0.39.0 # via # apispec-webframeworks # teal -apispec-webframeworks==0.5.2 - # via teal atomicwrites==1.4.0 # via -r requirements.in boltons==18.0.1 From fe5d5408fe6709ce44f6b09ee4533b4fc5849eb5 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 13:49:50 +0200 Subject: [PATCH 09/62] . --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 407b789a..61dc98fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ alembic==1.4.2 # via -r requirements.in anytree==2.4.3 # via teal -apispec[yaml]==0.39.0 +apispec==0.39.0 # via # apispec-webframeworks # teal From 8a49e63592a1349ccfdb4a7068ffdc32dc75e2c6 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 14:00:11 +0200 Subject: [PATCH 10/62] Run selenium only on reviews --- .github/workflows/selenium.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index fab0faab..1408a3d3 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -1,10 +1,8 @@ name: Selenium on: - push: - branches: [master, testing] pull_request: - branches: [master, testing] + types: [ready_for_review, review_requested] jobs: build: From bcec57e752c3ba3973b9e81e61af9549b14d352d Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 14:00:48 +0200 Subject: [PATCH 11/62] Fix apispec dependencies --- requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 61dc98fa..bba8c5a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,10 +8,12 @@ alembic==1.4.2 # via -r requirements.in anytree==2.4.3 # via teal -apispec==0.39.0 +apispec==4.5.0 # via # apispec-webframeworks # teal +apispec-webframeworks==0.5.2 + # via teal atomicwrites==1.4.0 # via -r requirements.in boltons==18.0.1 From d9ba3889c006a1ed95b4c3f9ff4b30fb994c95cd Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 16:47:27 +0200 Subject: [PATCH 12/62] Revert teal version to 0.2.0a38 There are code not compatible with newer libraries --- requirements.in | 5 ++++- requirements.txt | 14 ++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/requirements.in b/requirements.in index b76366ef..11ce696b 100644 --- a/requirements.in +++ b/requirements.in @@ -24,7 +24,7 @@ requests-toolbelt==0.9.1 sortedcontainers==2.1.0 sqlalchemy-citext==1.3.post0 sqlalchemy-utils==0.33.11 -teal==0.2.0a41 +teal==0.2.0a38 tqdm==4.32.2 pint==0.9 @@ -35,3 +35,6 @@ odfpy==1.4.1 # pandas dependency xlrd==2.0.1 # pandas dependency openpyxl==3.0.10 # pandas dependency et_xmlfile==1.1.0 # pandas dependency + +# manual dependency +marshmallow-enum==1.4.1 diff --git a/requirements.txt b/requirements.txt index bba8c5a8..d698c4c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,11 +8,7 @@ alembic==1.4.2 # via -r requirements.in anytree==2.4.3 # via teal -apispec==4.5.0 - # via - # apispec-webframeworks - # teal -apispec-webframeworks==0.5.2 +apispec==0.39.0 # via teal atomicwrites==1.4.0 # via -r requirements.in @@ -114,7 +110,7 @@ marshmallow==3.0.0b11 # teal # webargs marshmallow-enum==1.4.1 - # via teal + # via -r requirements.in more-itertools==8.12.0 # via -r requirements.in numpy==1.22.0 @@ -200,7 +196,7 @@ sqlalchemy-utils[color,password,phone]==0.33.11 # via # -r requirements.in # teal -teal==0.2.0a41 +teal==0.2.0a38 # via -r requirements.in tinycss2==1.1.1 # via @@ -223,9 +219,7 @@ webencodings==0.5.1 # html5lib # tinycss2 werkzeug==2.0.3 - # via - # flask - # teal + # via flask wtforms==3.0.1 # via flask-wtf xlrd==2.0.1 From 814f36d4eeafdbcec325d1a68582b361c3c377ea Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 17:08:40 +0200 Subject: [PATCH 13/62] Drop "message" parameter of pytest.raises Deprecated since version 4.1 https://docs.pytest.org/en/4.6.x/deprecations.html#raises-message-deprecated --- tests/test_action.py | 23 ++++++++-------- tests/test_auth.py | 18 ++++++++++--- tests/test_inventory.py | 59 ++++++++++++++++++++++++++++------------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/tests/test_action.py b/tests/test_action.py index 14b9d697..bc27065b 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -1,5 +1,4 @@ import copy -import ipaddress import json import os import shutil @@ -7,7 +6,7 @@ from datetime import datetime, timedelta from decimal import Decimal from io import BytesIO from json.decoder import JSONDecodeError -from typing import Tuple, Type +from typing import Tuple import pytest from dateutil.tz import tzutc @@ -15,7 +14,7 @@ from flask import current_app as app from flask import g from pytest import raises from sqlalchemy.util import OrderedSet -from teal.enums import Currency, Subdivision +from teal.enums import Currency from ereuse_devicehub.client import Client, UserClient from ereuse_devicehub.db import db @@ -82,11 +81,7 @@ def test_erase_basic(): def test_validate_device_data_storage(): """Checks the validation for data-storage-only actions works.""" # We can't set a GraphicCard - with pytest.raises( - TypeError, - message='EraseBasic.device must be a DataStorage ' - 'but you passed ', - ): + with pytest.raises(TypeError): models.EraseBasic( device=GraphicCard( serial_number='foo', manufacturer='bar', model='foo-bar' @@ -94,6 +89,10 @@ def test_validate_device_data_storage(): clean_with_zeros=True, **conftest.T, ) + pytest.fail( + 'EraseBasic.device must be a DataStorage ' + 'but you passed ' + ) @pytest.mark.mvp @@ -292,9 +291,7 @@ def test_generic_action( for ams in [models.Recycling, models.Use, models.Refurbish, models.Management] ), ) -def test_simple_status_actions( - action_model: models.Action, user2: UserClient -): +def test_simple_status_actions(action_model: models.Action, user2: UserClient): """Simple test of status action.""" user = user2 snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) @@ -554,7 +551,9 @@ def test_status_without_lot(action_model: models.Action, user: UserClient): for ams in [models.Recycling, models.Use, models.Refurbish, models.Management] ), ) -def test_status_in_temporary_lot(action_model: models.Action, user: UserClient, app: Devicehub): +def test_status_in_temporary_lot( + action_model: models.Action, user: UserClient, app: Devicehub +): """Test of status actions for devices in a temporary lot.""" snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) abstract = Device.query.filter_by(id=snap['device']['id']).first() diff --git a/tests/test_auth.py b/tests/test_auth.py index 81cefb0e..54c70a3e 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -21,14 +21,22 @@ def test_authenticate_success(app: Devicehub): def test_authenticate_error(app: Devicehub): """Tests the authenticate method with wrong token values.""" with app.app_context(): - MESSAGE = 'Provide a suitable token.' create_user() # Token doesn't exist - with pytest.raises(Unauthorized, message=MESSAGE): + with pytest.raises(Unauthorized): app.auth.authenticate(token=str(uuid4())) + pytest.fail('Provide a suitable token.') + + +@pytest.mark.mvp +def test_authenticate_error_malformed_token(app: Devicehub): + """Tests the authenticate method with malformed token.""" + with app.app_context(): + create_user() # Wrong token format - with pytest.raises(Unauthorized, message=MESSAGE): + with pytest.raises(Unauthorized): app.auth.authenticate(token='this is a wrong uuid') + pytest.fail('Provide a suitable token.') @pytest.mark.mvp @@ -36,4 +44,6 @@ def test_auth_view(user: UserClient, client: Client): """Tests authentication at endpoint / view.""" user.get(res='User', item=user.user['id'], status=200) client.get(res='User', item=user.user['id'], status=Unauthorized) - client.get(res='User', item=user.user['id'], token='wrong token', status=Unauthorized) + client.get( + res='User', item=user.user['id'], token='wrong token', status=Unauthorized + ) diff --git a/tests/test_inventory.py b/tests/test_inventory.py index 70e4dfb7..8d939733 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -21,10 +21,12 @@ from tests.conftest import TestConfig class NoExcCliRunner(click.testing.CliRunner): """Runner that interfaces with the Devicehub CLI.""" - def invoke(self, *args, input=None, env=None, catch_exceptions=False, color=False, - **extra): - r = super().invoke(ereuse_devicehub.cli.cli, - args, input, env, catch_exceptions, color, **extra) + def invoke( + self, *args, input=None, env=None, catch_exceptions=False, color=False, **extra + ): + r = super().invoke( + ereuse_devicehub.cli.cli, args, input, env, catch_exceptions, color, **extra + ) assert r.exit_code == 0, 'CLI code {}: {}'.format(r.exit_code, r.output) return r @@ -69,16 +71,26 @@ def test_inventory_create_delete_user(cli, tdb1, tdb2): """ # Create first DB cli.inv('tdb1') - cli.invoke('inv', 'add', - '-n', 'Test DB1', - '-on', 'ACME DB1', - '-oi', 'acme-id', - '-tu', 'https://example.com', - '-tt', '3c66a6ad-22de-4db6-ac46-d8982522ec40', - '--common') + cli.invoke( + 'inv', + 'add', + '-n', + 'Test DB1', + '-on', + 'ACME DB1', + '-oi', + 'acme-id', + '-tu', + 'https://example.com', + '-tt', + '3c66a6ad-22de-4db6-ac46-d8982522ec40', + '--common', + ) # Create an user for first DB - cli.invoke('user', 'add', 'foo@foo.com', '-a', 'Foo', '-c', 'ES', '-p', 'Such password') + cli.invoke( + 'user', 'add', 'foo@foo.com', '-a', 'Foo', '-c', 'ES', '-p', 'Such password' + ) with tdb1.app_context(): # There is a row for the inventory @@ -98,12 +110,20 @@ def test_inventory_create_delete_user(cli, tdb1, tdb2): cli.inv('tdb2') # Create a second DB # Note how we don't create common anymore - cli.invoke('inv', 'add', - '-n', 'Test DB2', - '-on', 'ACME DB2', - '-oi', 'acme-id-2', - '-tu', 'https://example.com', - '-tt', 'fbad1c08-ffdc-4a61-be49-464962c186a8') + cli.invoke( + 'inv', + 'add', + '-n', + 'Test DB2', + '-on', + 'ACME DB2', + '-oi', + 'acme-id-2', + '-tu', + 'https://example.com', + '-tt', + 'fbad1c08-ffdc-4a61-be49-464962c186a8', + ) # Create an user for with access for both DB cli.invoke('user', 'add', 'bar@bar.com', '-a', 'Bar', '-p', 'Wow password') @@ -144,5 +164,6 @@ def test_create_existing_inventory(cli, tdb1): cli.invoke('inv', 'add', '--common') with tdb1.app_context(): assert db.has_schema('tdb1') - with pytest.raises(AssertionError, message='Schema tdb1 already exists.'): + with pytest.raises(AssertionError): cli.invoke('inv', 'add', '--common') + pytest.fail('Schema tdb1 already exists.') From bf423ab51546a9ce1c32d08159d74990c1ef0ea7 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 17:12:18 +0200 Subject: [PATCH 14/62] Fix flake F841 variable assigned never used --- tests/test_action.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/test_action.py b/tests/test_action.py index bc27065b..fe5b67e9 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -726,7 +726,7 @@ def test_live_without_TestDataStorage(user: UserClient, client: Client, app: Dev acer = file('acer.happy.battery.snapshot') snapshot, _ = user.post(acer, res=models.Snapshot) device_id = snapshot['device']['id'] - db_device = Device.query.filter_by(id=device_id).one() + post_request = { "transaction": "ccc", "name": "John", @@ -766,7 +766,7 @@ def test_live_without_hdd_1(user: UserClient, client: Client, app: Devicehub): acer = file('acer.happy.battery.snapshot') snapshot, _ = user.post(acer, res=models.Snapshot) device_id = snapshot['device']['id'] - db_device = Device.query.filter_by(id=device_id).one() + post_request = { "transaction": "ccc", "name": "John", @@ -802,7 +802,7 @@ def test_live_without_hdd_2(user: UserClient, client: Client, app: Devicehub): acer['components'] = components snapshot, _ = user.post(json_encode(acer), res=models.Snapshot) device_id = snapshot['device']['id'] - db_device = Device.query.filter_by(id=device_id).one() + post_request = { "transaction": "ccc", "name": "John", @@ -837,7 +837,7 @@ def test_live_without_hdd_3(user: UserClient, client: Client, app: Devicehub): acer['components'] = components snapshot, _ = user.post(json_encode(acer), res=models.Snapshot) device_id = snapshot['device']['id'] - db_device = Device.query.filter_by(id=device_id).one() + post_request = { "transaction": "ccc", "name": "John", @@ -874,7 +874,7 @@ def test_live_with_hdd_with_old_time(user: UserClient, client: Client, app: Devi acer = file('acer.happy.battery.snapshot') snapshot, _ = user.post(acer, res=models.Snapshot) device_id = snapshot['device']['id'] - db_device = Device.query.filter_by(id=device_id).one() + post_request = { "transaction": "ccc", "name": "John", @@ -2952,7 +2952,6 @@ def test_delete_devices_permitions(user: UserClient, user2: UserClient): file_snap = file('1-device-with-components.snapshot') snap, _ = user.post(file_snap, res=models.Snapshot) - device = Device.query.filter_by(id=snap['device']['id']).one() request = { 'type': 'Delete', @@ -3013,8 +3012,7 @@ def test_moveOnDocument_bug168(user: UserClient, user2: UserClient): 'container_to': id_hash, 'description': description, } - doc, _ = user.post(res=models.Action, data=request_moveOn) - trade = models.Trade.query.one() + user.post(res=models.Action, data=request_moveOn) trade_document1 = TradeDocument.query.filter_by(id=tradedocument_from['id']).one() trade_document2 = TradeDocument.query.filter_by(id=tradedocument_to['id']).one() assert trade_document1.total_weight == 150.0 From 5b9928699448f865b4466719479c584685db223c Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 17:15:40 +0200 Subject: [PATCH 15/62] E712 comparison to True should be 'if cond is True:' or 'if cond:' --- tests/test_action.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_action.py b/tests/test_action.py index fe5b67e9..8427bde1 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -1021,7 +1021,7 @@ def test_allocate(user: UserClient): allocate, _ = user.post(res=models.Allocate, data=post_request) # Normal allocate device, _ = user.get(res=Device, item=devicehub_id) - assert device['allocated'] == True + assert device['allocated'] is True action = [a for a in device['actions'] if a['type'] == 'Allocate'][0] assert action['transaction'] == allocate['transaction'] assert action['finalUserCode'] == allocate['finalUserCode'] @@ -1096,11 +1096,11 @@ def test_deallocate(user: UserClient): user.post(res=models.Allocate, data=post_allocate) device, _ = user.get(res=Device, item=devicehub_id) - assert device['allocated'] == True + assert device['allocated'] is True deallocate, _ = user.post(res=models.Deallocate, data=post_deallocate) assert deallocate['startTime'] == post_deallocate['startTime'] assert deallocate['devices'][0]['id'] == device_id - assert deallocate['devices'][0]['allocated'] == False + assert deallocate['devices'][0]['allocated'] is False res, _ = user.post(res=models.Deallocate, data=post_deallocate, status=422) assert res['code'] == 422 assert res['type'] == 'ValidationError' @@ -1203,8 +1203,8 @@ def test_offer_without_to(user: UserClient): users = [ac.user for ac in trade.acceptances] assert trade.user_to == device.owner assert request_post['code'].lower() in device.owner.email - assert device.owner.active == False - assert device.owner.phantom == True + assert device.owner.active is False + assert device.owner.phantom is True assert trade.user_to in users assert trade.user_from in users assert device.owner.email != user.email @@ -1282,8 +1282,8 @@ def test_offer_without_from(user: UserClient, user2: UserClient): phantom_user = trade.user_from assert request_post['code'].lower() in phantom_user.email - assert phantom_user.active == False - assert phantom_user.phantom == True + assert phantom_user.active is False + assert phantom_user.phantom is True # assert trade.confirm_transfer users = [ac.user for ac in trade.acceptances] @@ -2886,7 +2886,7 @@ def test_delete_devices(user: UserClient): assert action_delete.t == 'Delete' assert str(action_delete.id) == action['id'] - assert db_device.active == False + assert db_device.active is False # Check use of filter from frontend url = '/devices/?filter={"type":["Computer"]}' From c232320d6284be4d75fc26c388af16aecd4c99ae Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 17:18:10 +0200 Subject: [PATCH 16/62] Fix flake8: E713, E501 --- tests/test_action.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_action.py b/tests/test_action.py index 8427bde1..1a925db1 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -1777,12 +1777,12 @@ def test_confirmRevoke(user: UserClient, user2: UserClient): assert device_10 in trade.devices assert len(trade.devices) == 10 - # the SCRAP confirms the revoke action - request_confirm_revoke = { - 'type': 'ConfirmRevoke', - 'action': device_10.actions[-2].id, - 'devices': [snap10['device']['id']], - } + # TODO??? the SCRAP confirms the revoke action + # request_confirm_revoke = { + # 'type': 'ConfirmRevoke', + # 'action': device_10.actions[-2].id, + # 'devices': [snap10['device']['id']], + # } # check validation error # user2.post(res=models.Action, data=request_confirm_revoke, status=422) @@ -2804,7 +2804,7 @@ def test_moveOnDocument(user: UserClient, user2: UserClient): lotIn, _ = user.post({'name': 'MyLotIn'}, res=Lot) lotOut, _ = user.post({'name': 'MyLotOut'}, res=Lot) url = ( - 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa', + 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaap', ) request_post1 = { 'filename': 'test.pdf', @@ -2971,7 +2971,7 @@ def test_moveOnDocument_bug168(user: UserClient, user2: UserClient): lotIn, _ = user.post({'name': 'MyLotIn'}, res=Lot) lotOut, _ = user.post({'name': 'MyLotOut'}, res=Lot) url = ( - 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa', + 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaap', ) request_post1 = { 'filename': 'test.pdf', From 7bae66e04196056ae13f3f9178403a545a83f307 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 26 Sep 2022 17:19:14 +0200 Subject: [PATCH 17/62] Fix E711, E713 --- tests/test_action.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_action.py b/tests/test_action.py index 1a925db1..40e9cd15 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -2795,7 +2795,7 @@ def test_action_web_erase(user: UserClient, client: Client): ) assert "alert alert-info" in response assert "100% coincidence." in response - assert not "alert alert-danger" in response + assert "alert alert-danger" not in response @pytest.mark.mvp @@ -3018,6 +3018,6 @@ def test_moveOnDocument_bug168(user: UserClient, user2: UserClient): assert trade_document1.total_weight == 150.0 assert trade_document2.total_weight == 4.0 assert trade_document1.trading == 'Confirm' - assert trade_document2.trading == None + assert trade_document2.trading is None tradedocument, _ = user.delete(res=TradeDocument, item=tradedocument_to['id']) From 332c1088c0d3587d3cf14e38a54281b189b7872b Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 29 Sep 2022 11:42:50 +0200 Subject: [PATCH 18/62] Fix can't compare offset-naive and offset-aware datetimes --- tests/test_device.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_device.py b/tests/test_device.py index f422c653..e2b1f078 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -1,6 +1,7 @@ import copy import datetime import pytest +import pytz from uuid import UUID from flask import g @@ -570,7 +571,7 @@ def test_manufacturer(user: UserClient): m, r = user.get(res='Manufacturer', query=[('search', 'asus')]) assert m == {'items': [{'name': 'Asus', 'url': 'https://en.wikipedia.org/wiki/Asus'}]} assert r.cache_control.public - assert r.expires > datetime.datetime.now() + assert r.expires.timestamp() > datetime.datetime.now().timestamp() @pytest.mark.mvp From 72457088078a0468bd01106e499bcb75be946d03 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 29 Sep 2022 11:44:43 +0200 Subject: [PATCH 19/62] Fix flake8 & reformat using black --- tests/test_device.py | 351 ++++++++++++++++++++++++++++--------------- 1 file changed, 230 insertions(+), 121 deletions(-) diff --git a/tests/test_device.py b/tests/test_device.py index e2b1f078..ac8c1d07 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -1,14 +1,12 @@ import copy import datetime -import pytest -import pytz - from uuid import UUID -from flask import g +import pytest from colour import Color from ereuse_utils.naming import Naming from ereuse_utils.test import ANY +from flask import g from pytest import raises from sqlalchemy.util import OrderedSet from teal.db import ResourceNotFound @@ -21,26 +19,35 @@ from ereuse_devicehub.resources.action import models as m from ereuse_devicehub.resources.action.models import Remove, TestConnectivity from ereuse_devicehub.resources.agent.models import Person from ereuse_devicehub.resources.device import models as d -from ereuse_devicehub.resources.device.exceptions import NeedsId from ereuse_devicehub.resources.device.schemas import Device as DeviceS -from ereuse_devicehub.resources.device.sync import MismatchBetweenTags, MismatchBetweenTagsAndHid, \ - Sync -from ereuse_devicehub.resources.enums import ComputerChassis, DisplayTech, Severity, \ - SnapshotSoftware, TransferState +from ereuse_devicehub.resources.device.sync import ( + MismatchBetweenTags, + MismatchBetweenTagsAndHid, + Sync, +) +from ereuse_devicehub.resources.enums import ( + ComputerChassis, + DisplayTech, + Severity, + SnapshotSoftware, + TransferState, +) from ereuse_devicehub.resources.tag.model import Tag from ereuse_devicehub.resources.user import User from tests import conftest -from tests.conftest import file, yaml2json, json_encode +from tests.conftest import file, json_encode, yaml2json @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_device_model(): """Tests that the correctness of the device model and its relationships.""" - pc = d.Desktop(model='p1mo', - manufacturer='p1ma', - serial_number='p1s', - chassis=ComputerChassis.Tower) + pc = d.Desktop( + model='p1mo', + manufacturer='p1ma', + serial_number='p1s', + chassis=ComputerChassis.Tower, + ) net = d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s') graphic = d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500) pc.components.add(net) @@ -56,7 +63,9 @@ def test_device_model(): # Removing a component from pc doesn't delete the component pc.components.remove(net) db.session.commit() - pc = d.Device.query.filter_by(id=pc.id).first() # this is the same as querying for d.Desktop directly + pc = d.Device.query.filter_by( + id=pc.id + ).first() # this is the same as querying for d.Desktop directly assert pc.components == {graphic} network_adapter = d.NetworkAdapter.query.one() assert network_adapter not in pc.components @@ -73,7 +82,9 @@ def test_device_model(): assert network_adapter.id == 4 assert d.NetworkAdapter.query.first() is not None, 'We removed the network adaptor' assert gcard.id == 5, 'We should still hold a reference to a zombie graphic card' - assert d.GraphicCard.query.first() is None, 'We should have deleted it –it was inside the pc' + assert ( + d.GraphicCard.query.first() is None + ), 'We should have deleted it –it was inside the pc' @pytest.mark.xfail(reason='Test not developed') @@ -93,21 +104,25 @@ def test_device_schema(): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_physical_properties(): - c = d.Motherboard(slots=2, - usb=3, - serial_number='sn', - model='ml', - manufacturer='mr', - width=2.0, - color=Color()) - pc = d.Desktop(chassis=ComputerChassis.Tower, - model='foo', - manufacturer='bar', - serial_number='foo-bar', - weight=2.8, - width=1.4, - height=2.1, - color=Color('LightSeaGreen')) + c = d.Motherboard( + slots=2, + usb=3, + serial_number='sn', + model='ml', + manufacturer='mr', + width=2.0, + color=Color(), + ) + pc = d.Desktop( + chassis=ComputerChassis.Tower, + model='foo', + manufacturer='bar', + serial_number='foo-bar', + weight=2.8, + width=1.4, + height=2.1, + color=Color('LightSeaGreen'), + ) pc.components.add(c) db.session.add(pc) db.session.commit() @@ -123,7 +138,7 @@ def test_physical_properties(): 'manufacturer': 'mr', 'bios_date': None, 'ram_max_size': None, - 'ram_slots': None + 'ram_slots': None, } assert pc.physical_properties == { 'chassis': ComputerChassis.Tower, @@ -133,7 +148,7 @@ def test_physical_properties(): 'receiver_id': None, 'serial_number': 'foo-bar', 'part_number': None, - 'transfer_state': TransferState.Initial + 'transfer_state': TransferState.Initial, } @@ -143,14 +158,19 @@ def test_component_similar_one(): user = User.query.filter().first() snapshot = yaml2json('pc-components.db') pc = snapshot['device'] - snapshot['components'][0]['serial_number'] = snapshot['components'][1]['serial_number'] = None - pc = d.Desktop(**pc, components=OrderedSet(d.Component(**c) for c in snapshot['components'])) + snapshot['components'][0]['serial_number'] = snapshot['components'][1][ + 'serial_number' + ] = None + pc = d.Desktop( + **pc, components=OrderedSet(d.Component(**c) for c in snapshot['components']) + ) component1, component2 = pc.components # type: d.Component db.session.add(pc) db.session.flush() # Let's create a new component named 'A' similar to 1 - componentA = d.Component(model=component1.model, manufacturer=component1.manufacturer, - owner_id=user.id) + componentA = d.Component( + model=component1.model, manufacturer=component1.manufacturer, owner_id=user.id + ) similar_to_a = componentA.similar_one(pc, set()) assert similar_to_a == component1 # d.Component B does not have the same model @@ -176,9 +196,11 @@ def test_add_remove(): pc = d.Desktop(**pc, components=OrderedSet([c1, c2])) db.session.add(pc) c3 = d.Component(serial_number='nc1', owner_id=user.id) - pc2 = d.Desktop(serial_number='s2', - components=OrderedSet([c3]), - chassis=ComputerChassis.Microtower) + pc2 = d.Desktop( + serial_number='s2', + components=OrderedSet([c3]), + chassis=ComputerChassis.Microtower, + ) c4 = d.Component(serial_number='c4s', owner_id=user.id) db.session.add(pc2) db.session.add(c4) @@ -202,7 +224,9 @@ def test_sync_run_components_empty(): remove all the components from the device. """ s = yaml2json('pc-components.db') - pc = d.Desktop(**s['device'], components=OrderedSet(d.Component(**c) for c in s['components'])) + pc = d.Desktop( + **s['device'], components=OrderedSet(d.Component(**c) for c in s['components']) + ) db.session.add(pc) db.session.commit() @@ -220,7 +244,9 @@ def test_sync_run_components_none(): keep all the components from the device. """ s = yaml2json('pc-components.db') - pc = d.Desktop(**s['device'], components=OrderedSet(d.Component(**c) for c in s['components'])) + pc = d.Desktop( + **s['device'], components=OrderedSet(d.Component(**c) for c in s['components']) + ) db.session.add(pc) db.session.commit() @@ -250,7 +276,8 @@ def test_sync_execute_register_desktop_existing_no_tag(): db.session.commit() pc = d.Desktop( - **yaml2json('pc-components.db')['device']) # Create a new transient non-db object + **yaml2json('pc-components.db')['device'] + ) # Create a new transient non-db object # 1: device exists on DB db_pc = Sync().execute_register(pc) pc.amount = 0 @@ -286,7 +313,9 @@ def test_sync_execute_register_desktop_tag_not_linked(): db.session.commit() # Create a new transient non-db object - pc = d.Desktop(**yaml2json('pc-components.db')['device'], tags=OrderedSet([Tag(id='foo')])) + pc = d.Desktop( + **yaml2json('pc-components.db')['device'], tags=OrderedSet([Tag(id='foo')]) + ) returned_pc = Sync().execute_register(pc) assert returned_pc == pc assert tag.device == pc, 'Tag has to be linked' @@ -327,7 +356,9 @@ def test_sync_execute_register_tag_does_not_exist(): Tags have to be created before trying to link them through a Snapshot. """ user = User.query.filter().first() - pc = d.Desktop(**yaml2json('pc-components.db')['device'], tags=OrderedSet([Tag('foo')])) + pc = d.Desktop( + **yaml2json('pc-components.db')['device'], tags=OrderedSet([Tag('foo')]) + ) pc.owner_id = user.id with raises(ResourceNotFound): Sync().execute_register(pc) @@ -346,7 +377,8 @@ def test_sync_execute_register_tag_linked_same_device(): db.session.commit() pc = d.Desktop( - **yaml2json('pc-components.db')['device']) # Create a new transient non-db object + **yaml2json('pc-components.db')['device'] + ) # Create a new transient non-db object pc.tags.add(Tag(id='foo')) db_pc = Sync().execute_register(pc) assert db_pc.id == orig_pc.id @@ -370,7 +402,8 @@ def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags(): db.session.commit() pc1 = d.Desktop( - **yaml2json('pc-components.db')['device']) # Create a new transient non-db object + **yaml2json('pc-components.db')['device'] + ) # Create a new transient non-db object pc1.tags.add(Tag(id='foo-1')) pc1.tags.add(Tag(id='foo-2')) with raises(MismatchBetweenTags): @@ -394,7 +427,8 @@ def test_sync_execute_register_mismatch_between_tags_and_hid(): db.session.commit() pc1 = d.Desktop( - **yaml2json('pc-components.db')['device']) # Create a new transient non-db object + **yaml2json('pc-components.db')['device'] + ) # Create a new transient non-db object pc1.tags.add(Tag(id='foo-2')) with raises(MismatchBetweenTagsAndHid): Sync().execute_register(pc1) @@ -405,22 +439,36 @@ def test_sync_execute_register_mismatch_between_tags_and_hid(): def test_get_device(user: UserClient): """Checks GETting a d.Desktop with its components.""" g.user = User.query.one() - pc = d.Desktop(model='p1mo', - manufacturer='p1ma', - serial_number='p1s', - chassis=ComputerChassis.Tower, - owner_id=user.user['id']) - pc.components = OrderedSet([ - d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s', - owner_id=user.user['id']), - d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500, owner_id=user.user['id']) - ]) + pc = d.Desktop( + model='p1mo', + manufacturer='p1ma', + serial_number='p1s', + chassis=ComputerChassis.Tower, + owner_id=user.user['id'], + ) + pc.components = OrderedSet( + [ + d.NetworkAdapter( + model='c1mo', + manufacturer='c1ma', + serial_number='c1s', + owner_id=user.user['id'], + ), + d.GraphicCard( + model='c2mo', manufacturer='c2ma', memory=1500, owner_id=user.user['id'] + ), + ] + ) db.session.add(pc) # todo test is an abstract class. replace with another one - db.session.add(TestConnectivity(device=pc, - severity=Severity.Info, - agent=Person(name='Timmy'), - author=User(email='bar@bar.com'))) + db.session.add( + TestConnectivity( + device=pc, + severity=Severity.Info, + agent=Person(name='Timmy'), + author=User(email='bar@bar.com'), + ) + ) db.session.commit() pc_api, _ = user.get(res=d.Device, item=pc.devicehub_id) assert len(pc_api['actions']) == 1 @@ -428,10 +476,14 @@ def test_get_device(user: UserClient): assert pc_api['actions'][0]['device'] == pc.id assert pc_api['actions'][0]['severity'] == 'Info' assert UUID(pc_api['actions'][0]['author']) - assert 'actions_components' not in pc_api, 'actions_components are internal use only' + assert ( + 'actions_components' not in pc_api + ), 'actions_components are internal use only' assert 'actions_one' not in pc_api, 'they are internal use only' assert 'author' not in pc_api - assert tuple(c['id'] for c in pc_api['components']) == tuple(c.id for c in pc.components) + assert tuple(c['id'] for c in pc_api['components']) == tuple( + c.id for c in pc.components + ) assert pc_api['hid'] == 'desktop-p1ma-p1mo-p1s' assert pc_api['model'] == 'p1mo' assert pc_api['manufacturer'] == 'p1ma' @@ -444,41 +496,59 @@ def test_get_device(user: UserClient): def test_get_devices(app: Devicehub, user: UserClient): """Checks GETting multiple devices.""" g.user = User.query.one() - pc = d.Desktop(model='p1mo', - manufacturer='p1ma', - serial_number='p1s', - chassis=ComputerChassis.Tower, - owner_id=user.user['id']) - pc.components = OrderedSet([ - d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s', - owner_id=user.user['id']), - d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500, - owner_id=user.user['id']) - ]) - pc1 = d.Desktop(model='p2mo', - manufacturer='p2ma', - serial_number='p2s', - chassis=ComputerChassis.Tower, - owner_id=user.user['id']) - pc2 = d.Laptop(model='p3mo', - manufacturer='p3ma', - serial_number='p3s', - chassis=ComputerChassis.Netbook, - owner_id=user.user['id']) + pc = d.Desktop( + model='p1mo', + manufacturer='p1ma', + serial_number='p1s', + chassis=ComputerChassis.Tower, + owner_id=user.user['id'], + ) + pc.components = OrderedSet( + [ + d.NetworkAdapter( + model='c1mo', + manufacturer='c1ma', + serial_number='c1s', + owner_id=user.user['id'], + ), + d.GraphicCard( + model='c2mo', manufacturer='c2ma', memory=1500, owner_id=user.user['id'] + ), + ] + ) + pc1 = d.Desktop( + model='p2mo', + manufacturer='p2ma', + serial_number='p2s', + chassis=ComputerChassis.Tower, + owner_id=user.user['id'], + ) + pc2 = d.Laptop( + model='p3mo', + manufacturer='p3ma', + serial_number='p3s', + chassis=ComputerChassis.Netbook, + owner_id=user.user['id'], + ) db.session.add_all((pc, pc1, pc2)) db.session.commit() devices, _ = user.get(res=d.Device) ids = (pc.id, pc1.id, pc2.id, pc.components[0].id, pc.components[1].id) assert tuple(dev['id'] for dev in devices['items']) == ids assert tuple(dev['type'] for dev in devices['items']) == ( - d.Desktop.t, d.Desktop.t, d.Laptop.t, d.NetworkAdapter.t, d.GraphicCard.t + d.Desktop.t, + d.Desktop.t, + d.Laptop.t, + d.NetworkAdapter.t, + d.GraphicCard.t, ) @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_get_device_permissions(app: Devicehub, user: UserClient, user2: UserClient, - client: Client): +def test_get_device_permissions( + app: Devicehub, user: UserClient, user2: UserClient, client: Client +): """Checks GETting a d.Desktop with its components.""" s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot) @@ -530,12 +600,12 @@ def test_get_devices_unassigned(user: UserClient): assert len(devices['items']) == 2 from ereuse_devicehub.resources.lot.models import Lot + device_id = devices['items'][0]['id'] my_lot, _ = user.post(({'name': 'My_lot'}), res=Lot) - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(my_lot['id']), - query=[('id', device_id)]) + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(my_lot['id']), query=[('id', device_id)] + ) lot = Lot.query.filter_by(id=lot['id']).one() assert next(iter(lot.devices)).id == device_id @@ -555,13 +625,15 @@ def test_get_devices_unassigned(user: UserClient): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_computer_monitor(): - m = d.ComputerMonitor(technology=DisplayTech.LCD, - manufacturer='foo', - model='bar', - serial_number='foo-bar', - resolution_width=1920, - resolution_height=1080, - size=14.5) + m = d.ComputerMonitor( + technology=DisplayTech.LCD, + manufacturer='foo', + model='bar', + serial_number='foo-bar', + resolution_width=1920, + resolution_height=1080, + size=14.5, + ) db.session.add(m) db.session.commit() @@ -569,7 +641,9 @@ def test_computer_monitor(): @pytest.mark.mvp def test_manufacturer(user: UserClient): m, r = user.get(res='Manufacturer', query=[('search', 'asus')]) - assert m == {'items': [{'name': 'Asus', 'url': 'https://en.wikipedia.org/wiki/Asus'}]} + assert m == { + 'items': [{'name': 'Asus', 'url': 'https://en.wikipedia.org/wiki/Asus'}] + } assert r.cache_control.public assert r.expires.timestamp() > datetime.datetime.now().timestamp() @@ -592,12 +666,20 @@ def test_device_properties_format(app: Devicehub, user: UserClient): assert format(pc, 's') == '(asustek computer inc.) S/N 94OAAQ021116' assert pc.ram_size == 1024 assert pc.data_storage_size == 152627 - assert pc.graphic_card_model == 'mobile 945gse express integrated graphics controller' + assert ( + pc.graphic_card_model + == 'mobile 945gse express integrated graphics controller' + ) assert pc.processor_model == 'intel atom cpu n270 @ 1.60ghz' net = next(c for c in pc.components if isinstance(c, d.NetworkAdapter)) - assert format(net) == 'NetworkAdapter 5: model ar8121/ar8113/ar8114 ' \ - 'gigabit or fast ethernet, S/N 00:24:8c:7f:cf:2d' - assert format(net, 't') == 'NetworkAdapter ar8121/ar8113/ar8114 gigabit or fast ethernet' + assert ( + format(net) == 'NetworkAdapter 5: model ar8121/ar8113/ar8114 ' + 'gigabit or fast ethernet, S/N 00:24:8c:7f:cf:2d' + ) + assert ( + format(net, 't') + == 'NetworkAdapter ar8121/ar8113/ar8114 gigabit or fast ethernet' + ) assert format(net, 's') == 'qualcomm atheros 00:24:8C:7F:CF:2D – 100 Mbps' hdd = next(c for c in pc.components if isinstance(c, d.DataStorage)) assert format(hdd) == 'HardDrive 10: model st9160310as, S/N 5sv4tqa6' @@ -639,8 +721,12 @@ def test_networking_model(user: UserClient): @pytest.mark.usefixtures(conftest.app_context.__name__) def test_cooking_mixer(user: UserClient): - mixer = d.Mixer(serial_number='foo', model='bar', manufacturer='foobar', - owner_id=user.user['id']) + mixer = d.Mixer( + serial_number='foo', + model='bar', + manufacturer='foobar', + owner_id=user.user['id'], + ) db.session.add(mixer) db.session.commit() @@ -653,12 +739,12 @@ def test_cooking_mixer_api(user: UserClient): 'serialNumber': 'foo', 'model': 'bar', 'manufacturer': 'foobar', - 'type': 'Mixer' + 'type': 'Mixer', }, 'version': '11.0', - 'software': SnapshotSoftware.Web.name + 'software': SnapshotSoftware.Web.name, }, - res=m.Snapshot + res=m.Snapshot, ) mixer, _ = user.get(res=d.Device, item=snapshot['device']['id']) assert mixer['type'] == 'Mixer' @@ -674,14 +760,19 @@ def test_hid_with_mac(app: Devicehub, user: UserClient): pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID']) assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116' pc = d.Device.query.filter_by(devicehub_id=snap['device']['devicehubID']).one() - assert pc.placeholder.binding.hid == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + assert ( + pc.placeholder.binding.hid + == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + ) @pytest.mark.mvp def test_hid_without_mac(app: Devicehub, user: UserClient): """Checks hid without mac.""" snapshot = yaml2json('asus-eee-1000h.snapshot.11') - snapshot['components'] = [c for c in snapshot['components'] if c['type'] != 'NetworkAdapter'] + snapshot['components'] = [ + c for c in snapshot['components'] if c['type'] != 'NetworkAdapter' + ] snap, _ = user.post(json_encode(snapshot), res=m.Snapshot) pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID']) assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116' @@ -710,7 +801,10 @@ def test_hid_with_2networkadapters(app: Devicehub, user: UserClient): devices, _ = user.get(res=d.Device) laptop = devices['items'][0] - assert laptop['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + assert ( + laptop['hid'] + == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + ) assert len([c for c in devices['items'] if c['type'] == 'Laptop']) == 2 @@ -727,14 +821,20 @@ def test_hid_with_2network_and_drop_no_mac_in_hid(app: Devicehub, user: UserClie pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID']) assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116' pc = d.Device.query.filter_by(devicehub_id=snap['device']['devicehubID']).one() - assert pc.placeholder.binding.hid == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + assert ( + pc.placeholder.binding.hid + == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + ) snapshot['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abb' snapshot['components'] = [c for c in snapshot['components'] if c != network] user.post(json_encode(snapshot), res=m.Snapshot) devices, _ = user.get(res=d.Device) laptop = devices['items'][0] - assert pc.placeholder.binding.hid == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + assert ( + pc.placeholder.binding.hid + == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + ) assert len([c for c in devices['items'] if c['type'] == 'Laptop']) == 2 assert len([c for c in laptop['components'] if c['type'] == 'NetworkAdapter']) == 1 @@ -753,7 +853,10 @@ def test_hid_with_2network_and_drop_mac_in_hid(app: Devicehub, user: UserClient) pc, _ = user.get(res=d.Device, item=snap['device']['devicehubID']) assert pc['hid'] == 'laptop-asustek_computer_inc-1000h-94oaaq021116' pc = d.Device.query.filter_by(devicehub_id=snap['device']['devicehubID']).one() - assert pc.placeholder.binding.hid == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + assert ( + pc.placeholder.binding.hid + == 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d' + ) # we drop the network card then is used for to build the hid snapshot['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abb' @@ -763,19 +866,25 @@ def test_hid_with_2network_and_drop_mac_in_hid(app: Devicehub, user: UserClient) laptops = [c for c in devices['items'] if c['type'] == 'Laptop'] assert len(laptops) == 4 hids = [laptops[0]['hid'], laptops[2]['hid']] - proof_hid = ['laptop-asustek_computer_inc-1000h-94oaaq021116-a0:24:8c:7f:cf:2d', - 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d'] + proof_hid = [ + 'laptop-asustek_computer_inc-1000h-94oaaq021116-a0:24:8c:7f:cf:2d', + 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d', + ] assert all([h in proof_hid for h in hids]) # we drop all network cards snapshot['uuid'] = 'd1b70cb8-8929-4f36-99b7-fe052cec0abc' - snapshot['components'] = [c for c in snapshot['components'] if c not in [network, network2]] + snapshot['components'] = [ + c for c in snapshot['components'] if c not in [network, network2] + ] user.post(json_encode(snapshot), res=m.Snapshot) devices, _ = user.get(res=d.Device) laptops = [c for c in devices['items'] if c['type'] == 'Laptop'] assert len(laptops) == 4 hids = [laptops[0]['hid'], laptops[2]['hid']] - proof_hid = ['laptop-asustek_computer_inc-1000h-94oaaq021116-a0:24:8c:7f:cf:2d', - 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d', - 'laptop-asustek_computer_inc-1000h-94oaaq021116'] + proof_hid = [ + 'laptop-asustek_computer_inc-1000h-94oaaq021116-a0:24:8c:7f:cf:2d', + 'laptop-asustek_computer_inc-1000h-94oaaq021116-00:24:8c:7f:cf:2d', + 'laptop-asustek_computer_inc-1000h-94oaaq021116', + ] assert all([h in proof_hid for h in hids]) From 8fecf9e37b6d86dc08ca06e4a57bcb976bd0dd1d Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 29 Sep 2022 12:10:15 +0200 Subject: [PATCH 20/62] Group workbench dependencies --- requirements.in | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.in b/requirements.in index 11ce696b..13862036 100644 --- a/requirements.in +++ b/requirements.in @@ -27,6 +27,7 @@ sqlalchemy-utils==0.33.11 teal==0.2.0a38 tqdm==4.32.2 +# workbench json parsing dependencies pint==0.9 py-dmidecode==0.1.0 pandas==1.3.5 From 74166019b23273a10875132e1c209e1f2fd12ab1 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 29 Sep 2022 12:20:06 +0200 Subject: [PATCH 21/62] rebuild view for label details --- ereuse_devicehub/labels/views.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/ereuse_devicehub/labels/views.py b/ereuse_devicehub/labels/views.py index 421effcb..13766bfe 100644 --- a/ereuse_devicehub/labels/views.py +++ b/ereuse_devicehub/labels/views.py @@ -114,21 +114,35 @@ class PrintLabelsView(View): class LabelDetailView(View): + """This View is used to print labels from multiple devices""" + + methods = ['POST', 'GET'] decorators = [login_required] - template_name = 'labels/label_detail.html' + template_name = 'labels/print_labels.html' + title = 'Design and implementation of labels' def dispatch_request(self, id): lots = Lot.query.filter(Lot.owner_id == current_user.id) tag = ( Tag.query.filter(Tag.owner_id == current_user.id).filter(Tag.id == id).one() ) - context = { 'lots': lots, - 'tag': tag, - 'page_title': '{} Tag'.format(tag.code), + 'page_title': self.title, 'version': __version__, + 'referrer': request.referrer, } + + devices = [] + if tag.device: + form = PrintLabelsForm(devices=str(tag.device.id)) + devices = [tag.device] + else: + form = PrintLabelsForm() + + form._devices = devices + context['form'] = form + context['devices'] = devices return flask.render_template(self.template_name, **context) From 81fa78f6bb5bef1eae4698f7aaae2fac55656ddb Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 29 Sep 2022 12:22:22 +0200 Subject: [PATCH 22/62] Add pip-compile pre-commit hook --- .pre-commit-config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d1307be..4d3d3d8d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,3 +25,7 @@ repos: entry: npm run babel language: node files: ^ereuse_devicehub/static/js/main_inventory.js + - repo: https://github.com/jazzband/pip-tools + rev: 6.8.0 + hooks: + - id: pip-compile From e61a1d88bd5d21bafc3dc4e5019a16521b8556dc Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 29 Sep 2022 12:30:25 +0200 Subject: [PATCH 23/62] unique identifier instead of tags in print label --- ereuse_devicehub/templates/labels/print_labels.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/labels/print_labels.html b/ereuse_devicehub/templates/labels/print_labels.html index c629f887..df419a5f 100644 --- a/ereuse_devicehub/templates/labels/print_labels.html +++ b/ereuse_devicehub/templates/labels/print_labels.html @@ -153,7 +153,7 @@
- +
From f42ccba706a590aca3fb463c85deeb279825fea4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 29 Sep 2022 16:58:30 +0200 Subject: [PATCH 24/62] change contrib for middleware in ProfilerMiddleware --- examples/app.py | 2 +- examples/app_cprofile.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/app.py b/examples/app.py index 47671ec9..623bfaef 100644 --- a/examples/app.py +++ b/examples/app.py @@ -17,7 +17,7 @@ from ereuse_devicehub.workbench.views import workbench # from flask_wtf.csrf import CSRFProtect -# from werkzeug.contrib.profiler import ProfilerMiddleware +# from werkzeug.middleware.profiler import ProfilerMiddleware SENTRY_DSN = config('SENTRY_DSN', None) diff --git a/examples/app_cprofile.py b/examples/app_cprofile.py index e9f7d3fd..834f2f31 100644 --- a/examples/app_cprofile.py +++ b/examples/app_cprofile.py @@ -1,9 +1,9 @@ -from ereuse_devicehub.devicehub import Devicehub -from werkzeug.contrib.profiler import ProfilerMiddleware +from werkzeug.middleware.profiler import ProfilerMiddleware +from ereuse_devicehub.devicehub import Devicehub app = Devicehub(inventory='dbtest') app.config["SQLALCHEMY_RECORD_QUERIES"] = True app.config['PROFILE'] = True app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30]) -app.run(debug = True) +app.run(debug=True) From 6726d439bfa58bb0e2d5bbd9a39870b1988b5fe4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 29 Sep 2022 17:24:00 +0200 Subject: [PATCH 25/62] fix test render print label --- tests/test_render_2_0.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 0ffb4f91..50357d2e 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -356,7 +356,6 @@ def test_label_details(user3: UserClientFlask): user3.post(uri, data=data) body, status = user3.get('/labels/tag1/') - assert "tag1" in body assert "Print Label" in body From 45ae96ce334f8bc69576c3551d00e53c182ddc1d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:16:16 +0200 Subject: [PATCH 26/62] add id_internal and update phid incremental per user --- ...626c17026ca7_id_internal_in_placeholder.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ereuse_devicehub/migrations/versions/626c17026ca7_id_internal_in_placeholder.py diff --git a/ereuse_devicehub/migrations/versions/626c17026ca7_id_internal_in_placeholder.py b/ereuse_devicehub/migrations/versions/626c17026ca7_id_internal_in_placeholder.py new file mode 100644 index 00000000..8c5f28db --- /dev/null +++ b/ereuse_devicehub/migrations/versions/626c17026ca7_id_internal_in_placeholder.py @@ -0,0 +1,59 @@ +"""id internal in placeholder + +Revision ID: 626c17026ca7 +Revises: e919fe0611ff +Create Date: 2022-10-03 19:25:00.581699 + +""" +import sqlalchemy as sa +from alembic import context, op + +# revision identifiers, used by Alembic. +revision = '626c17026ca7' +down_revision = 'e919fe0611ff' +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_datas(): + con = op.get_bind() + sql = 'select id from common.user where phantom=false and active=true' + users = con.execute(sql) + for user in users: + phid = 1 + user_id = user.id + sql = f""" + select id from {get_inv()}.placeholder where owner_id='{user_id}' + order by id + """ + placeholders = con.execute(sql) + + for p in placeholders: + p_id = p.id + sql = f""" + update {get_inv()}.placeholder set phid='{phid}' + where id='{p_id}' + """ + con.execute(sql) + phid += 1 + + +def upgrade(): + op.add_column( + 'placeholder', + sa.Column('id_device_internal', sa.Unicode(), nullable=True), + schema=f'{get_inv()}', + ) + + upgrade_datas() + + +def downgrade(): + op.drop_column('placeholder', 'id_device_internal', schema=f'{get_inv()}') From 02f394d81e59e77ce04803c1b53be9eb9323a15d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:17:27 +0200 Subject: [PATCH 27/62] add id_internal in placeholder model --- ereuse_devicehub/resources/device/models.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 5a40cfc6..f3971c15 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -75,11 +75,15 @@ def create_code(context): return hashcode.encode(_id) -def create_phid(context): - _hid = Placeholder.query.order_by(Placeholder.id.desc()).first() - if _hid: - return str(_hid.id + 1) - return '1' +def create_phid(context, count=1): + phid = str(Placeholder.query.filter(Placeholder.owner == g.user).count() + count) + if ( + Placeholder.query.filter(Placeholder.owner == g.user) + .filter(Placeholder.phid == phid) + .count() + ): + return create_phid(context, count=count + 1) + return phid class Device(Thing): @@ -901,8 +905,8 @@ class DisplayMixin: class Placeholder(Thing): id = Column(BigInteger, Sequence('placeholder_seq'), primary_key=True) + phid = Column(nullable=False, default=create_phid) pallet = Column(Unicode(), nullable=True) - phid = Column(Unicode(), nullable=False, default=create_phid) pallet.comment = "used for identification where from where is this placeholders" info = db.Column(CIText()) components = Column(CIText()) @@ -912,6 +916,8 @@ class Placeholder(Thing): id_device_supplier.comment = ( "Identification used for one supplier of one placeholders" ) + id_device_internal = db.Column(CIText()) + id_device_internal.comment = "Identification used internaly for the user" device_id = db.Column( BigInteger, From 120646289cb997be62dc5a17b31c4c96ca107e36 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:18:42 +0200 Subject: [PATCH 28/62] id internal when create a new placeholder, wbform --- ereuse_devicehub/inventory/forms.py | 54 +++++-------------- ereuse_devicehub/inventory/views.py | 2 +- ereuse_devicehub/static/js/create_device.js | 4 ++ .../templates/inventory/device_create.html | 26 ++++----- 4 files changed, 32 insertions(+), 54 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 1dc9cff2..8d5694f1 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -322,7 +322,7 @@ class NewDeviceForm(FlaskForm): default=1, ) id_device_supplier = StringField('Id Supplier', [validators.Optional()]) - phid = StringField('Placeholder Hardware identity (Phid)', [validators.Optional()]) + id_device_internal = StringField('Id Internal', [validators.Optional()]) pallet = StringField('Identity of pallet', [validators.Optional()]) components = TextAreaField('Components', [validators.Optional()]) info = TextAreaField('Info', [validators.Optional()]) @@ -382,7 +382,7 @@ class NewDeviceForm(FlaskForm): self.type.data = self._obj.type self.amount.render_kw = disabled self.id_device_supplier.data = self._obj.placeholder.id_device_supplier - self.phid.data = self._obj.placeholder.phid + self.id_device_internal.data = self._obj.placeholder.id_device_internal self.pallet.data = self._obj.placeholder.pallet self.info.data = self._obj.placeholder.info self.components.data = self._obj.placeholder.components @@ -411,7 +411,7 @@ class NewDeviceForm(FlaskForm): if self._obj.placeholder.is_abstract: self.type.render_kw = disabled self.amount.render_kw = disabled - self.id_device_supplier.render_kw = disabled + # self.id_device_supplier.render_kw = disabled self.pallet.render_kw = disabled self.info.render_kw = disabled self.components.render_kw = disabled @@ -476,41 +476,11 @@ class NewDeviceForm(FlaskForm): self.meid.errors = error is_valid = False - if self.phid.data and self.amount.data == 1 and not self._obj: - dev = Placeholder.query.filter( - Placeholder.phid == self.phid.data, Placeholder.owner == g.user - ).first() - if dev: - msg = "Error, exist one Placeholder device with this PHID" - self.phid.errors = [msg] - is_valid = False - - if ( - self.phid.data - and self._obj - and self.phid.data != self._obj.placeholder.phid - ): - dev = Placeholder.query.filter( - Placeholder.phid == self.phid.data, Device.owner == g.user - ).first() - if dev: - msg = "Error, exist one Placeholder device with this PHID" - self.phid.errors = [msg] - is_valid = False - if not is_valid: return False if self.image.data == '': self.image.data = None - if self.manufacturer.data: - self.manufacturer.data = self.manufacturer.data.lower() - if self.model.data: - self.model.data = self.model.data.lower() - if self.serial_number.data: - self.serial_number.data = self.serial_number.data.lower() - if self.part_number.data: - self.part_number.data = self.part_number.data.lower() return True @@ -579,8 +549,9 @@ class NewDeviceForm(FlaskForm): def reset_ids(self): if self.amount.data > 1: - self.phid.data = None + # self.phid.data = None self.id_device_supplier.data = None + self.id_device_internal.data = None self.serial_number.data = None self.part_number.data = None self.sku.data = None @@ -590,8 +561,9 @@ class NewDeviceForm(FlaskForm): def get_placeholder(self): self.placeholder = Placeholder( **{ - 'phid': self.phid.data or None, + # 'phid': self.phid.data or None, 'id_device_supplier': self.id_device_supplier.data, + 'id_device_internal': self.id_device_internal.data, 'info': self.info.data, 'components': self.components.data, 'pallet': self.pallet.data, @@ -601,11 +573,13 @@ class NewDeviceForm(FlaskForm): return self.placeholder def edit_device(self): - self._obj.placeholder.phid = self.phid.data or self._obj.placeholder.phid if not self._obj.placeholder.is_abstract: self._obj.placeholder.id_device_supplier = ( self.id_device_supplier.data or None ) + self._obj.placeholder.id_device_internal = ( + self.id_device_internal.data or None + ) self._obj.placeholder.info = self.info.data or None self._obj.placeholder.components = self.components.data or None self._obj.placeholder.pallet = self.pallet.data or None @@ -1567,10 +1541,10 @@ class UploadPlaceholderForm(FlaskForm): if placeholder: self.dev_update += 1 device = placeholder.device - device.model = "{}".format(data['Model'][i]).lower() - device.manufacturer = "{}".format(data['Manufacturer'][i]).lower() - device.serial_number = "{}".format(data['Serial Number'][i]).lower() - device.part_number = "{}".format(data['Part Number'][i]).lower() + device.model = "{}".format(data['Model'][i]) + device.manufacturer = "{}".format(data['Manufacturer'][i]) + device.serial_number = "{}".format(data['Serial Number'][i]) + device.part_number = "{}".format(data['Part Number'][i]) placeholder.id_device_supplier = "{}".format( data['Id device Supplier'][i] ) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 684be46f..41a3c16b 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -545,7 +545,7 @@ class DeviceCreateView(GenericMixin): tpy = form.type.data txt = f'{amount} placeholders Device "{tpy}" created successfully.' placeholder = ( - Placeholder.query.filter_by(owner=g.user) + Placeholder.query.filter(Placeholder.owner == g.user) .order_by(Placeholder.id.desc()) .first() ) diff --git a/ereuse_devicehub/static/js/create_device.js b/ereuse_devicehub/static/js/create_device.js index 04533411..4acece0f 100644 --- a/ereuse_devicehub/static/js/create_device.js +++ b/ereuse_devicehub/static/js/create_device.js @@ -29,14 +29,18 @@ function amountInputs() { if ($("#amount").val() > 1) { $("#Phid").hide(); $("#Id_device_supplier").hide(); + $("#Id_device_internal").hide(); $("#Serial_number").hide(); + $("#Part_number").hide(); $("#Sku").hide(); $("#imei").hide(); $("#meid").hide(); } else { $("#Phid").show(); $("#Id_device_supplier").show(); + $("#Id_device_internal").show(); $("#Serial_number").show(); + $("#Part_number").show(); $("#Sku").show(); deviceInputs(); }; diff --git a/ereuse_devicehub/templates/inventory/device_create.html b/ereuse_devicehub/templates/inventory/device_create.html index fa7bd591..6acf564f 100644 --- a/ereuse_devicehub/templates/inventory/device_create.html +++ b/ereuse_devicehub/templates/inventory/device_create.html @@ -90,19 +90,6 @@ {% endif %}
-
- - {{ form.phid(class_="form-control") }} - Label that you want link to this device - {% if form.phid.errors %} -

- {% for error in form.phid.errors %} - {{ error }}
- {% endfor %} -

- {% endif %} -
-
{{ form.id_device_supplier(class_="form-control") }} @@ -116,6 +103,19 @@ {% endif %}
+
+ + {{ form.id_device_internal(class_="form-control") }} + Identity of device for the internal + {% if form.id_device_internal.errors %} +

+ {% for error in form.id_device_internal.errors %} + {{ error }}
+ {% endfor %} +

+ {% endif %} +
+
{{ form.pallet(class_="form-control") }} From 926f65e291ea49ec191ce4da2f1708cb991c07f9 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:19:16 +0200 Subject: [PATCH 29/62] id internal in device details --- ereuse_devicehub/templates/inventory/device_detail.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ereuse_devicehub/templates/inventory/device_detail.html b/ereuse_devicehub/templates/inventory/device_detail.html index 8fcd628b..762f7cb1 100644 --- a/ereuse_devicehub/templates/inventory/device_detail.html +++ b/ereuse_devicehub/templates/inventory/device_detail.html @@ -98,6 +98,11 @@
{{ placeholder.phid }}
+
+
Id device internal
+
{{ placeholder.id_device_internal or '' }}
+
+
Type
{{ placeholder.device.type }}
From 77d10661b36dec1b438df417febd0f88746a1e95 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:49:15 +0200 Subject: [PATCH 30/62] drop phid from UploadPlaceholderForm and drop update placeholder form this form --- ereuse_devicehub/inventory/forms.py | 38 +++-------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 8d5694f1..eac9fcfa 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -549,7 +549,6 @@ class NewDeviceForm(FlaskForm): def reset_ids(self): if self.amount.data > 1: - # self.phid.data = None self.id_device_supplier.data = None self.id_device_internal.data = None self.serial_number.data = None @@ -561,7 +560,6 @@ class NewDeviceForm(FlaskForm): def get_placeholder(self): self.placeholder = Placeholder( **{ - # 'phid': self.phid.data or None, 'id_device_supplier': self.id_device_supplier.data, 'id_device_internal': self.id_device_internal.data, 'info': self.info.data, @@ -1488,9 +1486,7 @@ class UploadPlaceholderForm(FlaskForm): else: self.source = "Excel File: {}".format(_file.filename) try: - data = ( - pd.read_excel(_file, converters={'Phid': str}).fillna('').to_dict() - ) + data = pd.read_excel(_file).fillna('').to_dict() except ValueError: txt = ["File don't have a correct format"] self.placeholder_file.errors = txt @@ -1512,12 +1508,12 @@ class UploadPlaceholderForm(FlaskForm): return False header = [ - 'Phid', 'Model', 'Manufacturer', 'Serial Number', 'Part Number', 'Id device Supplier', + 'Id device Internal', 'Pallet', 'Info', ] @@ -1531,32 +1527,7 @@ class UploadPlaceholderForm(FlaskForm): self.placeholders = [] schema = SnapshotSchema() self.path_snapshots = {} - for i in data['Phid'].keys(): - placeholder = None - data['Phid'][i] = str(data['Phid'][i]) - if data['Phid'][i]: - placeholder = Placeholder.query.filter_by(phid=data['Phid'][i]).first() - - # update one - if placeholder: - self.dev_update += 1 - device = placeholder.device - device.model = "{}".format(data['Model'][i]) - device.manufacturer = "{}".format(data['Manufacturer'][i]) - device.serial_number = "{}".format(data['Serial Number'][i]) - device.part_number = "{}".format(data['Part Number'][i]) - placeholder.id_device_supplier = "{}".format( - data['Id device Supplier'][i] - ) - placeholder.pallet = "{}".format(data['Pallet'][i]) - placeholder.info = "{}".format(data['Info'][i]) - - placeholder_log = PlaceholdersLog( - type="Update", source=self.source, placeholder=device.placeholder - ) - self.placeholders.append((device, placeholder_log)) - continue - + for i in data['Model'].keys(): # create a new one json_snapshot = { 'type': 'Snapshot', @@ -1571,8 +1542,8 @@ class UploadPlaceholderForm(FlaskForm): }, } json_placeholder = { - 'phid': data['Phid'][i] or None, 'id_device_supplier': data['Id device Supplier'][i], + 'id_device_internal': data['Id device Internal'][i], 'pallet': data['Pallet'][i], 'info': data['Info'][i], 'is_abstract': False, @@ -1609,7 +1580,6 @@ class EditPlaceholderForm(FlaskForm): serial_number = StringField('Serial Number', [validators.Optional()]) part_number = StringField('Part Number', [validators.Optional()]) id_device_supplier = StringField('Id Supplier', [validators.Optional()]) - phid = StringField('Phid', [validators.DataRequired()]) pallet = StringField('Pallet', [validators.Optional()]) info = StringField('Info', [validators.Optional()]) From 722ee1a26134eb07ba99efd10afe1584abd92b30 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 12:22:40 +0200 Subject: [PATCH 31/62] fix phid definition --- ereuse_devicehub/resources/device/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index f3971c15..67f4bb80 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -10,7 +10,6 @@ from boltons import urlutils from citext import CIText from ereuse_utils.naming import HID_CONVERSION_DOC, Naming from flask import g, request -from flask_sqlalchemy import event from more_itertools import unique_everseen from sqlalchemy import BigInteger, Boolean, Column from sqlalchemy import Enum as DBEnum @@ -905,7 +904,7 @@ class DisplayMixin: class Placeholder(Thing): id = Column(BigInteger, Sequence('placeholder_seq'), primary_key=True) - phid = Column(nullable=False, default=create_phid) + phid = Column(Unicode(), nullable=False, default=create_phid) pallet = Column(Unicode(), nullable=True) pallet.comment = "used for identification where from where is this placeholders" info = db.Column(CIText()) @@ -1599,4 +1598,5 @@ def create_code_tag(mapper, connection, device): db.session.add(tag) +# from flask_sqlalchemy import event # event.listen(Device, 'after_insert', create_code_tag, propagate=True) From 74dde2e62693604f880d85c2435c36d01dd02634 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 13:44:29 +0200 Subject: [PATCH 32/62] fix form new device --- ereuse_devicehub/inventory/forms.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index eac9fcfa..b1503c6e 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -481,6 +481,14 @@ class NewDeviceForm(FlaskForm): if self.image.data == '': self.image.data = None + if self.manufacturer.data: + self.manufacturer.data = self.manufacturer.data.lower() + if self.model.data: + self.model.data = self.model.data.lower() + if self.serial_number.data: + self.serial_number.data = self.serial_number.data.lower() + if self.part_number.data: + self.part_number.data = self.part_number.data.lower() return True From c97ef941016352aebf4cd73c94f9565f3e2e884f Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 13:45:06 +0200 Subject: [PATCH 33/62] fix tests --- tests/files/placeholder_test.csv | 8 +-- tests/files/placeholder_test.ods | Bin 10054 -> 10251 bytes tests/files/placeholder_test.xls | Bin 6144 -> 6144 bytes tests/files/placeholder_test.xlsx | Bin 5087 -> 5083 bytes tests/test_render_2_0.py | 90 +++++++++++------------------- 5 files changed, 36 insertions(+), 62 deletions(-) diff --git a/tests/files/placeholder_test.csv b/tests/files/placeholder_test.csv index 37579398..db9a65f7 100644 --- a/tests/files/placeholder_test.csv +++ b/tests/files/placeholder_test.csv @@ -1,4 +1,4 @@ -Phid;Model;Manufacturer;Serial Number;Part Number;Id device Supplier;Pallet;Info -a123;Vaio;Sony;12345678;;TTT;24A;Good conditions -a124;Vaio;Sony;12345679;;TTT;24A;Good conditions -a125;Vaio;Sony;12345680;;TTT;24A;Good conditions +Model;Manufacturer;Serial Number;Part Number;Id device Supplier;Id device Internal;Pallet;Info +Vaio;Sony;12345678;;TTT;AA;24A;Good conditions +Vaio;Sony;12345679;;TTT;BB;24A;Good conditions +Vaio;Sony;12345680;;TTT;CC;24A;Good conditions diff --git a/tests/files/placeholder_test.ods b/tests/files/placeholder_test.ods index f41c6b35b6d2394bbbbdda338f1160cfe34fe911..b882d2bdd6c016e5725ed8f12c313cbcf4b116d6 100644 GIT binary patch delta 6154 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 delta 6013 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 diff --git a/tests/files/placeholder_test.xls b/tests/files/placeholder_test.xls index e9b64b1a439a772b8c4cd63587c75a65d0694e97..50b76aa3bd43db7bbfad18623a325f4dad973be9 100644 GIT binary patch delta 404 zcmY+8&q~8U5XNUWskTX)jivD*9^z3DDN>6Hiv5Eno&<}YLlPlSvZ6)Qg9gE?m$~`? z9wk+ZZ_;P*AoS=#bW%&93$ydheDh-_sU(%xTOB{OBFp2s-ePO`t|wXwO8{U|;DK>? zUjlFgCkU@1KXlx<7hc3(cMjml>vqGQN^X26DV=yxmX2k7qX2X|9S%^hliaA*1wdQM z!0}}qM7>DHmt-~?^fjBmpwY5ZLJeB!=WPlSlV_*JO~xLuW)7H*U*_)e9oK*X2LU^e zBmP4)1C=U`2yBb$c1CAyPKJO2RT;v^$le6uQ{;a#N0TPLiPb5s%+Si1ZcJ!phE^b> z1uj}orb-&d2a(EQYOSXWTg<3YbDshV_6N2~PJV#T?#pnj37LGKM?w@LZvvrA|1)p_O|;+4!RyP&*fKec zZ#yIZWIlddnZG~@Mg}GZejv@jP{#z~F#~y4tsj{tr}LXw@&ScGikN^B{0y9M9Ge#a$pqK+l;$p>|$u2?)92dU)IyL?H4aUieEFzm5gesUAFK+%R!p;l; Dq8vhR diff --git a/tests/files/placeholder_test.xlsx b/tests/files/placeholder_test.xlsx index 28420bf21a5ea5ddad030efcdf4b5ec8cd907f6a..53b0034f77ac63a822c615dc235f406a8c2ba115 100644 GIT binary patch delta 2803 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 E4{#Oy5dZ)H diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 0ffb4f91..e65140b1 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -562,7 +562,6 @@ def test_update_monitor(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Monitor", - 'phid': '1', 'serial_number': "AAAAB", 'model': "LCD 43 b", 'manufacturer': "Samsung", @@ -575,8 +574,9 @@ def test_update_monitor(user3: UserClientFlask): } body, status = user3.post(uri, data=data) assert status == '200 OK' - assert 'Error, exist one Placeholder device with this PHID' in body - dev = Device.query.one() + # assert 'Error, exist one Placeholder device with this PHID' in body + dev = Device.query.all()[0] + assert Device.query.count() == 2 assert dev.type == 'Monitor' assert dev.placeholder.id_device_supplier == "b2" assert dev.hid == 'monitor-samsung-lc27t55-aaaab' @@ -597,7 +597,6 @@ def test_add_2_monitor(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Monitor", - 'phid': "AAB", 'serial_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", @@ -619,7 +618,7 @@ def test_add_2_monitor(user3: UserClientFlask): assert typ == 'Monitor' assert dev.placeholder.id_device_supplier == "b1" assert dev.hid == 'monitor-samsung-lc27t55-aaaab' - assert phid == 'AAB' + assert phid == '1' assert dhid == 'O48N2' assert dev.model == 'lc27t55' assert dev.placeholder.pallet == "l34" @@ -737,35 +736,6 @@ def test_add_with_ammount_laptops(user3: UserClientFlask): assert Device.query.count() == num -@pytest.mark.mvp -@pytest.mark.usefixtures(conftest.app_context.__name__) -def test_add_laptop_duplicate(user3: UserClientFlask): - - uri = '/inventory/device/add/' - body, status = user3.get(uri) - assert status == '200 OK' - assert "New Device" in body - - data = { - 'csrf_token': generate_csrf(), - 'type': "Laptop", - 'phid': 'laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b', - 'serial_number': "AAAAB", - 'model': "LC27T55", - 'manufacturer': "Samsung", - 'generation': 1, - 'weight': 0.1, - 'height': 0.1, - 'depth': 0.1, - } - body, status = user3.post(uri, data=data) - assert status == '200 OK' - assert Device.query.count() == 1 - body, status = user3.post(uri, data=data) - assert 'Error, exist one Placeholder device with this PHID' in body - assert Device.query.count() == 1 - - @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_filter_monitor(user3: UserClientFlask): @@ -1729,7 +1699,7 @@ def test_add_placeholder_excel(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' @@ -1755,7 +1725,7 @@ def test_add_placeholder_csv(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' @@ -1781,7 +1751,7 @@ def test_add_placeholder_ods(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' @@ -1809,10 +1779,11 @@ def test_add_placeholder_office_open_xml(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' + assert dev.placeholder.id_device_internal == 'AA' @pytest.mark.mvp @@ -1921,8 +1892,8 @@ def test_placeholder_log_manual_edit(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Laptop", - 'phid': 'ace', 'serial_number': "AAAAB", + 'part_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", 'generation': 1, @@ -1930,9 +1901,13 @@ def test_placeholder_log_manual_edit(user3: UserClientFlask): 'height': 0.1, 'depth': 0.1, 'id_device_supplier': "b2", + 'id_device_internal': "b2i", } user3.post(uri, data=data) dev = Device.query.one() + plz = Placeholder.query.first() + assert plz.id_device_supplier == "b2" + assert plz.id_device_internal == "b2i" uri = '/inventory/device/edit/{}/'.format(dev.devicehub_id) user3.get(uri) @@ -1948,16 +1923,20 @@ def test_placeholder_log_manual_edit(user3: UserClientFlask): 'height': 0.1, 'depth': 0.1, 'id_device_supplier': "a2", + 'id_device_internal': "a2i", } user3.post(uri, data=data) + plz = Placeholder.query.first() + assert plz.id_device_supplier == "a2" + assert plz.id_device_internal == "a2i" uri = '/inventory/placeholder-logs/' body, status = user3.get(uri) assert status == '200 OK' assert "Placeholder Logs" in body - assert "Web form" in body - assert "ace" in body assert "Update" in body + assert "Web form" in body + assert "1" in body assert dev.devicehub_id in body assert "✓" in body assert "CSV" not in body @@ -1980,7 +1959,7 @@ def test_placeholder_log_excel_new(user3: UserClientFlask): } user3.post(uri, data=data, content_type="multipart/form-data") dev = Device.query.first() - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' uri = '/inventory/placeholder-logs/' body, status = user3.get(uri) @@ -1989,7 +1968,6 @@ def test_placeholder_log_excel_new(user3: UserClientFlask): assert dev.placeholder.phid in body assert dev.devicehub_id in body assert "Web form" not in body - assert "Update" not in body assert "New device" in body assert "✓" in body assert "CSV" not in body @@ -2023,7 +2001,7 @@ def test_placeholder_log_excel_update(user3: UserClientFlask): user3.post(uri, data=data, content_type="multipart/form-data") dev = Device.query.first() - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' uri = '/inventory/placeholder-logs/' body, status = user3.get(uri) @@ -2032,7 +2010,6 @@ def test_placeholder_log_excel_update(user3: UserClientFlask): assert dev.placeholder.phid in body assert dev.devicehub_id in body assert "Web form" not in body - assert "Update" in body assert "New device" in body assert "✓" in body assert "CSV" in body @@ -2070,7 +2047,7 @@ def test_add_placeholder_excel_from_lot(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' @@ -2097,7 +2074,6 @@ def test_add_new_placeholder_from_lot(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Laptop", - 'phid': 'ace', 'serial_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", @@ -2110,7 +2086,7 @@ def test_add_new_placeholder_from_lot(user3: UserClientFlask): user3.post(uri, data=data) dev = Device.query.one() assert dev.hid == 'laptop-samsung-lc27t55-aaaab' - assert dev.placeholder.phid == 'ace' + assert dev.placeholder.phid == '1' assert len(lot.devices) == 1 @@ -2124,7 +2100,6 @@ def test_manual_binding(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Laptop", - 'phid': 'sid', 'serial_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", @@ -2136,7 +2111,7 @@ def test_manual_binding(user3: UserClientFlask): user3.post(uri, data=data) dev = Device.query.one() assert dev.hid == 'laptop-samsung-lc27t55-aaaab' - assert dev.placeholder.phid == 'sid' + assert dev.placeholder.phid == '1' assert dev.placeholder.is_abstract is False # add device from wb @@ -2155,7 +2130,7 @@ def test_manual_binding(user3: UserClientFlask): # page binding dhid = dev_wb.dhid - uri = f'/inventory/binding/{dhid}/sid/' + uri = f'/inventory/binding/{dhid}/1/' body, status = user3.get(uri) assert status == '200 OK' assert 'sid' in body @@ -2174,7 +2149,7 @@ def test_manual_binding(user3: UserClientFlask): assert txt in body # check new structure - assert dev_wb.binding.phid == 'sid' + assert dev_wb.binding.phid == '1' assert dev_wb.binding.device == dev assert Placeholder.query.filter_by(id=old_placeholder.id).first() is None assert Device.query.filter_by(id=old_placeholder.device.id).first() is None @@ -2237,7 +2212,6 @@ def test_unbinding(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Laptop", - 'phid': 'sid', 'serial_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", @@ -2262,7 +2236,7 @@ def test_unbinding(user3: UserClientFlask): # page binding dhid = dev_wb.dhid - uri = f'/inventory/binding/{dhid}/sid/' + uri = f'/inventory/binding/{dhid}/1/' user3.get(uri) # action binding @@ -2272,10 +2246,10 @@ def test_unbinding(user3: UserClientFlask): dhid = dev.dhid # action unbinding - uri = '/inventory/unbinding/sid/' + uri = '/inventory/unbinding/1/' body, status = user3.post(uri, data={}) assert status == '200 OK' - txt = f'Device with PHID:"sid" and DHID: {dhid} unbind successfully!' + txt = f'Device with PHID:"1" and DHID: {dhid} unbind successfully!' assert txt in body # assert 'Device "sid" unbind successfully!' in body @@ -2400,8 +2374,8 @@ def test_bug_3821_binding(user3: UserClientFlask): user3.post(uri, data=data) dev = Device.query.one() dhid = dev.dhid - assert dev.phid() == 'sid' - uri = f'/inventory/binding/{dhid}/sid/' + assert dev.phid() == '1' + uri = f'/inventory/binding/{dhid}/1/' body, status = user3.get(uri) assert status == '200 OK' assert 'is not a Snapshot device!' in body From 4f1e6c28eb3d9320501ba1a9df98b32bebaf4b50 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 14:49:09 +0200 Subject: [PATCH 34/62] up version to 2.4.1 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f96447f9..2be69eb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ ml). ## testing +## [2.4.1] - 2022-10-05 +- [added] #365 Manage dependencies using pip-tools. +- [added] #368 add migrations of monitors and mobiles. +- [changed]] #371 changes about phid, incremental per user. +- [fixed] #364 bad redirect to all devices. +- [fixed] #367 column PHID Erasure host. +- [fixed] #369 bug in test data storage. +- [fixed] #370 print label in details of the label. + ## [2.4.0] - 2022-09-23 - [added] #312 Placeholder: new, edit, update. (manually and with excel). - [added] #316 Placeholder: binding/unbinding. (manually). From 795d4a20fb77c3d526412e28dfa22ae836201269 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 15:05:20 +0200 Subject: [PATCH 35/62] up version --- ereuse_devicehub/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/__init__.py b/ereuse_devicehub/__init__.py index 3d67cd6b..54499df3 100644 --- a/ereuse_devicehub/__init__.py +++ b/ereuse_devicehub/__init__.py @@ -1 +1 @@ -__version__ = "2.4.0" +__version__ = "2.4.1" From 3d4b74f0ca870a04ca9552a14c0beb586a10d768 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 6 Oct 2022 08:35:00 +0200 Subject: [PATCH 36/62] drop logo --- ereuse_devicehub/templates/ereuse_devicehub/base_site.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index 6fdf1129..e3c81ad8 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -3,11 +3,7 @@ {% block body %}
{% endif %} -
- {% if lot and not lot.is_temporary %} + {% if lot %} {% endif %}
- {% if lot and lot.is_temporary %} - - {% endif %}
- {% if lot and not lot.is_temporary %} - - {% endif %} - @@ -436,6 +418,14 @@
{% if lot and not lot.is_temporary %}
+ +
Documents
From 1cd929e06ba48c302b73b0ef44dc1bf47f253478 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 6 Oct 2022 15:32:09 +0200 Subject: [PATCH 39/62] move links identifier and data storage to top --- .../templates/ereuse_devicehub/base_site.html | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index e3c81ad8..4abeec8b 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -39,6 +39,11 @@ + +
  • + + Data Storage Erasures + +
  • @@ -252,22 +262,6 @@ - - - - - - - - From 4b1f92573a7edb210b6ff970cd22c66a22d54989 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 6 Oct 2022 16:06:14 +0200 Subject: [PATCH 40/62] change type for type upload --- ereuse_devicehub/templates/inventory/placeholder_log_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/inventory/placeholder_log_list.html b/ereuse_devicehub/templates/inventory/placeholder_log_list.html index 11aa1fc9..1e369db3 100644 --- a/ereuse_devicehub/templates/inventory/placeholder_log_list.html +++ b/ereuse_devicehub/templates/inventory/placeholder_log_list.html @@ -27,7 +27,7 @@
    - + From abe3c44213c94db010a30b22ee74854024ad39c8 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 6 Oct 2022 16:56:12 +0200 Subject: [PATCH 41/62] add new buttons for transfer --- ereuse_devicehub/inventory/forms.py | 14 +++++++++----- ereuse_devicehub/inventory/views.py | 6 +++++- .../templates/ereuse_devicehub/base_site.html | 10 ++++++++++ .../templates/inventory/device_list.html | 4 ++-- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index b1503c6e..ed69f202 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -1275,14 +1275,14 @@ class TransferForm(FlaskForm): def __init__(self, *args, **kwargs): self._type = kwargs.get('type') lot_id = kwargs.pop('lot_id', None) - self._tmp_lot = Lot.query.filter(Lot.id == lot_id).one() + self._tmp_lot = None + if lot_id: + self._tmp_lot = Lot.query.filter(Lot.id == lot_id).one() super().__init__(*args, **kwargs) self._obj = None def validate(self, extra_validators=None): is_valid = super().validate(extra_validators) - if not self._tmp_lot: - return False if self._type and self.type.data not in ['incoming', 'outgoing']: return False @@ -1303,8 +1303,12 @@ class TransferForm(FlaskForm): return self._obj def set_obj(self): - self.newlot = Lot(name=self._tmp_lot.name) - self.newlot.devices = self._tmp_lot.devices + name = self.code.data + if self._tmp_lot: + name = self._tmp_lot.name + self.newlot = Lot(name=name) + if self._tmp_lot: + self.newlot.devices = self._tmp_lot.devices db.session.add(self.newlot) self._obj = Transfer(lot=self.newlot) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 41a3c16b..63d9294f 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -753,7 +753,7 @@ class NewTransferView(GenericMixin): form_class = TransferForm title = "Add new transfer" - def dispatch_request(self, lot_id, type_id): + def dispatch_request(self, type_id, lot_id=None): self.form = self.form_class(lot_id=lot_id, type=type_id) self.get_context() @@ -1355,6 +1355,10 @@ devices.add_url_rule( ) devices.add_url_rule( '/lot//transfer//', + view_func=NewTransferView.as_view('lot_new_transfer'), +) +devices.add_url_rule( + '/lot/transfer//', view_func=NewTransferView.as_view('new_transfer'), ) devices.add_url_rule( diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index 4abeec8b..a4deb465 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -194,6 +194,11 @@ {% else %} - + @@ -445,7 +445,7 @@ {% endif %} {% endfor %} @@ -459,7 +459,7 @@ {% endif %} {% endfor %} diff --git a/ereuse_devicehub/templates/inventory/placeholder_log_list.html b/ereuse_devicehub/templates/inventory/placeholder_log_list.html index 1e369db3..aaff827d 100644 --- a/ereuse_devicehub/templates/inventory/placeholder_log_list.html +++ b/ereuse_devicehub/templates/inventory/placeholder_log_list.html @@ -30,7 +30,7 @@ - + @@ -53,7 +53,7 @@ - + {% endfor %} diff --git a/ereuse_devicehub/templates/inventory/snapshots_list.html b/ereuse_devicehub/templates/inventory/snapshots_list.html index 2c6b5645..d41f239f 100644 --- a/ereuse_devicehub/templates/inventory/snapshots_list.html +++ b/ereuse_devicehub/templates/inventory/snapshots_list.html @@ -34,7 +34,7 @@ - + @@ -80,7 +80,7 @@ - + - + @@ -63,7 +63,7 @@ {% endif %} - + {% endfor %} From b66871e9c23582724bdc70381855139d861801ca Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 10 Oct 2022 17:44:20 +0200 Subject: [PATCH 47/62] first step for layout new settings --- .../templates/workbench/settings.html | 128 +++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index 4cf2e1cc..f571782d 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -43,7 +43,7 @@
    -
    Workbench 2022
    +
    Register devices
    {{ iso.demo.iso }} @@ -141,5 +141,131 @@ {% endif %} + +
    + +
    +
    + +
    +
    Register devices
    + +
    + +
    +

    + Download Checksum: SHA512SUM | + Help

    +

    +
    +
    + +
    +
    +
    +

    HW and Smart test

    +
    + +
    +
    + +
    + +
    +
    +
    +
    + +
    +
    Erasure hard-drive
    + +
    + +
    +

    + Download Checksum: SHA512SUM | + Help

    +

    +
    +
    + +
    +
    +
    +

    + Settings for basic data erasure using shred fast non-100% secured way of erasing data storage. +

    +

    + One overwriting round using a randomn pattern. + Compliant with British HMG Infosec Standard 5, + Baseline Standard +

    +
    + +
    + +
    +
    +

    + Settings for basic data erasure + using shred Linux command. A software-based fast non-100 + secured way of erasing data storage. +

    +

    + One overwriting round using all zeros. + Compliant with NIST SP-800-88 +

    +
    + +
    + +
    +
    +

    + Settings for advanced data erasure using badblocks Linux software. + A secured-way of erasing data storages, erase hidden areas, + provide a defects log list and list bad sectors that could not be + overwritten. +

    +

    + Three overwritting rounds using patterh + (All ones, all zeros, random). Compliant with British HMG Infosec + Standard 5, Enganced Standard +

    +
    + +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    + {% endblock main %} From b77d7abaa4087ad1d0aff26aa6f7e7d268317a7e Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 11 Oct 2022 16:00:38 +0200 Subject: [PATCH 48/62] layout settings --- .../templates/workbench/settings.html | 84 +++++++++++++++---- 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index f571782d..bd226256 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -43,7 +43,7 @@
    -
    Register devices
    +
    Usody Metadata Snapshots
    {{ iso.demo.iso }} @@ -142,35 +142,32 @@ -
    +
    -
    Register devices
    +
    Usody Metadata Snapshots
    -
    + -
    +

    Download Checksum: SHA512SUM | Help

    +
    -
    -
    +
    +

    HW and Smart test

    -
    -
    @@ -183,20 +180,76 @@
    -
    Erasure hard-drive
    +
    Usody Erasure Snapshots
    -
    + -
    +

    Download Checksum: SHA512SUM | Help

    +
    +
    +
    +
    +

    + Settings for basic data erasure using shred fast non-100% secured way of erasing data storage. +

    +

    + One overwriting round using a randomn pattern. + Compliant with British HMG Infosec Standard 5, + Baseline Standard +

    + + Download settings file + +
    +
    +
    +
    +

    + Settings for basic data erasure using shred fast non-100% secured way of erasing data storage. +

    +

    + Settings for basic data erasure using shred fast non-100% secured way of erasing data storage. +

    +

    + One overwriting round using a randomn pattern. + Compliant with British HMG Infosec Standard 5, + Baseline Standard +

    + Download settings file +
    +
    +
    +
    From c8c542189a087d82f9b1ce9d62944f3202b03631 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 11 Oct 2022 16:01:35 +0200 Subject: [PATCH 49/62] drop comments --- .../templates/workbench/settings.html | 80 ------------------- 1 file changed, 80 deletions(-) diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index bd226256..a498eebc 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -230,86 +230,6 @@
    -
    From 5819154c5a4b38bd735dc210fce8ca5fd7b604a6 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 11 Oct 2022 18:04:25 +0200 Subject: [PATCH 50/62] fix layout print label --- ereuse_devicehub/static/css/devicehub.css | 3 +++ ereuse_devicehub/templates/labels/print_labels.html | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ereuse_devicehub/static/css/devicehub.css b/ereuse_devicehub/static/css/devicehub.css index 84771144..46fae783 100644 --- a/ereuse_devicehub/static/css/devicehub.css +++ b/ereuse_devicehub/static/css/devicehub.css @@ -29,4 +29,7 @@ .doTransfer { margin-top: -8px; margin-right: 15px;" +} +.printLabelForm { + margin-left: 10px; } \ No newline at end of file diff --git a/ereuse_devicehub/templates/labels/print_labels.html b/ereuse_devicehub/templates/labels/print_labels.html index df419a5f..0e36d68d 100644 --- a/ereuse_devicehub/templates/labels/print_labels.html +++ b/ereuse_devicehub/templates/labels/print_labels.html @@ -13,7 +13,7 @@
    -
    +
    @@ -91,8 +91,7 @@
    {% endfor %}
    -
    -
    +
    From 6b173de5a8d2cb9083437aac160b1183e458729a Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 13 Oct 2022 18:21:44 +0200 Subject: [PATCH 51/62] setup kangaroo server --- ereuse_devicehub/inventory/views.py | 10 +++- ...13ed6ad0e3e_add_kangaroo_in_placeholder.py | 34 +++++++++++ ereuse_devicehub/resources/device/models.py | 1 + .../templates/workbench/settings.html | 56 +++++++++++++++++++ ereuse_devicehub/workbench/views.py | 31 +++++++++- 5 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 ereuse_devicehub/migrations/versions/a13ed6ad0e3e_add_kangaroo_in_placeholder.py diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index fde885d7..0267db00 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -296,6 +296,8 @@ class BindingView(GenericMixin): self.real_phid = self.new_placeholder.phid self.abstract_dhid = self.old_device.devicehub_id self.abstract_phid = self.old_placeholder.phid + if self.old_placeholder.kangaroo: + self.new_placeholder.kangaroo = True # to do a backup of abstract_dhid and abstract_phid in # workbench device @@ -383,6 +385,9 @@ class UnBindingView(GenericMixin): if device.binding.is_abstract: return + kangaroo = device.binding.kangaroo + device.binding.kangaroo = False + dict_device = copy.copy(device.__dict__) dict_device.pop('_sa_instance_state') dict_device.pop('id', None) @@ -402,7 +407,10 @@ class UnBindingView(GenericMixin): if c.binding: c.binding.device.parent = new_device - placeholder = Placeholder(device=new_device, binding=device, is_abstract=True) + placeholder = Placeholder( + device=new_device, binding=device, is_abstract=True, kangaroo=kangaroo + ) + if ( device.dhid_bk and not Device.query.filter_by(devicehub_id=device.dhid_bk).first() diff --git a/ereuse_devicehub/migrations/versions/a13ed6ad0e3e_add_kangaroo_in_placeholder.py b/ereuse_devicehub/migrations/versions/a13ed6ad0e3e_add_kangaroo_in_placeholder.py new file mode 100644 index 00000000..bc3b6790 --- /dev/null +++ b/ereuse_devicehub/migrations/versions/a13ed6ad0e3e_add_kangaroo_in_placeholder.py @@ -0,0 +1,34 @@ +"""add kangaroo in placeholder + +Revision ID: a13ed6ad0e3e +Revises: 626c17026ca7 +Create Date: 2022-10-13 11:56:15.303218 + +""" +import sqlalchemy as sa +from alembic import context, op + +# revision identifiers, used by Alembic. +revision = 'a13ed6ad0e3e' +down_revision = '626c17026ca7' +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( + 'placeholder', + sa.Column('kangaroo', sa.Boolean(), nullable=True), + schema=f'{get_inv()}', + ) + + +def downgrade(): + op.drop_column('placeholder', 'kangaroo', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 5f1ccb20..86d8c834 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -932,6 +932,7 @@ class Placeholder(Thing): ) id_device_internal = db.Column(CIText()) id_device_internal.comment = "Identification used internaly for the user" + kangaroo = db.Column(Boolean, default=False, nullable=True) device_id = db.Column( BigInteger, diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index 4cf2e1cc..f1644d3e 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -11,6 +11,62 @@
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    PHID Placeholder sourceTypeType Upload DHID Status Time
    FileUploaded onUploaded on
    - {{ doc.created.strftime('%H:%M %d-%m-%Y')}} + {{ doc.created.strftime('%Y-%m-%d %H:%M')}}
    - {{ doc.created.strftime('%H:%M %d-%m-%Y')}} + {{ doc.created.strftime('%Y-%m-%d %H:%M')}}
    Type Upload DHID StatusTimeTime
    {{ log.get_status() }} {{ log.created.strftime('%H:%M %d-%m-%Y') }}{{ log.created.strftime('%Y-%m-%d %H:%M') }}
    Type Upload Type Device Original DHIDTimeTime
    {{ snap.original_dhid }} {{ snap.created.strftime('%H:%M %d-%m-%Y') }}{{ snap.created.strftime('%Y-%m-%d %H:%M') }} {% if snap.snapshot_uuid %} diff --git a/ereuse_devicehub/templates/labels/label_list.html b/ereuse_devicehub/templates/labels/label_list.html index 4f843ea0..133189bf 100644 --- a/ereuse_devicehub/templates/labels/label_list.html +++ b/ereuse_devicehub/templates/labels/label_list.html @@ -47,7 +47,7 @@ Type Provider DeviceCreatedCreated
    {{ tag.created.strftime('%H:%M %d-%m-%Y') }}{{ tag.created.strftime('%Y-%m-%d %H:%M') }}
    + + + + + + + + {% for host in form.kangaroos %} + + + + + {% endfor %} + + + + + +
    PHIDErasure Host
    {{ host.phid }} + +
    + {% for f in form %} + {{ f }} + {% if f == form.phid and f.errors %} +

    + {% for error in f.errors %} + {{ error }}
    + {% endfor %} +

    + {% endif %} + {% endfor %} +
    + +
    + +
    +
    +
    + +
    + + + + +
    diff --git a/ereuse_devicehub/workbench/views.py b/ereuse_devicehub/workbench/views.py index 3640ab5b..60c34fd3 100644 --- a/ereuse_devicehub/workbench/views.py +++ b/ereuse_devicehub/workbench/views.py @@ -3,34 +3,43 @@ import time import flask from flask import Blueprint from flask import current_app as app -from flask import g, make_response, request +from flask import g, make_response, request, url_for +from flask.views import View from flask_login import login_required from ereuse_devicehub import auth from ereuse_devicehub.db import db +from ereuse_devicehub.resources.device.models import Placeholder from ereuse_devicehub.resources.enums import SessionType from ereuse_devicehub.resources.user.models import Session from ereuse_devicehub.views import GenericMixin from ereuse_devicehub.workbench import isos +from ereuse_devicehub.workbench.forms import KangarooForm workbench = Blueprint('workbench', __name__, url_prefix='/workbench') class SettingsView(GenericMixin): decorators = [login_required] + methods = ['GET', 'POST'] template_name = 'workbench/settings.html' page_title = "Workbench" def dispatch_request(self): self.get_context() + form_kangaroo = KangarooForm() self.context.update( { 'page_title': self.page_title, 'demo': g.user.email == app.config['EMAIL_DEMO'], 'iso': isos, + 'form': form_kangaroo, } ) + if form_kangaroo.validate_on_submit(): + form_kangaroo.save() + self.opt = request.values.get('opt') if self.opt in ['register', 'erease_basic', 'erease_sectors']: return self.download() @@ -86,4 +95,24 @@ class SettingsView(GenericMixin): return token +class ErasureHostView(View): + decorators = [login_required] + methods = ['GET'] + + def dispatch_request(self, id): + self.placeholder = ( + Placeholder.query.filter(Placeholder.id == id) + .filter(Placeholder.kangaroo.is_(True)) + .filter(Placeholder.owner_id == g.user.id) + .one() + ) + self.placeholder.kangaroo = False + db.session.commit() + + return flask.redirect(url_for('workbench.settings')) + + workbench.add_url_rule('/', view_func=SettingsView.as_view('settings')) +workbench.add_url_rule( + '/erasure_host//', view_func=ErasureHostView.as_view('erasure_host') +) From ce09de4133649710e170923a4ff808020f1001e4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 14 Oct 2022 15:33:43 +0200 Subject: [PATCH 52/62] add tests --- ereuse_devicehub/inventory/forms.py | 13 +++ .../templates/inventory/device_list.html | 2 + tests/test_basic.py | 1 + tests/test_render_2_0.py | 96 +++++++++++++++++++ 4 files changed, 112 insertions(+) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 25dcba02..69f0b4ba 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -86,12 +86,18 @@ DEVICES = { "Smartphone", "Cellphone", ], + "Drives & Storage": [ + "All DataStorage", + "HardDrives", + "SolidStageDrive", + ], } COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer'] MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"] MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"] +STORAGE = ["HardDrive", "SolidStateDrive"] class AdvancedSearchForm(FlaskForm): @@ -175,9 +181,16 @@ class FilterForm(FlaskForm): elif "All Mobile" == self.device_type: filter_type = MOBILE + elif "All DataStorage" == self.device_type: + filter_type = STORAGE + if filter_type: self.devices = self.devices.filter(Device.type.in_(filter_type)) + # if self.device_type in STORAGE + ["All DataStorage"]: + # import pdb; pdb.set_trace() + # self.devices = self.devices.filter(Component.parent_id.is_(None)) + return self.devices.order_by(Device.updated.desc()) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 2132f7c7..22686f84 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -357,6 +357,7 @@ {% for dev in devices %} + {% if not dev.parent_id or dev.parent.placeholder.kangaroo %} + {% endif %} {% endfor %} diff --git a/tests/test_basic.py b/tests/test_basic.py index 43c93d3c..3d7a0ad8 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -105,6 +105,7 @@ def test_api_docs(client: Client): '/users/logout/', '/versions/', '/workbench/', + '/workbench/erasure_host/{id}/', } assert docs['info'] == {'title': 'Devicehub', 'version': '0.2'} assert docs['components']['securitySchemes']['bearerAuth'] == { diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 27672c01..2dcd7100 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -2438,3 +2438,99 @@ def test_bug_3831_documents(user3: UserClientFlask): uri = f'/inventory/lot/{lot_id}/trade-document/add/' # body, status = user3.post(uri, data=data, content_type="multipart/form-data") # assert status == '200 OK' + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_hdd_filter(user3: UserClientFlask): + create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + + hdds = Device.query.filter_by(type='HardDrive').all() + for hdd in hdds: + hdd.parent = None + db.session.commit() + + csrf = generate_csrf() + uri = f'/inventory/device/?filter=All+DataStorage&csrf_token={csrf}' + body, status = user3.get(uri) + + assert status == '200 OK' + for hdd in hdds: + assert hdd.dhid in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_add_kangaroo(user3: UserClientFlask): + create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + + body, status = user3.get('/workbench/') + + assert status == '200 OK' + + pc = Device.query.filter_by(type='Laptop').first() + data = { + 'csrf_token': generate_csrf(), + 'phid': pc.phid(), + } + + body, status = user3.post('/workbench/', data=data) + assert status == '200 OK' + assert pc.phid() in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_drop_kangaroo(user3: UserClientFlask): + create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + pc = Device.query.filter_by(type='Laptop').first() + phid = 'AAA' + pc.placeholder.phid = phid + db.session.commit() + + body, status = user3.get('/workbench/') + assert phid not in body + + data = { + 'csrf_token': generate_csrf(), + 'phid': phid, + } + + body, status = user3.post('/workbench/', data=data) + assert status == '200 OK' + assert phid in body + + placeholder_id = pc.placeholder.id + uri = f'/workbench/erasure_host/{placeholder_id}/' + body, status = user3.get(uri) + assert status == '200 OK' + assert phid not in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_filter_hdd_in_kangaroo(user3: UserClientFlask): + create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + csrf = generate_csrf() + uri = f'/inventory/device/?filter=All+DataStorage&csrf_token={csrf}' + body, status = user3.get(uri) + + assert status == '200 OK' + for hdd in Device.query.filter_by(type='HardDrive').all(): + assert hdd.dhid not in body + + user3.get('/workbench/') + pc = Device.query.filter_by(type='Laptop').first() + data = { + 'csrf_token': generate_csrf(), + 'phid': pc.phid(), + } + user3.post('/workbench/', data=data) + + csrf = generate_csrf() + uri = f'/inventory/device/?filter=All+DataStorage&csrf_token={csrf}' + body, status = user3.get(uri) + + assert status == '200 OK' + for hdd in Device.query.filter_by(type='HardDrive').all(): + assert hdd.dhid in body From a54942a8a8c5d59b7b4b19af5f26047e1fc2b894 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 14 Oct 2022 15:47:54 +0200 Subject: [PATCH 53/62] add kangaroo form --- ereuse_devicehub/workbench/forms.py | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 ereuse_devicehub/workbench/forms.py diff --git a/ereuse_devicehub/workbench/forms.py b/ereuse_devicehub/workbench/forms.py new file mode 100644 index 00000000..648f5ceb --- /dev/null +++ b/ereuse_devicehub/workbench/forms.py @@ -0,0 +1,47 @@ +from flask import g +from flask_wtf import FlaskForm +from wtforms import StringField, validators + +from ereuse_devicehub.db import db +from ereuse_devicehub.resources.device.models import Placeholder + + +class KangarooForm(FlaskForm): + phid = StringField('Phid', [validators.length(min=1)]) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.placeholder = None + self.kangaroos = Placeholder.query.filter( + Placeholder.kangaroo.is_(True) + ).filter(Placeholder.owner_id == g.user.id) + + def validate(self, extra_validators=None): + is_valid = super().validate(extra_validators) + if not is_valid: + return False + + if not self.placeholder: + self.placeholder = ( + Placeholder.query.filter(Placeholder.phid == self.phid.data) + .filter(Placeholder.owner_id == g.user.id) + .first() + ) + if self.placeholder: + if self.placeholder.status not in ['Snapshot', 'Twin']: + self.placeholder = None + + if not self.placeholder: + self.phid.errors = ["Device not exist"] + return False + + return True + + def save(self): + if not self.placeholder or self.placeholder.kangaroo: + return + + self.placeholder.kangaroo = True + db.session.commit() + return self.placeholder From c9a3dbf6c64051ca43fd6aaad0196c321b744cdd Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 14 Oct 2022 16:39:38 +0200 Subject: [PATCH 54/62] fix template device list --- ereuse_devicehub/templates/inventory/device_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 22686f84..3bb32fc5 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -357,7 +357,7 @@ {% for dev in devices %} - {% if not dev.parent_id or dev.parent.placeholder.kangaroo %} + {% if dev.placeholder and (not dev.parent_id or dev.parent.placeholder.kangaroo) %} Date: Mon, 17 Oct 2022 11:35:14 +0200 Subject: [PATCH 55/62] second version of layout for settings --- .../templates/workbench/settings.html | 193 ++++++------------ ereuse_devicehub/workbench/__init__.py | 4 +- ereuse_devicehub/workbench/views.py | 6 +- 3 files changed, 63 insertions(+), 140 deletions(-) diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index c302f0b7..c4c31139 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -68,7 +68,7 @@
    -
    +
    @@ -82,122 +82,13 @@
    -
    +
    -
    -
    - - {% if demo %}
    -
    - -
    -
    - -
    -
    Usody Metadata Snapshots
    -
    - -
    -

    - Download Checksum: SHA512SUM | - Help

    -

    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - - {% else %} - {% for d, v in iso.items() %} - {% if d != 'demo' %} -
    -
    - -
    -
    - -
    -
    Workbench {{ d }}
    - {% if d == 'v14' %} -
    - -
    -

    - Settings for basic data erasure. | Help -

    -
    -
    -
    - -
    -

    - Settings for advanced data erasure through sectors and hidden areas. Guarantee of data removal. | Help -

    -
    -
    - {% else %} -
    - -
    -

    - Settings for register devices. -

    -
    -
    - {% endif %} - {% if iso %} -
    -
    - -
    -

    - {{ v.iso }} -

    -

    - Download Checksum: SHA512SUM | - Help

    -

    -
    -
    - {% endif %} -
    -
    - -
    - -
    -
    -
    -
    - {% endif %} - {% endfor %} - - {% endif %} - - -
    @@ -208,12 +99,14 @@

    - Download Checksum: SHA512SUM | - Help

    + Download Checksum: SHA512SUM | + Help

    @@ -222,16 +115,22 @@
    -

    HW and Smart test

    - Download settings file +
    Hardware Capture
    +

    HW Metadata Capture and Smart test

    + Download settings file
    -
    + + + + {% if not demo %} +
    +
    @@ -240,12 +139,14 @@

    - Download Checksum: SHA512SUM | - Help

    + Download Checksum: SHA512SUM | + Help

    @@ -254,14 +155,14 @@
    +
    Basic Erasure

    - Settings for basic data erasure using shred fast non-100% secured way of erasing data storage. -

    -

    - One overwriting round using a randomn pattern. - Compliant with British HMG Infosec Standard 5, - Baseline Standard + Settings for basic data erasure using shred Linux command. + A software-based fast non-100%-secured way of erasing data storage.

    +

    + Performs 1 pass overwriting one round using all zeros. Compliant with NIST SP-800-88 +

    @@ -269,20 +170,42 @@
    +
    +
    Baseline Secure Erasure

    - Settings for basic data erasure using shred fast non-100% secured way of erasing data storage. + Settings for advanced data erasure using badblocks Linux software. + A secured-way of erasing data storages, erase hidden areas, checking the erase sector by sector.

    - Settings for basic data erasure using shred fast non-100% secured way of erasing data storage. + Performs 1 pass overwriting each sector with zeros and a final verification. + Compliant with HMG Infosec Standard 5 Baseline. +

    + + Download settings file + +
    +
    + +
    +
    +
    Enhanced Secure Erasure
    +

    + Settings for advanced data erasure using badblocks Linux software. + A secured-way of erasing data storages, erase hidden areas, checking the erase sector by sector.

    - One overwriting round using a randomn pattern. - Compliant with British HMG Infosec Standard 5, - Baseline Standard -

    - Download settings file + Performs 3 passes overwriting every sector with zeros and ones, and final verification. + Compliant with HMG Infosec Standard 5 Enhanced. +

    + + Download settings file +
    @@ -291,11 +214,9 @@
    - -
    -
    + {% endif %} {% endblock main %} diff --git a/ereuse_devicehub/workbench/__init__.py b/ereuse_devicehub/workbench/__init__.py index bf0e2926..bd4530a4 100644 --- a/ereuse_devicehub/workbench/__init__.py +++ b/ereuse_devicehub/workbench/__init__.py @@ -3,11 +3,11 @@ isos = { 'iso': "USODY_2022.8.0-Demo.iso", 'url': 'http://releases.usody.com/demo/', }, - "2022": { + "register": { 'iso': "USODY_2022.8.0-beta.iso", 'url': 'http://releases.usody.com/2022/', }, - "v14": { + "erease": { 'iso': "USODY_14.0.0.iso", 'url': 'http://releases.usody.com/v14/', }, diff --git a/ereuse_devicehub/workbench/views.py b/ereuse_devicehub/workbench/views.py index 60c34fd3..28842826 100644 --- a/ereuse_devicehub/workbench/views.py +++ b/ereuse_devicehub/workbench/views.py @@ -23,7 +23,7 @@ class SettingsView(GenericMixin): decorators = [login_required] methods = ['GET', 'POST'] template_name = 'workbench/settings.html' - page_title = "Workbench" + page_title = "Snapshots" def dispatch_request(self): self.get_context() @@ -32,7 +32,9 @@ class SettingsView(GenericMixin): { 'page_title': self.page_title, 'demo': g.user.email == app.config['EMAIL_DEMO'], - 'iso': isos, + 'iso_demo': isos['demo'], + 'iso_register': isos['register'], + 'iso_erease': isos['erease'], 'form': form_kangaroo, } ) From fc297ee5f8328d4d36921088c62dc925c1b7f444 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 17 Oct 2022 18:07:16 +0200 Subject: [PATCH 56/62] add is_server_erase in snapshot action --- .../d65745749e34_add_is_server_erase.py | 34 +++++++++++++++++++ ereuse_devicehub/resources/action/models.py | 1 + .../resources/action/views/snapshot.py | 7 ++++ tests/test_render_2_0.py | 33 ++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 ereuse_devicehub/migrations/versions/d65745749e34_add_is_server_erase.py diff --git a/ereuse_devicehub/migrations/versions/d65745749e34_add_is_server_erase.py b/ereuse_devicehub/migrations/versions/d65745749e34_add_is_server_erase.py new file mode 100644 index 00000000..20d001db --- /dev/null +++ b/ereuse_devicehub/migrations/versions/d65745749e34_add_is_server_erase.py @@ -0,0 +1,34 @@ +"""add is_server_erase + +Revision ID: d65745749e34 +Revises: a13ed6ad0e3e +Create Date: 2022-10-17 13:20:29.875274 + +""" +import sqlalchemy as sa +from alembic import context, op + +# revision identifiers, used by Alembic. +revision = 'd65745749e34' +down_revision = 'a13ed6ad0e3e' +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('is_server_erase', sa.Boolean(), nullable=True), + schema=f'{get_inv()}', + ) + + +def downgrade(): + op.drop_column('snapshot', 'is_server_erase', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 496eed34..87a9d392 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) + is_server_erase = Column(Boolean(), nullable=True) def get_last_lifetimes(self): """We get the lifetime and serial_number of the first disk""" diff --git a/ereuse_devicehub/resources/action/views/snapshot.py b/ereuse_devicehub/resources/action/views/snapshot.py index bca48f84..dd967fee 100644 --- a/ereuse_devicehub/resources/action/views/snapshot.py +++ b/ereuse_devicehub/resources/action/views/snapshot.py @@ -115,8 +115,15 @@ class SnapshotMixin: if snapshot.device.hid is None: snapshot.severity = Severity.Warning + self.is_server_erase(snapshot) + return snapshot + def is_server_erase(self, snapshot): + if snapshot.device.binding: + if snapshot.device.binding.kangaroo: + snapshot.is_server_erase = True + def get_old_smbios_version(self, debug): capabilities = debug.get('lshw', {}).get('capabilities', {}) for x in capabilities.values(): diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 2dcd7100..dbe3c851 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -2534,3 +2534,36 @@ def test_filter_hdd_in_kangaroo(user3: UserClientFlask): assert status == '200 OK' for hdd in Device.query.filter_by(type='HardDrive').all(): assert hdd.dhid in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_snapshot_is_server_erase(user3: UserClientFlask): + snapshot = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + + user3.get('/workbench/') + data = { + 'csrf_token': generate_csrf(), + 'phid': snapshot.device.phid(), + } + user3.post('/workbench/', data=data) + + uri = '/inventory/upload-snapshot/' + file_name = 'real-eee-1001pxd.snapshot.12' + snapshot_json = conftest.yaml2json(file_name) + snapshot_json['uuid'] = 'c058e8d2-fb92-47cb-a4b7-522b75561136' + b_snapshot = bytes(json.dumps(snapshot_json), 'utf-8') + file_snap = (BytesIO(b_snapshot), file_name) + user3.get(uri) + + data = { + 'snapshot': file_snap, + 'csrf_token': generate_csrf(), + } + user3.post(uri, data=data, content_type="multipart/form-data") + snapshot2 = Snapshot.query.filter_by(uuid=snapshot_json['uuid']).one() + + assert not snapshot.is_server_erase + assert snapshot2.is_server_erase + assert snapshot in snapshot.device.actions + assert snapshot2 in snapshot.device.actions From fb46f48461d9d005cda018199e2c318d1a1f41af Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 17 Oct 2022 18:25:12 +0200 Subject: [PATCH 57/62] fix text settings setup --- .../templates/workbench/settings.html | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index c4c31139..1a051e76 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -96,6 +96,10 @@
    Usody Metadata Snapshots
    +

    + A certified collection of hardware details and testing reports
    + Desktops, Servers and Laptops +

    @@ -115,8 +119,8 @@
    -
    Hardware Capture
    -

    HW Metadata Capture and Smart test

    +
    Basic Metadata
    +

    Settings for basic hardware metadata collection and hard drive smart Test reports.

    Download settings file
    @@ -136,6 +140,10 @@
    Usody Erasure Snapshots
    +

    + A certified data erasure software to irreversibly removing data sored on hard drives.
    + Desktops, Servers and Laptops +

    From 5d5ed0c5526c2392f488b9ceb7936d7dea03f502 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 17 Oct 2022 19:01:53 +0200 Subject: [PATCH 58/62] add version of isos --- ereuse_devicehub/templates/workbench/settings.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index 1a051e76..edcf93b6 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -95,7 +95,7 @@
    -
    Usody Metadata Snapshots
    +
    Usody Hardware Metadata 2022

    A certified collection of hardware details and testing reports
    Desktops, Servers and Laptops @@ -139,7 +139,7 @@

    -
    Usody Erasure Snapshots
    +
    Usody Data Erasure v14

    A certified data erasure software to irreversibly removing data sored on hard drives.
    Desktops, Servers and Laptops From fc86181311a97f669f4dc7baf5fd457776fd94cb Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 18 Oct 2022 09:49:54 +0200 Subject: [PATCH 59/62] change snapshot id for snapshot uuid --- ereuse_devicehub/templates/inventory/snapshots_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/inventory/snapshots_list.html b/ereuse_devicehub/templates/inventory/snapshots_list.html index d41f239f..4c35f44e 100644 --- a/ereuse_devicehub/templates/inventory/snapshots_list.html +++ b/ereuse_devicehub/templates/inventory/snapshots_list.html @@ -26,7 +26,7 @@ SID - Snapshot id + Snapshot UUID Version DHID System UUID From 106ab2f6502408709ad0c39da1934a4c3488ad28 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 18 Oct 2022 11:18:50 +0200 Subject: [PATCH 60/62] add tests and redirect from abstract device to twin page --- ereuse_devicehub/resources/device/views.py | 5 ++++- tests/test_render_2_0.py | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/resources/device/views.py b/ereuse_devicehub/resources/device/views.py index e6e158a4..6896089c 100644 --- a/ereuse_devicehub/resources/device/views.py +++ b/ereuse_devicehub/resources/device/views.py @@ -137,7 +137,10 @@ class DeviceView(View): return self.one_private(id) def one_public(self, id: int): - device = Device.query.filter_by(devicehub_id=id, active=True).one() + devices = Device.query.filter_by(devicehub_id=id, active=True).all() + if not devices: + devices = [Device.query.filter_by(dhid_bk=id, active=True).one()] + device = devices[0] abstract = None if device.binding: return flask.redirect(device.public_link) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index dbe3c851..bfb248f2 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -1675,6 +1675,7 @@ def test_export_lots(user3: UserClientFlask): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_export_snapshot_json(user3: UserClientFlask): + # ?? file_name = 'real-eee-1001pxd.snapshot.13.json' snap = create_device(user3, file_name) @@ -2158,9 +2159,16 @@ def test_manual_binding(user3: UserClientFlask): # check new structure assert dev_wb.binding.phid == '1' assert dev_wb.binding.device == dev + assert dev_wb.phid() == dev.phid() + assert dev_wb.is_abstract() == dev.is_abstract() == 'Twin' + # assert dev_wb. assert Placeholder.query.filter_by(id=old_placeholder.id).first() is None assert Device.query.filter_by(id=old_placeholder.device.id).first() is None + body_real, status = user3.get(f'/devices/{dhid_real}') + body_abstract, status = user3.get(f'/devices/{dhid_abstract}') + assert body_real == body_abstract + @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) From 2a32ba47b891fa5a421c12798571452528db7990 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 18 Oct 2022 13:48:03 +0200 Subject: [PATCH 61/62] fix components placeholder in unbinding --- ereuse_devicehub/inventory/views.py | 11 ++++++++--- tests/test_render_2_0.py | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 0267db00..9682e0c1 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -382,11 +382,13 @@ class UnBindingView(GenericMixin): return flask.render_template(self.template_name, **self.context) def clone_device(self, device): - if device.binding.is_abstract: + if device.binding and device.binding.is_abstract: return - kangaroo = device.binding.kangaroo - device.binding.kangaroo = False + kangaroo = False + if device.binding: + kangaroo = device.binding.kangaroo + device.binding.kangaroo = False dict_device = copy.copy(device.__dict__) dict_device.pop('_sa_instance_state') @@ -406,6 +408,9 @@ class UnBindingView(GenericMixin): for c in device.components: if c.binding: c.binding.device.parent = new_device + else: + new_c = self.clone_device(c) + new_c.parent = new_device placeholder = Placeholder( device=new_device, binding=device, is_abstract=True, kangaroo=kangaroo diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index dbe3c851..a2332842 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -2247,9 +2247,11 @@ def test_unbinding(user3: UserClientFlask): user3.get(uri) # action binding + assert Placeholder.query.count() == 11 assert dev.placeholder.binding is None user3.post(uri, data={}) assert dev.placeholder.binding == dev_wb + assert Placeholder.query.count() == 1 dhid = dev.dhid # action unbinding @@ -2273,6 +2275,7 @@ def test_unbinding(user3: UserClientFlask): assert Device.query.filter_by(id=dev_wb.binding.device.id).first() assert Device.query.filter_by(id=dev.id).first() assert Placeholder.query.filter_by(id=dev.placeholder.id).first() + assert Placeholder.query.count() == 11 @pytest.mark.mvp From c8a4b16667bd1591d84bc91cf9db123969b44682 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 18 Oct 2022 17:01:38 +0200 Subject: [PATCH 62/62] CHANGELOG.md and change version --- CHANGELOG.md | 16 ++++++++++++++++ ereuse_devicehub/__init__.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2be69eb7..2d69d4a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ ml). ## testing +## [2.4.2] - 2022-10-18 +- [added] #373 Enhancement - UX Lots. +- [added] #377 add prefix in lots in device list. +- [added] #378 add new button transfer. +- [added] #381 add servers erase and show storage disk in list of device. +- [added] #383 new setup page and add server_erase in placeholder. +- [added] #384 add redirect snapshot to twin public page. +- [changed] #371 changes phid. +- [changed] #372 remove logo. +- [changed] #374 changes links UI management and Data Storage Erasure. +- [changed] #375 changes columns in snapshot logs. +- [changed] #379 changes representation date times. +- [fixed] #380 fix layout print label. +- [fixed] #382 fix template device list. +- [fixed] #385 components in unbinding process. + ## [2.4.1] - 2022-10-05 - [added] #365 Manage dependencies using pip-tools. - [added] #368 add migrations of monitors and mobiles. diff --git a/ereuse_devicehub/__init__.py b/ereuse_devicehub/__init__.py index 54499df3..60be088d 100644 --- a/ereuse_devicehub/__init__.py +++ b/ereuse_devicehub/__init__.py @@ -1 +1 @@ -__version__ = "2.4.1" +__version__ = "2.4.2"