From 0b1f856ce6b8a69d9df86fd4f2033041baed0d2b Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 11 Mar 2022 19:16:39 +0100 Subject: [PATCH 01/27] modify Changelog.md --- CHANGELOG.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83520944..96d64604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.ht ml). -## master - [1.0.12-beta] -## testing - [1.0.13-beta] - -## [1.0.13-beta] +## [2.0.0-alpha] +- [changes] #209 adding a new device in a lot if it is created from a lot +- [addend] #208 render from backend filter for type of devices in the general list +- [bugfix] #206 fix 2 bugs about visibility devices when you are not the owner +- [addend] #205 ux improvements +- [addend] #204 render from backend export files +- [addend] #203 render from backend Trade action +- [addend] #201 render from backend Data Wipe action +- [addend] #196 render from backend action system +- [addend] #195 render from backend tags system +- [addend] #193 render from backend devices and lots +- [changes] #191 pass to drop teal and use the pure flask and use render from flask ## [1.0.12-beta] - [changes] #187 now is possible duplicate slots of RAM. From 4e35660080ccb8fc6351bc7945b3a19ba4bccbfc Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 14 Mar 2022 13:32:39 +0100 Subject: [PATCH 02/27] change conftest --- tests/conftest.py | 61 +++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 991374ba..b85b34fd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,15 +1,15 @@ import io import uuid -import jwt -import ereuse_utils from contextlib import redirect_stdout from datetime import datetime from pathlib import Path -from decouple import config import boltons.urlutils +import ereuse_utils +import jwt import pytest import yaml +from decouple import config from psycopg2 import IntegrityError from sqlalchemy.exc import ProgrammingError @@ -17,11 +17,12 @@ from ereuse_devicehub.client import Client, UserClient from ereuse_devicehub.config import DevicehubConfig from ereuse_devicehub.db import db from ereuse_devicehub.devicehub import Devicehub +from ereuse_devicehub.inventory.views import devices from ereuse_devicehub.resources.agent.models import Person -from ereuse_devicehub.resources.tag import Tag -from ereuse_devicehub.resources.user.models import User -from ereuse_devicehub.resources.user.models import Session from ereuse_devicehub.resources.enums import SessionType +from ereuse_devicehub.resources.tag import Tag +from ereuse_devicehub.resources.user.models import Session, User +from ereuse_devicehub.views import core STARTT = datetime(year=2000, month=1, day=1, hour=1) """A dummy starting time to use in tests.""" @@ -50,6 +51,19 @@ def config(): @pytest.fixture(scope='session') def _app(config: TestConfig) -> Devicehub: + # dh_config = DevicehubConfig() + # config = TestConfig(dh_config) + app = Devicehub(inventory='test', config=config, db=db) + app.register_blueprint(core) + app.register_blueprint(devices) + app.config["SQLALCHEMY_RECORD_QUERIES"] = True + app.config['PROFILE'] = True + # app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30]) + return app + + +@pytest.fixture(scope='session') +def _app2(config: TestConfig) -> Devicehub: return Devicehub(inventory='test', config=config, db=db) @@ -61,13 +75,15 @@ def app(request, _app: Devicehub) -> Devicehub: db.drop_all() def _init(): - _app.init_db(name='Test Inventory', - org_name='FooOrg', - org_id='foo-org-id', - tag_url=boltons.urlutils.URL('https://example.com'), - tag_token=uuid.UUID('52dacef0-6bcb-4919-bfed-f10d2c96ecee'), - erase=False, - common=True) + _app.init_db( + name='Test Inventory', + org_name='FooOrg', + org_id='foo-org-id', + tag_url=boltons.urlutils.URL('https://example.com'), + tag_token=uuid.UUID('52dacef0-6bcb-4919-bfed-f10d2c96ecee'), + erase=False, + common=True, + ) with _app.app_context(): try: @@ -99,7 +115,9 @@ def user(app: Devicehub) -> UserClient: with app.app_context(): password = 'foo' user = create_user(password=password) - client = UserClient(app, user.email, password, response_wrapper=app.response_class) + client = UserClient( + app, user.email, password, response_wrapper=app.response_class + ) client.login() return client @@ -111,7 +129,9 @@ def user2(app: Devicehub) -> UserClient: password = 'foo' email = 'foo2@foo.com' user = create_user(email=email, password=password) - client = UserClient(app, user.email, password, response_wrapper=app.response_class) + client = UserClient( + app, user.email, password, response_wrapper=app.response_class + ) client.login() return client @@ -145,16 +165,13 @@ def auth_app_context(app: Devicehub): def json_encode(dev: str) -> dict: """Encode json.""" data = {"type": "Snapshot"} - data['data'] = jwt.encode(dev, - P, - algorithm="HS256", - json_encoder=ereuse_utils.JSONEncoder + data['data'] = jwt.encode( + dev, P, algorithm="HS256", json_encoder=ereuse_utils.JSONEncoder ) return data - def yaml2json(name: str) -> dict: """Opens and parses a YAML file from the ``files`` subdir.""" with Path(__file__).parent.joinpath('files').joinpath(name + '.yaml').open() as f: @@ -168,7 +185,9 @@ def file(name: str) -> dict: def file_workbench(name: str) -> dict: """Opens and parses a YAML file from the ``files`` subdir.""" - with Path(__file__).parent.joinpath('workbench_files').joinpath(name + '.json').open() as f: + with Path(__file__).parent.joinpath('workbench_files').joinpath( + name + '.json' + ).open() as f: return yaml.load(f) From 071e1452645fe8b7e28c213b6ceb083daea9fedc Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 15 Mar 2022 11:17:05 +0100 Subject: [PATCH 03/27] Add body in the check of request --- tests/test_render_2_0.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/test_render_2_0.py diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py new file mode 100644 index 00000000..45c79d30 --- /dev/null +++ b/tests/test_render_2_0.py @@ -0,0 +1,28 @@ +# import pytest +from flask.testing import FlaskClient + +from ereuse_devicehub.client import UserClient +from ereuse_devicehub.devicehub import Devicehub + +# from tests import conftest + + +# @pytest.mark.mvp +# @pytest.mark.usefixtures() +# def test_create_application(client: FlaskClient, mocker): +# @pytest.mark.usefixtures(conftest.app_context.__name__) +def test_login(user: UserClient, app: Devicehub): + """Checks a simple login""" + + client = FlaskClient(app, use_cookies=True, response_wrapper=app.response_class) + body, status, headers = client.get('/login/') + body = next(body).decode("utf-8") + assert status == '200 OK' + assert "Login to Your Account" in body + + data = {'email': user.email, 'password': 'foo', "remember": False} + body, status, headers = client.post('/login/', data=data) + + body = next(body).decode("utf-8") + assert status == '200 OK' + assert "Login to Your Account" not in body From 39dc4d86354793853924915276f41d0f0fe42229 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 15 Mar 2022 19:57:54 +0100 Subject: [PATCH 04/27] add one example that how use client validate --- tests/test_render_2_0.py | 43 +++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 45c79d30..0a3a19ad 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -1,28 +1,53 @@ -# import pytest +import pytest from flask.testing import FlaskClient +from flask_wtf.csrf import generate_csrf from ereuse_devicehub.client import UserClient from ereuse_devicehub.devicehub import Devicehub - -# from tests import conftest +from tests import conftest -# @pytest.mark.mvp +@pytest.mark.mvp # @pytest.mark.usefixtures() -# def test_create_application(client: FlaskClient, mocker): -# @pytest.mark.usefixtures(conftest.app_context.__name__) +@pytest.mark.usefixtures(conftest.app_context.__name__) def test_login(user: UserClient, app: Devicehub): """Checks a simple login""" - client = FlaskClient(app, use_cookies=True, response_wrapper=app.response_class) + client = FlaskClient(app, use_cookies=True) + body, status, headers = client.get('/login/') body = next(body).decode("utf-8") assert status == '200 OK' assert "Login to Your Account" in body - data = {'email': user.email, 'password': 'foo', "remember": False} - body, status, headers = client.post('/login/', data=data) + data = { + 'email': user.email, + 'password': 'foo', + 'remember': False, + 'csrf_token': generate_csrf(), + } + body, status, headers = client.post('/login/', data=data, follow_redirects=True) body = next(body).decode("utf-8") assert status == '200 OK' assert "Login to Your Account" not in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_inventory(user: UserClient, app: Devicehub): + client = FlaskClient(app, use_cookies=True) + client.get('/login/') + data = { + 'email': user.email, + 'password': 'foo', + 'remember': False, + 'csrf_token': generate_csrf(), + } + body, status, headers = client.post('/login/', data=data, follow_redirects=True) + body, status, headers = client.get('/inventory/device/', headers=headers) + + body = next(body).decode("utf-8") + assert status == '200 OK' + # import pdb; pdb.set_trace() + assert "Unassgined" in body From ec99ad22b5957ab6af3f0938864b088ac8c75d9d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 17 Mar 2022 11:50:57 +0100 Subject: [PATCH 05/27] . --- ereuse_devicehub/client.py | 60 ++++++++++++++++++++++++++++++++++++++ tests/conftest.py | 27 ++++++++++++++++- tests/test_render_2_0.py | 34 ++++++++++++--------- 3 files changed, 107 insertions(+), 14 deletions(-) diff --git a/ereuse_devicehub/client.py b/ereuse_devicehub/client.py index 3224dfd3..3ddf94ff 100644 --- a/ereuse_devicehub/client.py +++ b/ereuse_devicehub/client.py @@ -1,5 +1,7 @@ from inspect import isclass from typing import Dict, Iterable, Type, Union +from flask.testing import FlaskClient +from flask_wtf.csrf import generate_csrf from ereuse_utils.test import JSON, Res from teal.client import Client as TealClient, Query, Status @@ -156,3 +158,61 @@ class UserClient(Client): response = super().login(self.email, self.password) self.user = response[0] return response + + +class UserClientFlask: + + def __init__(self, application, + email: str, + password: str, + response_wrapper=None, + use_cookies=True, + follow_redirects=True): + self.email = email + self.password = password + self.follow_redirects = follow_redirects + self.user = None + + self.client = FlaskClient(application, use_cookies=use_cookies) + self.client.get('/login/') + + data = { + 'email': email, + 'password': password, + 'csrf_token': generate_csrf(), + } + body, status, headers = self.client.post('/login/', data=data, follow_redirects=True) + self.headers = headers + body = next(body).decode("utf-8") + assert "Unassgined" in body + + def get(self, + uri='', + data=None, + follow_redirects=True, + **kw): + + body, status, headers = self.client.get( + uri, + data=data, + follow_redirects=follow_redirects, + headers=self.headers + ) + body = next(body).decode("utf-8") + return (body, status) + + def post(self, + uri='', + data=None, + follow_redirects=True, + **kw): + + import pdb; pdb.set_trace() + body, status, headers = self.client.post( + uri, + data=data, + follow_redirects=follow_redirects, + headers=self.headers + ) + body = next(body).decode("utf-8") + return (body, status) diff --git a/tests/conftest.py b/tests/conftest.py index b85b34fd..e571e96b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,7 +13,7 @@ from decouple import config from psycopg2 import IntegrityError from sqlalchemy.exc import ProgrammingError -from ereuse_devicehub.client import Client, UserClient +from ereuse_devicehub.client import Client, UserClient, UserClientFlask from ereuse_devicehub.config import DevicehubConfig from ereuse_devicehub.db import db from ereuse_devicehub.devicehub import Devicehub @@ -136,6 +136,31 @@ def user2(app: Devicehub) -> UserClient: return client +@pytest.fixture() +def user3(app: Devicehub) -> UserClientFlask: + """Gets a client with a logged-in dummy user.""" + with app.app_context(): + password = 'foo' + user = create_user(password=password) + client = UserClientFlask( + app, user.email, password + ) + return client + + +@pytest.fixture() +def user4(app: Devicehub) -> UserClient: + """Gets a client with a logged-in dummy user.""" + with app.app_context(): + password = 'foo' + email = 'foo2@foo.com' + user = create_user(email=email, password=password) + client = UserClientFlask( + app, user.email, password + ) + return client + + def create_user(email='foo@foo.com', password='foo') -> User: user = User(email=email, password=password) user.individuals.add(Person(name='Timmy')) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 0a3a19ad..3d3775ed 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -2,7 +2,7 @@ import pytest from flask.testing import FlaskClient from flask_wtf.csrf import generate_csrf -from ereuse_devicehub.client import UserClient +from ereuse_devicehub.client import UserClient, UserClientFlask from ereuse_devicehub.devicehub import Devicehub from tests import conftest @@ -35,19 +35,27 @@ def test_login(user: UserClient, app: Devicehub): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_inventory(user: UserClient, app: Devicehub): - client = FlaskClient(app, use_cookies=True) - client.get('/login/') +def test_inventory(user3: UserClientFlask): + body, status = user3.get('/inventory/device/') + + assert status == '200 OK' + assert "Unassgined" in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_add_lot(user3: UserClientFlask): + body, status = user3.get('/inventory/lot/add/') + + assert status == '200 OK' + assert "Add a new lot" in body + data = { - 'email': user.email, - 'password': 'foo', - 'remember': False, + 'name': 'lot1', 'csrf_token': generate_csrf(), } - body, status, headers = client.post('/login/', data=data, follow_redirects=True) - body, status, headers = client.get('/inventory/device/', headers=headers) - - body = next(body).decode("utf-8") - assert status == '200 OK' # import pdb; pdb.set_trace() - assert "Unassgined" in body + body, status = user3.post('/inventory/lot/add/', data=data) + + assert status == '200 OK' + assert "lot1" in body From f2ab02804b366b93b0ed4aed1210a9a509f51ef0 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 17 Mar 2022 13:13:15 +0100 Subject: [PATCH 06/27] new userclient from flask instead of teal --- ereuse_devicehub/client.py | 329 +++++++++++++++++++++---------------- tests/conftest.py | 8 +- 2 files changed, 193 insertions(+), 144 deletions(-) diff --git a/ereuse_devicehub/client.py b/ereuse_devicehub/client.py index 3ddf94ff..36d3bd49 100644 --- a/ereuse_devicehub/client.py +++ b/ereuse_devicehub/client.py @@ -1,10 +1,11 @@ from inspect import isclass from typing import Dict, Iterable, Type, Union -from flask.testing import FlaskClient -from flask_wtf.csrf import generate_csrf from ereuse_utils.test import JSON, Res -from teal.client import Client as TealClient, Query, Status +from flask.testing import FlaskClient +from flask_wtf.csrf import generate_csrf +from teal.client import Client as TealClient +from teal.client import Query, Status from werkzeug.exceptions import HTTPException from ereuse_devicehub.resources import models, schemas @@ -15,110 +16,156 @@ ResourceLike = Union[Type[Union[models.Thing, schemas.Thing]], str] class Client(TealClient): """A client suited for Devicehub main usage.""" - def __init__(self, application, - response_wrapper=None, - use_cookies=False, - allow_subdomain_redirects=False): - super().__init__(application, response_wrapper, use_cookies, allow_subdomain_redirects) + def __init__( + self, + application, + response_wrapper=None, + use_cookies=False, + allow_subdomain_redirects=False, + ): + super().__init__( + application, response_wrapper, use_cookies, allow_subdomain_redirects + ) - def open(self, - uri: str, - res: ResourceLike = None, - status: Status = 200, - query: Query = tuple(), - accept=JSON, - content_type=JSON, - item=None, - headers: dict = None, - token: str = None, - **kw) -> Res: + def open( + self, + uri: str, + res: ResourceLike = None, + status: Status = 200, + query: Query = tuple(), + accept=JSON, + content_type=JSON, + item=None, + headers: dict = None, + token: str = None, + **kw, + ) -> Res: if isclass(res) and issubclass(res, (models.Thing, schemas.Thing)): res = res.t - return super().open(uri, res, status, query, accept, content_type, item, headers, token, - **kw) + return super().open( + uri, res, status, query, accept, content_type, item, headers, token, **kw + ) - def get(self, - uri: str = '', - res: ResourceLike = None, - query: Query = tuple(), - status: Status = 200, - item: Union[int, str] = None, - accept: str = JSON, - headers: dict = None, - token: str = None, - **kw) -> Res: + def get( + self, + uri: str = '', + res: ResourceLike = None, + query: Query = tuple(), + status: Status = 200, + item: Union[int, str] = None, + accept: str = JSON, + headers: dict = None, + token: str = None, + **kw, + ) -> Res: return super().get(uri, res, query, status, item, accept, headers, token, **kw) - def post(self, - data: str or dict, - uri: str = '', - res: ResourceLike = None, - query: Query = tuple(), - status: Status = 201, - content_type: str = JSON, - accept: str = JSON, - headers: dict = None, - token: str = None, - **kw) -> Res: - return super().post(data, uri, res, query, status, content_type, accept, headers, token, - **kw) + def post( + self, + data: str or dict, + uri: str = '', + res: ResourceLike = None, + query: Query = tuple(), + status: Status = 201, + content_type: str = JSON, + accept: str = JSON, + headers: dict = None, + token: str = None, + **kw, + ) -> Res: + return super().post( + data, uri, res, query, status, content_type, accept, headers, token, **kw + ) - def patch(self, - data: str or dict, - uri: str = '', - res: ResourceLike = None, - query: Query = tuple(), - item: Union[int, str] = None, - status: Status = 200, - content_type: str = JSON, - accept: str = JSON, - headers: dict = None, - token: str = None, - **kw) -> Res: - return super().patch(data, uri, res, query, item, status, content_type, accept, token, - headers, **kw) + def patch( + self, + data: str or dict, + uri: str = '', + res: ResourceLike = None, + query: Query = tuple(), + item: Union[int, str] = None, + status: Status = 200, + content_type: str = JSON, + accept: str = JSON, + headers: dict = None, + token: str = None, + **kw, + ) -> Res: + return super().patch( + data, + uri, + res, + query, + item, + status, + content_type, + accept, + token, + headers, + **kw, + ) - def put(self, - data: str or dict, - uri: str = '', - res: ResourceLike = None, - query: Query = tuple(), - item: Union[int, str] = None, - status: Status = 201, - content_type: str = JSON, - accept: str = JSON, - headers: dict = None, - token: str = None, - **kw) -> Res: - return super().put(data, uri, res, query, item, status, content_type, accept, token, - headers, **kw) + def put( + self, + data: str or dict, + uri: str = '', + res: ResourceLike = None, + query: Query = tuple(), + item: Union[int, str] = None, + status: Status = 201, + content_type: str = JSON, + accept: str = JSON, + headers: dict = None, + token: str = None, + **kw, + ) -> Res: + return super().put( + data, + uri, + res, + query, + item, + status, + content_type, + accept, + token, + headers, + **kw, + ) - def delete(self, - uri: str = '', - res: ResourceLike = None, - query: Query = tuple(), - status: Status = 204, - item: Union[int, str] = None, - accept: str = JSON, - headers: dict = None, - token: str = None, - **kw) -> Res: - return super().delete(uri, res, query, status, item, accept, headers, token, **kw) + def delete( + self, + uri: str = '', + res: ResourceLike = None, + query: Query = tuple(), + status: Status = 204, + item: Union[int, str] = None, + accept: str = JSON, + headers: dict = None, + token: str = None, + **kw, + ) -> Res: + return super().delete( + uri, res, query, status, item, accept, headers, token, **kw + ) def login(self, email: str, password: str): assert isinstance(email, str) assert isinstance(password, str) - return self.post({'email': email, 'password': password}, '/users/login/', status=200) + return self.post( + {'email': email, 'password': password}, '/users/login/', status=200 + ) - def get_many(self, - res: ResourceLike, - resources: Iterable[Union[dict, int]], - key: str = None, - **kw) -> Iterable[Union[Dict[str, object], str]]: + def get_many( + self, + res: ResourceLike, + resources: Iterable[Union[dict, int]], + key: str = None, + **kw, + ) -> Iterable[Union[Dict[str, object], str]]: """Like :meth:`.get` but with many resources.""" return ( - self.get(res=res, item=r[key] if key else r, **kw)[0] - for r in resources + self.get(res=res, item=r[key] if key else r, **kw)[0] for r in resources ) @@ -128,30 +175,47 @@ class UserClient(Client): It will automatically perform login on the first request. """ - def __init__(self, application, - email: str, - password: str, - response_wrapper=None, - use_cookies=False, - allow_subdomain_redirects=False): - super().__init__(application, response_wrapper, use_cookies, allow_subdomain_redirects) + def __init__( + self, + application, + email: str, + password: str, + response_wrapper=None, + use_cookies=False, + allow_subdomain_redirects=False, + ): + super().__init__( + application, response_wrapper, use_cookies, allow_subdomain_redirects + ) self.email = email # type: str self.password = password # type: str self.user = None # type: dict - def open(self, - uri: str, - res: ResourceLike = None, - status: int or HTTPException = 200, - query: Query = tuple(), - accept=JSON, - content_type=JSON, - item=None, - headers: dict = None, - token: str = None, - **kw) -> Res: - return super().open(uri, res, status, query, accept, content_type, item, headers, - self.user['token'] if self.user else token, **kw) + def open( + self, + uri: str, + res: ResourceLike = None, + status: int or HTTPException = 200, + query: Query = tuple(), + accept=JSON, + content_type=JSON, + item=None, + headers: dict = None, + token: str = None, + **kw, + ) -> Res: + return super().open( + uri, + res, + status, + query, + accept, + content_type, + item, + headers, + self.user['token'] if self.user else token, + **kw, + ) # noinspection PyMethodOverriding def login(self): @@ -161,13 +225,15 @@ class UserClient(Client): class UserClientFlask: - - def __init__(self, application, - email: str, - password: str, - response_wrapper=None, - use_cookies=True, - follow_redirects=True): + def __init__( + self, + application, + email: str, + password: str, + response_wrapper=None, + use_cookies=True, + follow_redirects=True, + ): self.email = email self.password = password self.follow_redirects = follow_redirects @@ -181,38 +247,25 @@ class UserClientFlask: 'password': password, 'csrf_token': generate_csrf(), } - body, status, headers = self.client.post('/login/', data=data, follow_redirects=True) + body, status, headers = self.client.post( + '/login/', data=data, follow_redirects=True + ) self.headers = headers body = next(body).decode("utf-8") assert "Unassgined" in body - def get(self, - uri='', - data=None, - follow_redirects=True, - **kw): + def get(self, uri='', data=None, follow_redirects=True, **kw): body, status, headers = self.client.get( - uri, - data=data, - follow_redirects=follow_redirects, - headers=self.headers + uri, data=data, follow_redirects=follow_redirects, headers=self.headers ) body = next(body).decode("utf-8") return (body, status) - def post(self, - uri='', - data=None, - follow_redirects=True, - **kw): + def post(self, uri='', data=None, follow_redirects=True, **kw): - import pdb; pdb.set_trace() body, status, headers = self.client.post( - uri, - data=data, - follow_redirects=follow_redirects, - headers=self.headers + uri, data=data, follow_redirects=follow_redirects, headers=self.headers ) body = next(body).decode("utf-8") return (body, status) diff --git a/tests/conftest.py b/tests/conftest.py index e571e96b..1bd1ec71 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -142,9 +142,7 @@ def user3(app: Devicehub) -> UserClientFlask: with app.app_context(): password = 'foo' user = create_user(password=password) - client = UserClientFlask( - app, user.email, password - ) + client = UserClientFlask(app, user.email, password) return client @@ -155,9 +153,7 @@ def user4(app: Devicehub) -> UserClient: password = 'foo' email = 'foo2@foo.com' user = create_user(email=email, password=password) - client = UserClientFlask( - app, user.email, password - ) + client = UserClientFlask(app, user.email, password) return client From 25dc0047dc3a0ec7989554d7d0cebf8249592861 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 19 Apr 2022 18:39:05 +0200 Subject: [PATCH 07/27] add tests --- tests/conftest.py | 2 + tests/files/export_devices.csv | 2 + tests/test_render_2_0.py | 191 ++++++++++++++++++++++++++++++++- 3 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 tests/files/export_devices.csv diff --git a/tests/conftest.py b/tests/conftest.py index 1bd1ec71..a8d04506 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,6 +18,7 @@ from ereuse_devicehub.config import DevicehubConfig from ereuse_devicehub.db import db from ereuse_devicehub.devicehub import Devicehub from ereuse_devicehub.inventory.views import devices +from ereuse_devicehub.labels.views import labels from ereuse_devicehub.resources.agent.models import Person from ereuse_devicehub.resources.enums import SessionType from ereuse_devicehub.resources.tag import Tag @@ -56,6 +57,7 @@ def _app(config: TestConfig) -> Devicehub: app = Devicehub(inventory='test', config=config, db=db) app.register_blueprint(core) app.register_blueprint(devices) + app.register_blueprint(labels) app.config["SQLALCHEMY_RECORD_QUERIES"] = True app.config['PROFILE'] = True # app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30]) diff --git a/tests/files/export_devices.csv b/tests/files/export_devices.csv new file mode 100644 index 00000000..586a25e7 --- /dev/null +++ b/tests/files/export_devices.csv @@ -0,0 +1,2 @@ +DHID;DocumentID;Public Link;Lots;Tag 1 Type;Tag 1 ID;Tag 1 Organization;Tag 2 Type;Tag 2 ID;Tag 2 Organization;Tag 3 Type;Tag 3 ID;Tag 3 Organization;Device Hardware ID;Device Type;Device Chassis;Device Serial Number;Device Model;Device Manufacturer;Registered in;Registered (process);Updated in (software);Updated in (web);Physical state;Trading state;Processor;RAM (MB);Data Storage Size (MB);Processor 1;Processor 1 Manufacturer;Processor 1 Model;Processor 1 Serial Number;Processor 1 Number of cores;Processor 1 Speed (GHz);Benchmark Processor 1 (points);Benchmark ProcessorSysbench Processor 1 (points);Processor 2;Processor 2 Manufacturer;Processor 2 Model;Processor 2 Serial Number;Processor 2 Number of cores;Processor 2 Speed (GHz);Benchmark Processor 2 (points);Benchmark ProcessorSysbench Processor 2 (points);RamModule 1;RamModule 1 Manufacturer;RamModule 1 Model;RamModule 1 Serial Number;RamModule 1 Size (MB);RamModule 1 Speed (MHz);RamModule 2;RamModule 2 Manufacturer;RamModule 2 Model;RamModule 2 Serial Number;RamModule 2 Size (MB);RamModule 2 Speed (MHz);RamModule 3;RamModule 3 Manufacturer;RamModule 3 Model;RamModule 3 Serial Number;RamModule 3 Size (MB);RamModule 3 Speed (MHz);RamModule 4;RamModule 4 Manufacturer;RamModule 4 Model;RamModule 4 Serial Number;RamModule 4 Size (MB);RamModule 4 Speed (MHz);DataStorage 1;DataStorage 1 Manufacturer;DataStorage 1 Model;DataStorage 1 Serial Number;DataStorage 1 Size (MB);Erasure DataStorage 1;Erasure DataStorage 1 Serial Number;Erasure DataStorage 1 Size (MB);Erasure DataStorage 1 Software;Erasure DataStorage 1 Result;Erasure DataStorage 1 Certificate URL;Erasure DataStorage 1 Type;Erasure DataStorage 1 Method;Erasure DataStorage 1 Elapsed (hours);Erasure DataStorage 1 Date;Erasure DataStorage 1 Steps;Erasure DataStorage 1 Steps Start Time;Erasure DataStorage 1 Steps End Time;Benchmark DataStorage 1 Read Speed (MB/s);Benchmark DataStorage 1 Writing speed (MB/s);Test DataStorage 1 Software;Test DataStorage 1 Type;Test DataStorage 1 Result;Test DataStorage 1 Power cycle count;Test DataStorage 1 Lifetime (days);Test DataStorage 1 Power on hours;DataStorage 2;DataStorage 2 Manufacturer;DataStorage 2 Model;DataStorage 2 Serial Number;DataStorage 2 Size (MB);Erasure DataStorage 2;Erasure DataStorage 2 Serial Number;Erasure DataStorage 2 Size (MB);Erasure DataStorage 2 Software;Erasure DataStorage 2 Result;Erasure DataStorage 2 Certificate URL;Erasure DataStorage 2 Type;Erasure DataStorage 2 Method;Erasure DataStorage 2 Elapsed (hours);Erasure DataStorage 2 Date;Erasure DataStorage 2 Steps;Erasure DataStorage 2 Steps Start Time;Erasure DataStorage 2 Steps End Time;Benchmark DataStorage 2 Read Speed (MB/s);Benchmark DataStorage 2 Writing speed (MB/s);Test DataStorage 2 Software;Test DataStorage 2 Type;Test DataStorage 2 Result;Test DataStorage 2 Power cycle count;Test DataStorage 2 Lifetime (days);Test DataStorage 2 Power on hours;DataStorage 3;DataStorage 3 Manufacturer;DataStorage 3 Model;DataStorage 3 Serial Number;DataStorage 3 Size (MB);Erasure DataStorage 3;Erasure DataStorage 3 Serial Number;Erasure DataStorage 3 Size (MB);Erasure DataStorage 3 Software;Erasure DataStorage 3 Result;Erasure DataStorage 3 Certificate URL;Erasure DataStorage 3 Type;Erasure DataStorage 3 Method;Erasure DataStorage 3 Elapsed (hours);Erasure DataStorage 3 Date;Erasure DataStorage 3 Steps;Erasure DataStorage 3 Steps Start Time;Erasure DataStorage 3 Steps End Time;Benchmark DataStorage 3 Read Speed (MB/s);Benchmark DataStorage 3 Writing speed (MB/s);Test DataStorage 3 Software;Test DataStorage 3 Type;Test DataStorage 3 Result;Test DataStorage 3 Power cycle count;Test DataStorage 3 Lifetime (days);Test DataStorage 3 Power on hours;DataStorage 4;DataStorage 4 Manufacturer;DataStorage 4 Model;DataStorage 4 Serial Number;DataStorage 4 Size (MB);Erasure DataStorage 4;Erasure DataStorage 4 Serial Number;Erasure DataStorage 4 Size (MB);Erasure DataStorage 4 Software;Erasure DataStorage 4 Result;Erasure DataStorage 4 Certificate URL;Erasure DataStorage 4 Type;Erasure DataStorage 4 Method;Erasure DataStorage 4 Elapsed (hours);Erasure DataStorage 4 Date;Erasure DataStorage 4 Steps;Erasure DataStorage 4 Steps Start Time;Erasure DataStorage 4 Steps End Time;Benchmark DataStorage 4 Read Speed (MB/s);Benchmark DataStorage 4 Writing speed (MB/s);Test DataStorage 4 Software;Test DataStorage 4 Type;Test DataStorage 4 Result;Test DataStorage 4 Power cycle count;Test DataStorage 4 Lifetime (days);Test DataStorage 4 Power on hours;Motherboard 1;Motherboard 1 Manufacturer;Motherboard 1 Model;Motherboard 1 Serial Number;Display 1;Display 1 Manufacturer;Display 1 Model;Display 1 Serial Number;GraphicCard 1;GraphicCard 1 Manufacturer;GraphicCard 1 Model;GraphicCard 1 Serial Number;GraphicCard 1 Memory (MB);GraphicCard 2;GraphicCard 2 Manufacturer;GraphicCard 2 Model;GraphicCard 2 Serial Number;GraphicCard 2 Memory (MB);NetworkAdapter 1;NetworkAdapter 1 Manufacturer;NetworkAdapter 1 Model;NetworkAdapter 1 Serial Number;NetworkAdapter 2;NetworkAdapter 2 Manufacturer;NetworkAdapter 2 Model;NetworkAdapter 2 Serial Number;SoundCard 1;SoundCard 1 Manufacturer;SoundCard 1 Model;SoundCard 1 Serial Number;SoundCard 2;SoundCard 2 Manufacturer;SoundCard 2 Model;SoundCard 2 Serial Number;Device Rate;Device Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range;Price;Benchmark RamSysbench (points) +O48N2;;http://localhost/devices/O48N2;;named;O48N2;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b;Laptop;Netbook;b8oaas048285;1001pxd;asustek computer inc.;Tue Apr 19 18:13:44 2022;Workbench 11.0a2;2022-04-19 18:13:45.018710+02:00;;;;intel atom cpu n455 @ 2.66ghz;1024;238475;Processor 6: model intel atom cpu n455 @ 2.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 2.66ghz;;1;2.667;6666.24;164.0803;;;;;;;;;RamModule 10: model None, S/N None;;;;1024;667;;;;;;;;;;;;;;;;;;;HardDrive 11: model hts54322, S/N e2024242cv86mm;hitachi;hts54322;e2024242cv86mm;238475;harddrive-hitachi-hts54322-e2024242cv86mm;e2024242cv86mm;238475;Workbench 11.0a2;Success;;EraseBasic;Shred;1:16:49;2022-04-19 18:13:44.975393+02:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0a2;Short;Failure;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 12: model 1001pxd, S/N eee0123456720;asustek computer inc.;1001pxd;eee0123456720;;;;;GraphicCard 7: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 4: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c9;NetworkAdapter 5: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7b;SoundCard 8: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;SoundCard 9: model usb 2.0 uvc vga webcam, S/N 0x0001;azurewave;usb 2.0 uvc vga webcam;0x0001;1.75;LOW;1.55;LOW;1.53;LOW;3.76;HIGH;52.50 €;15.7188 diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 3d3775ed..7ebd5695 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -1,14 +1,20 @@ +import csv +import json +from io import BytesIO +from pathlib import Path + import pytest from flask.testing import FlaskClient from flask_wtf.csrf import generate_csrf from ereuse_devicehub.client import UserClient, UserClientFlask from ereuse_devicehub.devicehub import Devicehub +from ereuse_devicehub.resources.action.models import Snapshot +from ereuse_devicehub.resources.lot.models import Lot from tests import conftest @pytest.mark.mvp -# @pytest.mark.usefixtures() @pytest.mark.usefixtures(conftest.app_context.__name__) def test_login(user: UserClient, app: Devicehub): """Checks a simple login""" @@ -47,15 +53,192 @@ def test_inventory(user3: UserClientFlask): def test_add_lot(user3: UserClientFlask): body, status = user3.get('/inventory/lot/add/') + lot_name = "lot1" assert status == '200 OK' assert "Add a new lot" in body + assert lot_name not in body data = { - 'name': 'lot1', + 'name': lot_name, 'csrf_token': generate_csrf(), } - # import pdb; pdb.set_trace() body, status = user3.post('/inventory/lot/add/', data=data) assert status == '200 OK' - assert "lot1" in body + assert lot_name in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_del_lot(user3: UserClientFlask): + body, status = user3.get('/inventory/lot/add/') + + lot_name = "lot1" + assert status == '200 OK' + assert "Add a new lot" in body + assert lot_name not in body + + data = { + 'name': lot_name, + 'csrf_token': generate_csrf(), + } + body, status = user3.post('/inventory/lot/add/', data=data) + + assert status == '200 OK' + assert lot_name in body + + lot = Lot.query.filter_by(name=lot_name).one() + uri = '/inventory/lot/{id}/del/'.format(id=lot.id) + body, status = user3.get(uri) + assert lot_name not in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_update_lot(user3: UserClientFlask): + user3.get('/inventory/lot/add/') + + # Add lot + # import pdb; pdb.set_trace() + data = { + 'name': "lot1", + 'csrf_token': generate_csrf(), + } + user3.post('/inventory/lot/add/', data=data) + + data = { + 'name': "lot2", + 'csrf_token': generate_csrf(), + } + + lot = Lot.query.one() + uri = '/inventory/lot/{uuid}/'.format(uuid=lot.id) + body, status = user3.post(uri, data=data) + + assert status == '200 OK' + assert "lot2" in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_upload_snapshot(user3: UserClientFlask): + uri = '/inventory/upload-snapshot/' + file_name = 'real-eee-1001pxd.snapshot.12.json' + body, status = user3.get(uri) + + assert status == '200 OK' + assert "Select a Snapshot file" in body + + snapshot = conftest.yaml2json(file_name.split(".json")[0]) + b_snapshot = bytes(json.dumps(snapshot), 'utf-8') + file_snap = (BytesIO(b_snapshot), file_name) + + data = { + 'snapshot': file_snap, + 'csrf_token': generate_csrf(), + } + body, status = user3.post(uri, data=data, content_type="multipart/form-data") + + txt = f"{file_name}: Ok" + assert status == '200 OK' + assert txt in body + db_snapthot = Snapshot.query.one() + dev = db_snapthot.device + assert str(db_snapthot.uuid) == snapshot['uuid'] + assert dev.type == 'Laptop' + assert dev.serial_number == 'b8oaas048285' + assert len(dev.actions) == 12 + assert len(dev.components) == 9 + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_inventory_with_device(user3: UserClientFlask): + uri = '/inventory/upload-snapshot/' + file_name = 'real-eee-1001pxd.snapshot.12.json' + snapshot = conftest.yaml2json(file_name.split(".json")[0]) + b_snapshot = bytes(json.dumps(snapshot), '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") + + body, status = user3.get('/inventory/device/') + + assert status == '200 OK' + assert "Unassgined" in body + db_snapthot = Snapshot.query.one() + assert db_snapthot.device.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_inventory_filter(user3: UserClientFlask): + uri = '/inventory/upload-snapshot/' + file_name = 'real-eee-1001pxd.snapshot.12.json' + snapshot = conftest.yaml2json(file_name.split(".json")[0]) + b_snapshot = bytes(json.dumps(snapshot), '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") + + csrf = generate_csrf() + body, status = user3.get(f'/inventory/device/?filter=Laptop&csrf_token={csrf}') + + assert status == '200 OK' + assert "Unassgined" in body + db_snapthot = Snapshot.query.one() + assert db_snapthot.device.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_export_devices(user3: UserClientFlask): + uri = '/inventory/upload-snapshot/' + file_name = 'real-eee-1001pxd.snapshot.12.json' + snapshot = conftest.yaml2json(file_name.split(".json")[0]) + b_snapshot = bytes(json.dumps(snapshot), '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") + + snap = Snapshot.query.one() + uri = "/inventory/export/devices/?ids={id}".format(id=snap.device.devicehub_id) + + body, status = user3.get(uri) + assert status == '200 OK' + + export_csv = [line.split(";") for line in body.split("\n")] + + with Path(__file__).parent.joinpath('files').joinpath( + 'export_devices.csv' + ).open() as csv_file: + obj_csv = csv.reader(csv_file, delimiter=';', quotechar='"') + fixture_csv = list(obj_csv) + + assert fixture_csv[0] == export_csv[0], 'Headers are not equal' + assert ( + fixture_csv[1][:19] == export_csv[1][:19] + ), 'Computer information are not equal' + assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal' + assert ( + fixture_csv[1][22:82] == export_csv[1][22:82] + ), 'Computer information are not equal' + assert fixture_csv[1][83] == export_csv[1][83], 'Computer information are not equal' + assert ( + fixture_csv[1][86:] == export_csv[1][86:] + ), 'Computer information are not equal' From 4a5ad374f8f2e82374219f929cad3e95dc8d02e4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 19 Apr 2022 18:39:42 +0200 Subject: [PATCH 08/27] fix UserClientFlask --- ereuse_devicehub/client.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/ereuse_devicehub/client.py b/ereuse_devicehub/client.py index 36d3bd49..31a9f136 100644 --- a/ereuse_devicehub/client.py +++ b/ereuse_devicehub/client.py @@ -254,7 +254,14 @@ class UserClientFlask: body = next(body).decode("utf-8") assert "Unassgined" in body - def get(self, uri='', data=None, follow_redirects=True, **kw): + def get( + self, + uri='', + data=None, + follow_redirects=True, + content_type='text/html; charset=utf-8', + **kw, + ): body, status, headers = self.client.get( uri, data=data, follow_redirects=follow_redirects, headers=self.headers @@ -262,10 +269,21 @@ class UserClientFlask: body = next(body).decode("utf-8") return (body, status) - def post(self, uri='', data=None, follow_redirects=True, **kw): + def post( + self, + uri='', + data=None, + follow_redirects=True, + content_type='application/x-www-form-urlencoded', + **kw, + ): body, status, headers = self.client.post( - uri, data=data, follow_redirects=follow_redirects, headers=self.headers + uri, + data=data, + follow_redirects=follow_redirects, + headers=self.headers, + content_type=content_type, ) body = next(body).decode("utf-8") return (body, status) From 7c93fc68c5e1a5ce73a1e9e45ef148506408b95f Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 20 Apr 2022 12:35:25 +0200 Subject: [PATCH 09/27] fix encode of response in client --- ereuse_devicehub/client.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/client.py b/ereuse_devicehub/client.py index 31a9f136..aaafb9f3 100644 --- a/ereuse_devicehub/client.py +++ b/ereuse_devicehub/client.py @@ -260,13 +260,15 @@ class UserClientFlask: data=None, follow_redirects=True, content_type='text/html; charset=utf-8', + decode=True, **kw, ): body, status, headers = self.client.get( uri, data=data, follow_redirects=follow_redirects, headers=self.headers ) - body = next(body).decode("utf-8") + if decode: + body = next(body).decode("utf-8") return (body, status) def post( @@ -275,6 +277,7 @@ class UserClientFlask: data=None, follow_redirects=True, content_type='application/x-www-form-urlencoded', + decode=True, **kw, ): @@ -285,5 +288,6 @@ class UserClientFlask: headers=self.headers, content_type=content_type, ) - body = next(body).decode("utf-8") + if decode: + body = next(body).decode("utf-8") return (body, status) From 2fecd1aa60f8d67a511765b3ced61da0c763dcc6 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 20 Apr 2022 12:35:50 +0200 Subject: [PATCH 10/27] add more test --- tests/test_render_2_0.py | 261 +++++++++++++++++++++++++++++++++------ 1 file changed, 220 insertions(+), 41 deletions(-) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 7ebd5695..0b9dc184 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -14,6 +14,22 @@ from ereuse_devicehub.resources.lot.models import Lot from tests import conftest +def create_device(user, file_name): + uri = '/inventory/upload-snapshot/' + snapshot = conftest.yaml2json(file_name.split(".json")[0]) + b_snapshot = bytes(json.dumps(snapshot), 'utf-8') + file_snap = (BytesIO(b_snapshot), file_name) + user.get(uri) + + data = { + 'snapshot': file_snap, + 'csrf_token': generate_csrf(), + } + user.post(uri, data=data, content_type="multipart/form-data") + + return Snapshot.query.one() + + @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_login(user: UserClient, app: Devicehub): @@ -154,69 +170,31 @@ def test_upload_snapshot(user3: UserClientFlask): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_inventory_with_device(user3: UserClientFlask): - uri = '/inventory/upload-snapshot/' - file_name = 'real-eee-1001pxd.snapshot.12.json' - snapshot = conftest.yaml2json(file_name.split(".json")[0]) - b_snapshot = bytes(json.dumps(snapshot), '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") - + db_snapthot = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') body, status = user3.get('/inventory/device/') assert status == '200 OK' assert "Unassgined" in body - db_snapthot = Snapshot.query.one() assert db_snapthot.device.devicehub_id in body @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_inventory_filter(user3: UserClientFlask): - uri = '/inventory/upload-snapshot/' - file_name = 'real-eee-1001pxd.snapshot.12.json' - snapshot = conftest.yaml2json(file_name.split(".json")[0]) - b_snapshot = bytes(json.dumps(snapshot), '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") + db_snapthot = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') csrf = generate_csrf() body, status = user3.get(f'/inventory/device/?filter=Laptop&csrf_token={csrf}') assert status == '200 OK' assert "Unassgined" in body - db_snapthot = Snapshot.query.one() assert db_snapthot.device.devicehub_id in body @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_export_devices(user3: UserClientFlask): - uri = '/inventory/upload-snapshot/' - file_name = 'real-eee-1001pxd.snapshot.12.json' - snapshot = conftest.yaml2json(file_name.split(".json")[0]) - b_snapshot = bytes(json.dumps(snapshot), '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") - - snap = Snapshot.query.one() + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') uri = "/inventory/export/devices/?ids={id}".format(id=snap.device.devicehub_id) body, status = user3.get(uri) @@ -242,3 +220,204 @@ def test_export_devices(user3: UserClientFlask): assert ( fixture_csv[1][86:] == export_csv[1][86:] ), 'Computer information are not equal' + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_export_metrics(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + uri = "/inventory/export/metrics/?ids={id}".format(id=snap.device.devicehub_id) + + body, status = user3.get(uri) + assert status == '200 OK' + assert body == '' + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_export_links(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + uri = "/inventory/export/links/?ids={id}".format(id=snap.device.devicehub_id) + + body, status = user3.get(uri) + assert status == '200 OK' + body = body.split("\n") + assert ['links', 'http://localhost/devices/O48N2', ''] == body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_export_certificates(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + uri = "/inventory/export/certificates/?ids={id}".format(id=snap.device.devicehub_id) + + body, status = user3.get(uri, decode=False) + body = str(next(body)) + assert status == '200 OK' + assert "PDF-1.5" in body + assert 'hts54322' in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_labels(user3: UserClientFlask): + body, status = user3.get('/labels/') + + assert status == '200 OK' + assert "Tags Management" in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_add_tag(user3: UserClientFlask): + uri = '/labels/add/' + body, status = user3.get(uri) + + assert status == '200 OK' + assert "Add a new Tag" in body + + data = { + 'code': "tag1", + 'csrf_token': generate_csrf(), + } + body, status = user3.post(uri, data=data) + + assert status == '200 OK' + assert "tag1" in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_label_details(user3: UserClientFlask): + uri = '/labels/add/' + user3.get(uri) + + data = { + 'code': "tag1", + 'csrf_token': generate_csrf(), + } + user3.post(uri, data=data) + + body, status = user3.get('/labels/tag1/') + assert "tag1" in body + assert "Print Label" in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_link_tag_to_device(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/labels/add/' + user3.get(uri) + + data = { + 'code': "tag1", + 'csrf_token': generate_csrf(), + } + user3.post(uri, data=data) + + body, status = user3.get('/inventory/device/') + assert "tag1" in body + + data = { + 'tag': "tag1", + 'device': dev.id, + 'csrf_token': generate_csrf(), + } + + uri = '/inventory/tag/devices/add/' + user3.post(uri, data=data) + assert len(list(dev.tags)) == 2 + tag = list(dev.tags)[0] + assert tag.id == "tag1" + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_unlink_tag_to_device(user3: UserClientFlask): + # create device + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + + # create tag + uri = '/labels/add/' + user3.get(uri) + + data = { + 'code': "tag1", + 'csrf_token': generate_csrf(), + } + user3.post(uri, data=data) + + # link tag to device + data = { + 'tag': "tag1", + 'device': dev.id, + 'csrf_token': generate_csrf(), + } + + uri = '/inventory/tag/devices/add/' + user3.post(uri, data=data) + + # unlink tag to device + uri = '/inventory/tag/devices/{id}/del/'.format(id=dev.id) + user3.get(uri) + + data = { + 'code': "tag1", + 'csrf_token': generate_csrf(), + } + user3.post(uri, data=data) + + data = { + 'tag': "tag1", + 'csrf_token': generate_csrf(), + } + + user3.post(uri, data=data) + assert len(list(dev.tags)) == 1 + tag = list(dev.tags)[0] + assert not tag.id == "tag1" + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_print_labels(user3: UserClientFlask): + # create device + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + + # create tag + uri = '/labels/add/' + user3.get(uri) + + data = { + 'code': "tag1", + 'csrf_token': generate_csrf(), + } + user3.post(uri, data=data) + + # link tag to device + data = { + 'tag': "tag1", + 'device': dev.id, + 'csrf_token': generate_csrf(), + } + + uri = '/inventory/tag/devices/add/' + user3.post(uri, data=data) + + assert len(list(dev.tags)) == 2 + + uri = '/labels/print' + data = { + 'devices': "{}".format(dev.id), + 'csrf_token': generate_csrf(), + } + body, status = user3.post(uri, data=data) + + assert status == '200 OK' + path = "/inventory/device/{}/".format(dev.devicehub_id) + assert path in body + assert "tag1" not in body From b986b6e95a1e394f49f3a64541c455292a0c25de Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 20 Apr 2022 18:22:22 +0200 Subject: [PATCH 11/27] more tests --- tests/test_render_2_0.py | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 0b9dc184..a95b8fa5 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -10,6 +10,7 @@ from flask_wtf.csrf import generate_csrf from ereuse_devicehub.client import UserClient, UserClientFlask from ereuse_devicehub.devicehub import Devicehub from ereuse_devicehub.resources.action.models import Snapshot +from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.lot.models import Lot from tests import conftest @@ -421,3 +422,58 @@ def test_print_labels(user3: UserClientFlask): path = "/inventory/device/{}/".format(dev.devicehub_id) assert path in body assert "tag1" not in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_add_monitor(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': "Monitor", + '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 "Monitor" created successfully!' in body + dev = Device.query.one() + assert dev.type == 'Monitor' + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_filter_monitor(user3: UserClientFlask): + uri = '/inventory/device/add/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Monitor", + 'serial_number': "AAAAB", + 'model': "LC27T55", + 'manufacturer': "Samsung", + 'generation': 1, + 'weight': 0.1, + 'height': 0.1, + 'depth': 0.1, + } + user3.post(uri, data=data) + csrf = generate_csrf() + # import pdb; pdb.set_trace() + + uri = f'/inventory/device/?filter=Monitor&csrf_token={csrf}' + body, status = user3.get(uri) + + assert status == '200 OK' + dev = Device.query.one() + assert dev.devicehub_id in body From fd74804f35edf02384879a74f9da19696aa85bce Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 21 Apr 2022 14:01:03 +0200 Subject: [PATCH 12/27] add actions test --- tests/test_render_2_0.py | 383 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 379 insertions(+), 4 deletions(-) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index a95b8fa5..0565d153 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -56,6 +56,16 @@ def test_login(user: UserClient, app: Devicehub): assert "Login to Your Account" not in body +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_profile(user3: UserClientFlask): + body, status = user3.get('/profile/') + + assert status == '200 OK' + assert "Profile" in body + assert user3.email in body + + @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_inventory(user3: UserClientFlask): @@ -116,7 +126,6 @@ def test_update_lot(user3: UserClientFlask): user3.get('/inventory/lot/add/') # Add lot - # import pdb; pdb.set_trace() data = { 'name': "lot1", 'csrf_token': generate_csrf(), @@ -330,8 +339,8 @@ def test_link_tag_to_device(user3: UserClientFlask): uri = '/inventory/tag/devices/add/' user3.post(uri, data=data) assert len(list(dev.tags)) == 2 - tag = list(dev.tags)[0] - assert tag.id == "tag1" + tags = [tag.id for tag in dev.tags] + assert "tag1" in tags @pytest.mark.mvp @@ -469,7 +478,6 @@ def test_filter_monitor(user3: UserClientFlask): } user3.post(uri, data=data) csrf = generate_csrf() - # import pdb; pdb.set_trace() uri = f'/inventory/device/?filter=Monitor&csrf_token={csrf}' body, status = user3.get(uri) @@ -477,3 +485,370 @@ def test_filter_monitor(user3: UserClientFlask): assert status == '200 OK' dev = Device.query.one() assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_recycling(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + # fail request + data = { + 'csrf_token': generate_csrf(), + 'type': "Allocate", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert dev.actions[-1].type == 'EreusePrice' + assert 'Action Allocate error!' in body + + # good request + data = { + 'csrf_token': generate_csrf(), + 'type': "Recycling", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'Recycling' + assert 'Action "Recycling" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_error_without_devices(user3: UserClientFlask): + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Recycling", + 'severity': "Info", + 'devices': "", + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert 'Action Recycling error!' in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_use(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Use", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'Use' + assert 'Action "Use" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_refurbish(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Refurbish", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'Refurbish' + assert 'Action "Refurbish" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_management(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Management", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'Management' + assert 'Action "Management" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_allocate(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Allocate", + 'severity': "Info", + 'devices': "{}".format(dev.id), + 'start_time': '2000-01-01', + 'end_time': '2000-06-01', + 'end_users': 2, + } + + uri = '/inventory/action/allocate/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'Allocate' + assert 'Action "Allocate" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_allocate_error_required(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Trade", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/allocate/add/' + body, status = user3.post(uri, data=data) + assert dev.actions[-1].type != 'Allocate' + + data = { + 'csrf_token': generate_csrf(), + 'type': "Allocate", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/allocate/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert 'You need to specify a number of users' in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_allocate_error_dates(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Allocate", + 'severity': "Info", + 'devices': "{}".format(dev.id), + 'start_time': '2000-06-01', + 'end_time': '2000-01-01', + 'end_users': 2, + } + + uri = '/inventory/action/allocate/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert 'The action cannot finish before it starts.' in body + assert dev.actions[-1].type != 'Allocate' + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_deallocate(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Allocate", + 'severity': "Info", + 'devices': "{}".format(dev.id), + 'start_time': '2000-01-01', + 'end_time': '2000-06-01', + 'end_users': 2, + } + + uri = '/inventory/action/allocate/add/' + + user3.post(uri, data=data) + assert dev.actions[-1].type == 'Allocate' + + data = { + 'csrf_token': generate_csrf(), + 'type': "Deallocate", + 'severity': "Info", + 'devices': "{}".format(dev.id), + 'start_time': '2000-01-01', + 'end_time': '2000-06-01', + 'end_users': 2, + } + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'Deallocate' + assert 'Action "Deallocate" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_toprepare(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "ToPrepare", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'ToPrepare' + assert 'Action "ToPrepare" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_prepare(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Prepare", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'Prepare' + assert 'Action "Prepare" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_torepair(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "ToRepair", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'ToRepair' + assert 'Action "ToRepair" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_ready(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + data = { + 'csrf_token': generate_csrf(), + 'type': "Ready", + 'severity': "Info", + 'devices': "{}".format(dev.id), + } + + uri = '/inventory/action/add/' + body, status = user3.post(uri, data=data) + assert status == '200 OK' + assert dev.actions[-1].type == 'Ready' + assert 'Action "Ready" created successfully!' in body + assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_action_datawipe(user3: UserClientFlask): + snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + dev = snap.device + uri = '/inventory/device/' + user3.get(uri) + + b_file = b'1234567890' + file_name = "my_file.doc" + file_upload = (BytesIO(b_file), file_name) + + data = { + 'csrf_token': generate_csrf(), + 'type': "DataWipe", + 'severity': "Info", + 'devices': "{}".format(dev.id), + 'document-file_name': file_upload, + } + + uri = '/inventory/action/datawipe/add/' + body, status = user3.post(uri, data=data, content_type="multipart/form-data") + assert status == '200 OK' + assert dev.actions[-1].type == 'DataWipe' + assert 'Action "DataWipe" created successfully!' in body + assert dev.devicehub_id in body From e37fa49c3e2bea9bc3bde04b6534a610697c1c23 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 21 Apr 2022 14:01:33 +0200 Subject: [PATCH 13/27] fix bug in validation actions --- ereuse_devicehub/inventory/forms.py | 53 +++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 72f033f8..08b6eeca 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -497,7 +497,7 @@ class TagDeviceForm(FlaskForm): db.session.commit() -class NewActionForm(FlaskForm): +class ActionFormMix(FlaskForm): name = StringField( 'Name', [validators.length(max=50)], @@ -529,17 +529,23 @@ class NewActionForm(FlaskForm): if not is_valid: return False - self._devices = OrderedSet() - if self.devices.data: - devices = set(self.devices.data.split(",")) - self._devices = OrderedSet( - Device.query.filter(Device.id.in_(devices)) - .filter(Device.owner_id == g.user.id) - .all() - ) + if self.type.data in [None, '']: + return False - if not self._devices: - return False + if not self.devices.data: + return False + + self._devices = OrderedSet() + + devices = set(self.devices.data.split(",")) + self._devices = OrderedSet( + Device.query.filter(Device.id.in_(devices)) + .filter(Device.owner_id == g.user.id) + .all() + ) + + if not self._devices: + return False return True @@ -572,7 +578,20 @@ class NewActionForm(FlaskForm): return self.type.data -class AllocateForm(NewActionForm): +class NewActionForm(ActionFormMix): + def validate(self, extra_validators=None): + is_valid = super().validate(extra_validators) + + if not is_valid: + return False + + if self.type.data in ['Allocate', 'Deallocate', 'Trade', 'DataWipe']: + return False + + return True + + +class AllocateForm(ActionFormMix): start_time = DateField('Start time') end_time = DateField('End time') final_user_code = StringField('Final user code', [validators.length(max=50)]) @@ -582,6 +601,9 @@ class AllocateForm(NewActionForm): def validate(self, extra_validators=None): is_valid = super().validate(extra_validators) + if self.type.data not in ['Allocate', 'Deallocate']: + return False + start_time = self.start_time.data end_time = self.end_time.data if start_time and end_time and end_time < start_time: @@ -650,7 +672,7 @@ class DataWipeDocumentForm(Form): return self._obj -class DataWipeForm(NewActionForm): +class DataWipeForm(ActionFormMix): document = FormField(DataWipeDocumentForm) def save(self): @@ -677,7 +699,7 @@ class DataWipeForm(NewActionForm): return self.instance -class TradeForm(NewActionForm): +class TradeForm(ActionFormMix): user_from = StringField( 'Supplier', [validators.Optional()], @@ -724,6 +746,9 @@ class TradeForm(NewActionForm): email_from = self.user_from.data email_to = self.user_to.data + if self.type.data != "Trade": + return False + if not self.confirm.data and not self.code.data: self.code.errors = ["If you don't want to confirm, you need a code"] is_valid = False From f9be7f0a14eaaef90e4f57958b501d4c363eda7b Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 21 Apr 2022 14:02:16 +0200 Subject: [PATCH 14/27] return error response in post request --- ereuse_devicehub/inventory/views.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 38ff4bc6..e6110f97 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -315,16 +315,19 @@ class NewActionView(View): def dispatch_request(self): self.form = self.form_class() + next_url = self.get_next_url() if self.form.validate_on_submit(): self.form.save() messages.success( 'Action "{}" created successfully!'.format(self.form.type.data) ) - next_url = self.get_next_url() return flask.redirect(next_url) + messages.error('Action {} error!'.format(self.form.type.data)) + return flask.redirect(next_url) + def get_next_url(self): lot_id = self.form.lot.data @@ -350,10 +353,9 @@ class NewAllocateView(NewActionView, DeviceListMix): next_url = self.get_next_url() return flask.redirect(next_url) - lot_id = self.form.lot.data - self.get_context(lot_id) - self.context['form_new_allocate'] = self.form - return flask.render_template(self.template_name, **self.context) + messages.error('Action {} error!'.format(self.form.type.data)) + next_url = self.get_next_url() + return flask.redirect(next_url) class NewDataWipeView(NewActionView, DeviceListMix): @@ -372,10 +374,9 @@ class NewDataWipeView(NewActionView, DeviceListMix): next_url = self.get_next_url() return flask.redirect(next_url) - lot_id = self.form.lot.data - self.get_context(lot_id) - self.context['form_new_datawipe'] = self.form - return flask.render_template(self.template_name, **self.context) + messages.error('Action {} error!'.format(self.form.type.data)) + next_url = self.get_next_url() + return flask.redirect(next_url) class NewTradeView(NewActionView, DeviceListMix): @@ -394,10 +395,9 @@ class NewTradeView(NewActionView, DeviceListMix): next_url = self.get_next_url() return flask.redirect(next_url) - lot_id = self.form.lot.data - self.get_context(lot_id) - self.context['form_new_trade'] = self.form - return flask.render_template(self.template_name, **self.context) + messages.error('Action {} error!'.format(self.form.type.data)) + next_url = self.get_next_url() + return flask.redirect(next_url) class NewTradeDocumentView(View): From bf42a79ef7584f3d5bcab2dd522825fc0dc839d6 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 27 Apr 2022 18:55:27 +0200 Subject: [PATCH 15/27] change devices for tags in printlabels --- ereuse_devicehub/labels/forms.py | 7 ++----- ereuse_devicehub/labels/views.py | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ereuse_devicehub/labels/forms.py b/ereuse_devicehub/labels/forms.py index cd4b5bec..98427215 100644 --- a/ereuse_devicehub/labels/forms.py +++ b/ereuse_devicehub/labels/forms.py @@ -64,10 +64,7 @@ class PrintLabelsForm(FlaskForm): .all() ) - # print only tags that are DHID - dhids = [x.devicehub_id for x in self._devices] - self._tags = ( - Tag.query.filter(Tag.owner_id == g.user.id).filter(Tag.id.in_(dhids)).all() - ) + if not self._devices: + return False return is_valid diff --git a/ereuse_devicehub/labels/views.py b/ereuse_devicehub/labels/views.py index 445a4eb8..3f4fbf5d 100644 --- a/ereuse_devicehub/labels/views.py +++ b/ereuse_devicehub/labels/views.py @@ -102,7 +102,7 @@ class PrintLabelsView(View): form = PrintLabelsForm() if form.validate_on_submit(): context['form'] = form - context['tags'] = form._tags + context['devices'] = form._devices return flask.render_template(self.template_name, **context) else: messages.error('Error you need select one or more devices') From e378fef1a6499c1b247415927594067a5146e6cd Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 27 Apr 2022 18:56:15 +0200 Subject: [PATCH 16/27] add customize options in print html and pdf --- ereuse_devicehub/static/js/print.pdf.js | 110 ++++++++++++++++-- .../templates/labels/print_labels.html | 86 ++++++++++---- 2 files changed, 165 insertions(+), 31 deletions(-) diff --git a/ereuse_devicehub/static/js/print.pdf.js b/ereuse_devicehub/static/js/print.pdf.js index 0d6fe6d5..f0b1817c 100644 --- a/ereuse_devicehub/static/js/print.pdf.js +++ b/ereuse_devicehub/static/js/print.pdf.js @@ -1,8 +1,10 @@ $(document).ready(function() { STORAGE_KEY = 'tag-spec-key'; $("#printerType").on("change", change_size); + $(".form-check-input").on("change", change_check); change_size(); - load_size(); + load_settings(); + change_check(); }) function qr_draw(url, id) { @@ -16,27 +18,43 @@ function qr_draw(url, id) { }); } -function save_size() { +function save_settings() { var height = $("#height-tag").val(); var width = $("#width-tag").val(); var sizePreset = $("#printerType").val(); var data = {"height": height, "width": width, "sizePreset": sizePreset}; + data['dhid'] = $("#dhidCheck").prop('checked'); + data['qr'] = $("#qrCheck").prop('checked'); + data['serial_number'] = $("#serialNumberCheck").prop('checked'); + data['manufacturer'] = $("#manufacturerCheck").prop('checked'); + data['model'] = $("#modelCheck").prop('checked'); localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } -function load_size() { +function load_settings() { var data = JSON.parse(localStorage.getItem(STORAGE_KEY)); if (data){ $("#height-tag").val(data.height); $("#width-tag").val(data.width); $("#printerType").val(data.sizePreset); + $("#qrCheck").prop('checked', data.qr); + $("#dhidCheck").prop('checked', data.dhid); + $("#serialNumberCheck").prop('checked', data.serial_number); + $("#manufacturerCheck").prop('checked', data.manufacturer); + $("#modelCheck").prop('checked', data.model); }; } -function reset_size() { +function reset_settings() { localStorage.removeItem(STORAGE_KEY); $("#printerType").val('brotherSmall'); + $("#qrCheck").prop('checked', true); + $("#dhidCheck").prop('checked', true); + $("#serialNumberCheck").prop('checked', false); + $("#manufacturerCheck").prop('checked', false); + $("#modelCheck").prop('checked', false); change_size(); + change_check(); } function change_size() { @@ -50,29 +68,101 @@ function change_size() { } } +function change_check() { + if ($("#dhidCheck").prop('checked')) { + $(".dhid").show(); + } else { + $(".dhid").hide(); + } + if ($("#serialNumberCheck").prop('checked')) { + $(".serial_number").show(); + } else { + $(".serial_number").hide(); + } + if ($("#manufacturerCheck").prop('checked')) { + $(".manufacturer").show(); + } else { + $(".manufacturer").hide(); + } + if ($("#modelCheck").prop('checked')) { + $(".model").show(); + } else { + $(".model").hide(); + } + if ($("#qrCheck").prop('checked')) { + $(".qr").show(); + } else { + $(".qr").hide(); + } +} + function printpdf() { var border = 2; + var line = 5; var height = parseInt($("#height-tag").val()); var width = parseInt($("#width-tag").val()); - img_side = Math.min(height, width) - 2*border; + var img_side = Math.min(height, width) - 2*border; max_tag_side = (Math.max(height, width)/2) + border; if (max_tag_side < img_side) { - max_tag_side = img_side+ 2*border; + max_tag_side = img_side + 2*border; }; min_tag_side = (Math.min(height, width)/2) + border; var last_tag_code = ''; + if ($("#serialNumberCheck").prop('checked')) { + height += line; + }; + if ($("#manufacturerCheck").prop('checked')) { + height += line; + }; + if ($("#modelCheck").prop('checked')) { + height += line; + }; + var pdf = new jsPDF('l', 'mm', [width, height]); $(".tag").map(function(x, y) { if (x != 0){ pdf.addPage(); - console.log(x) }; + var space = line + border; + if ($("#qrCheck").prop('checked')) { + space += img_side; + } var tag = $(y).text(); last_tag_code = tag; - var imgData = $('#'+tag+' img').attr("src"); - pdf.addImage(imgData, 'PNG', border, border, img_side, img_side); - pdf.text(tag, max_tag_side, min_tag_side); + if ($("#qrCheck").prop('checked')) { + var imgData = $('#'+tag+' img').attr("src"); + pdf.addImage(imgData, 'PNG', border, border, img_side, img_side); + }; + + if ($("#dhidCheck").prop('checked')) { + if ($("#qrCheck").prop('checked')) { + pdf.setFontSize(15); + pdf.text(tag, max_tag_side, min_tag_side); + } else { + pdf.setFontSize(15); + pdf.text(tag, border, space); + space += line; + } + }; + if ($("#serialNumberCheck").prop('checked')) { + var sn = $(y).data('serial-number'); + pdf.setFontSize(12); + pdf.text(sn, border, space); + space += line; + }; + if ($("#manufacturerCheck").prop('checked')) { + var sn = $(y).data('manufacturer'); + pdf.setFontSize(12); + pdf.text(sn, border, space); + space += line; + }; + if ($("#modelCheck").prop('checked')) { + var sn = $(y).data('model'); + pdf.setFontSize(8); + pdf.text(sn, border, space); + space += line; + }; }); pdf.save('Tag_'+last_tag_code+'.pdf'); diff --git a/ereuse_devicehub/templates/labels/print_labels.html b/ereuse_devicehub/templates/labels/print_labels.html index 306b32f9..55d35111 100644 --- a/ereuse_devicehub/templates/labels/print_labels.html +++ b/ereuse_devicehub/templates/labels/print_labels.html @@ -24,16 +24,39 @@
- {% for tag in tags %} -
-
+ {% for dev in devices %} +
+
-
-
+
+
-
+
- {{ tag.id }} + {{ dev.devicehub_id }} +
+
+
+ + + @@ -71,20 +94,41 @@ mm
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
-
-
- Print -
-
- Save -
-
- Reset -
-
-
@@ -96,8 +140,8 @@ {% endblock main %} From ad11521b640c4687bca9c06ea4b5352eb049cfb9 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 28 Apr 2022 10:14:10 +0200 Subject: [PATCH 17/27] fix label details for new architecture --- .../templates/labels/label_detail.html | 88 +++++++++++++++---- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/ereuse_devicehub/templates/labels/label_detail.html b/ereuse_devicehub/templates/labels/label_detail.html index 75ff3efb..24c8a535 100644 --- a/ereuse_devicehub/templates/labels/label_detail.html +++ b/ereuse_devicehub/templates/labels/label_detail.html @@ -43,16 +43,49 @@
Print Label
-
+
-
+
-
-
{{ tag.id }}
+
+
+ {% if tag.device %} + {{ tag.id }} + {% else %} + {{ tag.id }} + {% endif %} +
+ {% if tag.device %} + + + + {% endif %}
@@ -84,20 +117,43 @@ mm
+ {% if tag.device %} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ {% endif %} +
-
-
- Print -
-
- Save -
-
- Reset -
-
-
From c2e431352144865693e60302cfbf43f739f77938 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 28 Apr 2022 11:16:12 +0200 Subject: [PATCH 18/27] change tag for Unique identifier in label_details --- ereuse_devicehub/templates/labels/label_detail.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ereuse_devicehub/templates/labels/label_detail.html b/ereuse_devicehub/templates/labels/label_detail.html index 24c8a535..c378c6f3 100644 --- a/ereuse_devicehub/templates/labels/label_detail.html +++ b/ereuse_devicehub/templates/labels/label_detail.html @@ -5,8 +5,8 @@

Inventory

@@ -26,7 +26,7 @@
Type
-
{% if tag.provider %}UnNamed Tag{% else %}Named{% endif %}
+
{% if tag.provider %}UnNamed Unique Identifier{% else %}Named{% endif %}
@@ -125,7 +125,7 @@
- +
From 87c66cf5cd1f281f435fa135602a0e6bce20f2aa Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 28 Apr 2022 11:47:52 +0200 Subject: [PATCH 19/27] change tags for unique identifiers --- .../templates/ereuse_devicehub/base_site.html | 11 +------ .../templates/inventory/addDevicestag.html | 6 ++-- .../templates/inventory/device_list.html | 29 +++++++++++++++---- .../inventory/tag_unlink_device.html | 8 ++--- .../templates/labels/label_detail.html | 6 ++-- .../templates/labels/label_list.html | 6 ++-- .../templates/labels/print_labels.html | 2 +- .../templates/labels/tag_create.html | 8 ++--- .../templates/labels/tag_create_unnamed.html | 8 ++--- 9 files changed, 46 insertions(+), 38 deletions(-) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index 4e721730..f10172e5 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -191,15 +191,6 @@ - - - - @@ -238,4 +229,4 @@
-{% endblock body %} \ No newline at end of file +{% endblock body %} diff --git a/ereuse_devicehub/templates/inventory/addDevicestag.html b/ereuse_devicehub/templates/inventory/addDevicestag.html index 51938211..bb02c221 100644 --- a/ereuse_devicehub/templates/inventory/addDevicestag.html +++ b/ereuse_devicehub/templates/inventory/addDevicestag.html @@ -3,14 +3,14 @@ + +