From 16d00256dfb7b10645a25e1895f1b4984b3c5230 Mon Sep 17 00:00:00 2001 From: Xavier Bustamante Talavera Date: Wed, 8 May 2019 19:12:05 +0200 Subject: [PATCH] Review rate changes --- ereuse_devicehub/config.py | 6 - .../dummy/files/asus-1001pxd.snapshot.11.yaml | 2 +- .../files/asus-eee-1000h.snapshot.11.yaml | 2 +- .../files/computer-monitor.snapshot.yaml | 2 +- .../files/dell-optiplexgx520.snapshot.11.yaml | 2 +- .../dummy/files/keyboard.snapshot.yaml | 2 +- .../dummy/files/nec.snapshot.11.yaml | 2 +- .../dummy/files/pc-laudem.snapshot.11.yaml | 2 +- .../files/real-eee-1001pxd.snapshot.11.yaml | 2 +- .../dummy/files/real-hp.snapshot.11.yaml | 2 +- .../dummy/files/smartphone.snapshot.yaml | 2 +- .../files/workbench-server-1.snapshot.yaml | 160 +++++----- ereuse_devicehub/resources/device/models.py | 3 +- ereuse_devicehub/resources/device/models.pyi | 12 +- ereuse_devicehub/resources/device/schemas.py | 1 - .../device/templates/devices/macros.html | 22 +- ereuse_devicehub/resources/device/views.py | 6 +- .../resources/documents/device_row.py | 13 +- .../resources/documents/documents.py | 5 +- ereuse_devicehub/resources/enums.py | 43 +-- ereuse_devicehub/resources/event/__init__.py | 9 +- ereuse_devicehub/resources/event/models.py | 279 +++++++++++------- ereuse_devicehub/resources/event/models.pyi | 105 ++++--- ereuse_devicehub/resources/event/rate/rate.py | 3 +- .../resources/event/rate/workbench/v1_0.py | 79 +++-- ereuse_devicehub/resources/event/schemas.py | 43 ++- ereuse_devicehub/resources/event/views.py | 5 +- ereuse_devicehub/resources/lot/dag.sql | 170 ++++++----- ereuse_devicehub/resources/tag/schema.py | 2 +- tests/files/asus-1001pxd.snapshot.yaml | 2 +- tests/files/basic.snapshot.yaml | 2 +- ...sktop-9644w8n-lenovo-0169622.snapshot.yaml | 2 +- ...k-hewlett-packard-cnd52270fw.snapshot.yaml | 2 +- tests/test_basic.py | 2 +- tests/test_device.py | 13 +- tests/test_event.py | 25 +- tests/test_rate.py | 34 ++- tests/test_rate_workbench_v1.py | 90 +++--- tests/test_snapshot.py | 35 +-- tests/test_workbench.py | 17 +- 40 files changed, 642 insertions(+), 568 deletions(-) diff --git a/ereuse_devicehub/config.py b/ereuse_devicehub/config.py index 251f72ce..edc54da7 100644 --- a/ereuse_devicehub/config.py +++ b/ereuse_devicehub/config.py @@ -39,12 +39,6 @@ class DevicehubConfig(Config): } API_DOC_CLASS_DISCRIMINATOR = 'type' - # TODO is necessary?? - WORKBENCH_RATE_VERSION = StrictVersion('1.0') - PHOTOBOX_RATE_VERSION = StrictVersion('1.0') - """ - Official versions for WorkbenchRate and PhotoboxRate - """ PRICE_SOFTWARE = PriceSoftware.Ereuse PRICE_VERSION = StrictVersion('1.0') PRICE_CURRENCY = Currency.EUR diff --git a/ereuse_devicehub/dummy/files/asus-1001pxd.snapshot.11.yaml b/ereuse_devicehub/dummy/files/asus-1001pxd.snapshot.11.yaml index e061af0f..a5af2694 100644 --- a/ereuse_devicehub/dummy/files/asus-1001pxd.snapshot.11.yaml +++ b/ereuse_devicehub/dummy/files/asus-1001pxd.snapshot.11.yaml @@ -164,7 +164,7 @@ { "appearanceRange": "A", "functionalityRange": "A", - "type": "TestVisual" + "type": "VisualTest" } ], "manufacturer": "ASUSTeK Computer INC.", diff --git a/ereuse_devicehub/dummy/files/asus-eee-1000h.snapshot.11.yaml b/ereuse_devicehub/dummy/files/asus-eee-1000h.snapshot.11.yaml index 08b55c2e..de93be61 100644 --- a/ereuse_devicehub/dummy/files/asus-eee-1000h.snapshot.11.yaml +++ b/ereuse_devicehub/dummy/files/asus-eee-1000h.snapshot.11.yaml @@ -16,7 +16,7 @@ { "appearanceRange": "A", "functionalityRange": "A", - "type": "TestVisual" + "type": "VisualTest" } ], "manufacturer": "ASUSTeK Computer INC." diff --git a/ereuse_devicehub/dummy/files/computer-monitor.snapshot.yaml b/ereuse_devicehub/dummy/files/computer-monitor.snapshot.yaml index 7236615b..3b02e128 100644 --- a/ereuse_devicehub/dummy/files/computer-monitor.snapshot.yaml +++ b/ereuse_devicehub/dummy/files/computer-monitor.snapshot.yaml @@ -11,7 +11,7 @@ device: resolutionHeight: 1080 size: 21.5 events: - - type: TestVisual + - type: VisualTest appearanceRange: A functionalityRange: C labelling: False diff --git a/ereuse_devicehub/dummy/files/dell-optiplexgx520.snapshot.11.yaml b/ereuse_devicehub/dummy/files/dell-optiplexgx520.snapshot.11.yaml index a708ef55..98ae89a5 100644 --- a/ereuse_devicehub/dummy/files/dell-optiplexgx520.snapshot.11.yaml +++ b/ereuse_devicehub/dummy/files/dell-optiplexgx520.snapshot.11.yaml @@ -16,7 +16,7 @@ { "appearanceRange": "A", "functionalityRange": "A", - "type": "TestVisual" + "type": "VisualTest" } ], "type": "Desktop", diff --git a/ereuse_devicehub/dummy/files/keyboard.snapshot.yaml b/ereuse_devicehub/dummy/files/keyboard.snapshot.yaml index 00ee55c0..e5e44ea6 100644 --- a/ereuse_devicehub/dummy/files/keyboard.snapshot.yaml +++ b/ereuse_devicehub/dummy/files/keyboard.snapshot.yaml @@ -8,7 +8,7 @@ device: manufacturer: BAZ layout: ES events: - - type: TestVisual + - type: VisualTest appearanceRange: A functionalityRange: A labelling: False diff --git a/ereuse_devicehub/dummy/files/nec.snapshot.11.yaml b/ereuse_devicehub/dummy/files/nec.snapshot.11.yaml index 3723aed2..68ba4065 100644 --- a/ereuse_devicehub/dummy/files/nec.snapshot.11.yaml +++ b/ereuse_devicehub/dummy/files/nec.snapshot.11.yaml @@ -18,7 +18,7 @@ { "appearanceRange": "B", "functionalityRange": "C", - "type": "TestVisual" + "type": "VisualTest" } ], "type": "Desktop", diff --git a/ereuse_devicehub/dummy/files/pc-laudem.snapshot.11.yaml b/ereuse_devicehub/dummy/files/pc-laudem.snapshot.11.yaml index 1bc00363..e6ce4df9 100644 --- a/ereuse_devicehub/dummy/files/pc-laudem.snapshot.11.yaml +++ b/ereuse_devicehub/dummy/files/pc-laudem.snapshot.11.yaml @@ -122,7 +122,7 @@ { "appearanceRange": "A", "functionalityRange": "A", - "type": "TestVisual" + "type": "VisualTest" } ], "manufacturer": "Hewlett-Packard", diff --git a/ereuse_devicehub/dummy/files/real-eee-1001pxd.snapshot.11.yaml b/ereuse_devicehub/dummy/files/real-eee-1001pxd.snapshot.11.yaml index 987035d1..bb30132a 100644 --- a/ereuse_devicehub/dummy/files/real-eee-1001pxd.snapshot.11.yaml +++ b/ereuse_devicehub/dummy/files/real-eee-1001pxd.snapshot.11.yaml @@ -154,7 +154,7 @@ { "appearanceRange": "B", "functionalityRange": "A", - "type": "TestVisual" + "type": "VisualTest" } ] }, diff --git a/ereuse_devicehub/dummy/files/real-hp.snapshot.11.yaml b/ereuse_devicehub/dummy/files/real-hp.snapshot.11.yaml index 5cde0a35..cd8d15a3 100644 --- a/ereuse_devicehub/dummy/files/real-hp.snapshot.11.yaml +++ b/ereuse_devicehub/dummy/files/real-hp.snapshot.11.yaml @@ -157,7 +157,7 @@ { "appearanceRange": "B", "functionalityRange": "D", - "type": "TestVisual" + "type": "VisualTest" } ], "serialNumber": "CZC0408YJG", diff --git a/ereuse_devicehub/dummy/files/smartphone.snapshot.yaml b/ereuse_devicehub/dummy/files/smartphone.snapshot.yaml index 04c4874f..63be0f3d 100644 --- a/ereuse_devicehub/dummy/files/smartphone.snapshot.yaml +++ b/ereuse_devicehub/dummy/files/smartphone.snapshot.yaml @@ -8,7 +8,7 @@ device: serialNumber: ABCDEF imei: 35686800-004141-20 events: - - type: TestVisual + - type: VisualTest appearanceRange: A functionalityRange: B labelling: False diff --git a/ereuse_devicehub/dummy/files/workbench-server-1.snapshot.yaml b/ereuse_devicehub/dummy/files/workbench-server-1.snapshot.yaml index 9a31c355..c5a99cb3 100644 --- a/ereuse_devicehub/dummy/files/workbench-server-1.snapshot.yaml +++ b/ereuse_devicehub/dummy/files/workbench-server-1.snapshot.yaml @@ -18,90 +18,90 @@ device: model: d1ml manufacturer: d1mr tags: - - type: Tag - id: tag1 + - type: Tag + id: tag1 events: - - type: TestVisual + - type: VisualTest appearanceRange: A functionalityRange: B - type: BenchmarkRamSysbench rate: 2444 elapsed: 1 components: -- type: GraphicCard - serialNumber: gc1-1s - model: gc1-1ml - manufacturer: gc1-1mr -- type: RamModule - serialNumber: rm1-1s - model: rm1-1ml - manufacturer: rm1-1mr - size: 1024 -- type: RamModule - serialNumber: rm2-1s - model: rm2-1ml - manufacturer: rm2-1mr - size: 1024 -- type: Processor - model: p1-1ml - manufacturer: p1-1mr - events: - - type: BenchmarkProcessor - rate: 2410 - elapsed: 44 - - type: BenchmarkProcessorSysbench - rate: 4400 - elapsed: 44 -- type: SolidStateDrive - serialNumber: ssd1-1s - model: ssd1-1ml - manufacturer: ssd1-1mr - size: 1100 - events: - - type: BenchmarkDataStorage - readSpeed: 20 - writeSpeed: 15 - elapsed: 21 - - type: TestDataStorage - elapsed: 233 - severity: Info - status: Completed without error - length: Short - lifetime: 99 - assessment: True - powerCycleCount: 11 - reallocatedSectorCount: 2 - reportedUncorrectableErrors: 1 - commandTimeout: 11 - currentPendingSectorCount: 1 - offlineUncorrectable: 33 - remainingLifetimePercentage: 1 -- type: HardDrive - serialNumber: hdd1-1s - model: hdd1-1ml - manufacturer: hdd1-1mr - events: - - type: BenchmarkDataStorage - readSpeed: 10 - writeSpeed: 5 - elapsed: 20 -- type: Motherboard - serialNumber: mb1-1s - model: mb1-1ml - manufacturer: mb1-1mr -- type: NetworkAdapter - serialNumber: na1-s - model: na1-1ml - manufacturer: na1-1mr - speed: 1000 - wireless: False -- type: NetworkAdapter - serialNumber: na2-s - model: na2-1ml - manufacturer: na2-1mr - wireless: True - speed: 58 -- type: RamModule - serialNumber: rm3-1s - model: rm3-1ml - manufacturer: rm3-1mr + - type: GraphicCard + serialNumber: gc1-1s + model: gc1-1ml + manufacturer: gc1-1mr + - type: RamModule + serialNumber: rm1-1s + model: rm1-1ml + manufacturer: rm1-1mr + size: 1024 + - type: RamModule + serialNumber: rm2-1s + model: rm2-1ml + manufacturer: rm2-1mr + size: 1024 + - type: Processor + model: p1-1ml + manufacturer: p1-1mr + events: + - type: BenchmarkProcessor + rate: 2410 + elapsed: 44 + - type: BenchmarkProcessorSysbench + rate: 4400 + elapsed: 44 + - type: SolidStateDrive + serialNumber: ssd1-1s + model: ssd1-1ml + manufacturer: ssd1-1mr + size: 1100 + events: + - type: BenchmarkDataStorage + readSpeed: 20 + writeSpeed: 15 + elapsed: 21 + - type: TestDataStorage + elapsed: 233 + severity: Info + status: Completed without error + length: Short + lifetime: 99 + assessment: True + powerCycleCount: 11 + reallocatedSectorCount: 2 + reportedUncorrectableErrors: 1 + commandTimeout: 11 + currentPendingSectorCount: 1 + offlineUncorrectable: 33 + remainingLifetimePercentage: 1 + - type: HardDrive + serialNumber: hdd1-1s + model: hdd1-1ml + manufacturer: hdd1-1mr + events: + - type: BenchmarkDataStorage + readSpeed: 10 + writeSpeed: 5 + elapsed: 20 + - type: Motherboard + serialNumber: mb1-1s + model: mb1-1ml + manufacturer: mb1-1mr + - type: NetworkAdapter + serialNumber: na1-s + model: na1-1ml + manufacturer: na1-1mr + speed: 1000 + wireless: False + - type: NetworkAdapter + serialNumber: na2-s + model: na2-1ml + manufacturer: na2-1mr + wireless: True + speed: 58 + - type: RamModule + serialNumber: rm3-1s + model: rm3-1ml + manufacturer: rm3-1mr diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index b84e2dcc..29c0a39c 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -422,8 +422,7 @@ class Computer(Device): 1. The max Ethernet speed of the computer, 0 if ethernet adaptor exists but its speed is unknown, None if no eth - adaptor exists.# TODO add all grade tables (chassis defects, camera defects, buttons test, connectivity, ..) - + adaptor exists. 2. The max WiFi speed of the computer, 0 if computer has WiFi but its speed is unknown, None if no WiFi adaptor exists. diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index fcc7509b..d1956a3a 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -42,7 +42,7 @@ class Device(Thing): production_date = ... # type: Column brand = ... # type: Column generation = ... # type: Column - variant = ... # type: Column + variant = ... # type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) @@ -65,7 +65,7 @@ class Device(Thing): self.production_date = ... # type: Optional[datetime] self.brand = ... # type: Optional[str] self.generation = ... # type: Optional[int] - self.variant = ... # type: Optional[str] + self.variant = ... # type: Optional[str] @property def events(self) -> List[e.Event]: @@ -202,15 +202,15 @@ class TelevisionSet(Monitor): class Mobile(Device): imei = ... # type: Column meid = ... # type: Column - ram_size = ... # type: Column - data_storage_size = ... # type: Column + ram_size = ... # type: Column + data_storage_size = ... # type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.imei = ... # type: Optional[int] self.meid = ... # type: Optional[str] - self.ram_size = ... # type: Optional[int] - self.data_storage_size = ... # type: Optional[int] + self.ram_size = ... # type: Optional[int] + self.data_storage_size = ... # type: Optional[int] class Smartphone(Mobile): diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index a5776adb..17b010f3 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -183,7 +183,6 @@ class Mobile(Device): data_key='dataStorageSize', description=m.Mobile.data_storage_size) - @pre_load def convert_check_imei(self, data): if data.get('imei', None): diff --git a/ereuse_devicehub/resources/device/templates/devices/macros.html b/ereuse_devicehub/resources/device/templates/devices/macros.html index 9d13c11b..fdd9d5e7 100644 --- a/ereuse_devicehub/resources/device/templates/devices/macros.html +++ b/ereuse_devicehub/resources/device/templates/devices/macros.html @@ -1,18 +1,18 @@ {% macro component_type(components, type) %} - + {% endmacro %} {% macro rate(range) %} - + {{ range }} {% endmacro %} diff --git a/ereuse_devicehub/resources/device/views.py b/ereuse_devicehub/resources/device/views.py index bbe229d6..337628b9 100644 --- a/ereuse_devicehub/resources/device/views.py +++ b/ereuse_devicehub/resources/device/views.py @@ -31,9 +31,9 @@ class OfType(f.Str): class RateQ(query.Query): - rating = query.Between(events.Rate.rating, f.Float()) - appearance = query.Between(events.Rate.appearance, f.Float()) - functionality = query.Between(events.Rate.functionality, f.Float()) + rating = query.Between(events.Rate._rating, f.Float()) + appearance = query.Between(events.Rate._appearance, f.Float()) + functionality = query.Between(events.Rate._functionality, f.Float()) class TagQ(query.Query): diff --git a/ereuse_devicehub/resources/documents/device_row.py b/ereuse_devicehub/resources/documents/device_row.py index 78e9575f..8a1e3a4c 100644 --- a/ereuse_devicehub/resources/documents/device_row.py +++ b/ereuse_devicehub/resources/documents/device_row.py @@ -3,7 +3,8 @@ from collections import OrderedDict from flask import current_app from ereuse_devicehub.resources.device import models as d -from ereuse_devicehub.resources.event.models import TestDataStorage, BenchmarkDataStorage +from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, RateComputer, \ + TestDataStorage class DeviceRow(OrderedDict): @@ -43,6 +44,7 @@ class DeviceRow(OrderedDict): if rate: self['Rate'] = rate.rating self['Range'] = rate.rating_range + assert isinstance(rate, RateComputer) self['Processor Rate'] = rate.processor self['Processor Range'] = rate.processor_range self['RAM Rate'] = rate.ram @@ -92,15 +94,18 @@ class DeviceRow(OrderedDict): self['{} {} Size (MB)'.format(type, i)] = component.size self['{} {} Privacy'.format(type, i)] = component.privacy try: - self['{} {} Lifetime'.format(type, i)] = component.last_event_of(TestDataStorage).lifetime + self['{} {} Lifetime'.format(type, i)] = component.last_event_of( + TestDataStorage).lifetime except: self['{} {} Lifetime'.format(type, i)] = '' try: - self['{} {} Reading speed'.format(type, i)] = component.last_event_of(BenchmarkDataStorage).read_speed + self['{} {} Reading speed'.format(type, i)] = component.last_event_of( + BenchmarkDataStorage).read_speed except: self['{} {} Reading speed'.format(type, i)] = '' try: - self['{} {} Writing speed'.format(type, i)] = component.last_event_of(BenchmarkDataStorage).write_speed + self['{} {} Writing speed'.format(type, i)] = component.last_event_of( + BenchmarkDataStorage).write_speed except: self['{} {} Writing speed'.format(type, i)] = '' diff --git a/ereuse_devicehub/resources/documents/documents.py b/ereuse_devicehub/resources/documents/documents.py index f8402078..c30506f4 100644 --- a/ereuse_devicehub/resources/documents/documents.py +++ b/ereuse_devicehub/resources/documents/documents.py @@ -109,7 +109,6 @@ class DevicesDocumentView(DeviceView): query = self.query(args) return self.generate_post_csv(query) - # TODO fix only put one row for device.t == computer (rewrite multiples_devices.csv) def generate_post_csv(self, query): """ Get device query and put information in csv format @@ -122,9 +121,9 @@ class DevicesDocumentView(DeviceView): for device in query: d = DeviceRow(device) if first: - cw.writerow(name for name in d.keys()) + cw.writerow(d.keys()) first = False - cw.writerow(v for v in d.values()) + cw.writerow(d.values()) output = make_response(data.getvalue()) output.headers['Content-Disposition'] = 'attachment; filename=export.csv' output.headers['Content-type'] = 'text/csv' diff --git a/ereuse_devicehub/resources/enums.py b/ereuse_devicehub/resources/enums.py index 47b38668..47278c27 100644 --- a/ereuse_devicehub/resources/enums.py +++ b/ereuse_devicehub/resources/enums.py @@ -1,5 +1,4 @@ from contextlib import suppress -from distutils.version import StrictVersion from enum import Enum, IntEnum, unique from typing import Set, Union @@ -18,8 +17,8 @@ class SnapshotSoftware(Enum): return self.name -RATE_POSITIVE = 0, 10 -RATE_NEGATIVE = -3, 5 +R_POSITIVE = 0, 10 +R_NEGATIVE = -3, 5 @unique @@ -65,45 +64,30 @@ class PriceSoftware(Enum): Ereuse = 'Ereuse' -@unique -class AggregateRatingVersions(Enum): - v1 = StrictVersion('1.0') - """ - This version is set to aggregate :class:`ereuse_devicehub.resources. - event.models.RateComputer` version X and :class:`ereuse_devicehub. - resources.event.models.PhotoboxRate` version Y. - """ - - @unique class AppearanceRange(Enum): - """ - This grade will be defined based on the aesthetics/cosmetic aspects, like visual damage or blemishes principally - focused on chassis, physical buttons and screens. - """ + """Grades the imperfections that aesthetically affect the device, but not its usage.""" Z = 'Z. The device is new' A = 'A. Is like new; without visual damage' B = 'B. Is in really good condition; small visual damage in difficult places to spot' - C = 'C. Is in good condition; small visual damage in parts that are easy to spot, minor cosmetic blemishes on chassis)' + C = 'C. Is in good condition; small visual damage in parts that are easy to spot, minor cosmetic blemishes on chassis' D = 'D. Is acceptable; visual damage in visible parts, major cosmetic blemishes on chassis, missing cosmetic parts' - E = 'E. Is unacceptable; severity visual damage, missing essential parts' - NONE = 'NA. Grade doesn’t exists' + E = 'E. Is unacceptable; considerable visual damage, missing essential parts' - -APPEARANCE_RANGE = 0.5, -0.3 + def __str__(self): + return self.name @unique class FunctionalityRange(Enum): - """Based on usage condition of a device and its functionality aspects, like screen defect or camera defects""" + """Grades the defects of a device that affect its usage.""" A = 'A. All the buttons works perfectly, no screen/camera defects and chassis without usage issues' B = 'B. There is a button difficult to press or unstable it, a screen/camera defect or chassis problem' C = 'C. Chassis defects or multiple buttons don\'t work; broken or unusable it, some screen/camera defect' D = 'D. Chassis severity usage problems. All buttons, screen or camera don\'t work; broken or unusable it' - NONE = 'NA. Grade doesn’t exists' - -FUNCTIONALITY_RANGE = 0.4, -0.3 + def __str__(self): + return self.name @unique @@ -113,9 +97,11 @@ class BatteryHealthRange(Enum): B = 'B. Battery health is good' C = 'C. Battery health is overheat / over voltage status but can stand the minimum duration' D = 'D. Battery health is bad; can’t stand the minimum duration time' - E = 'E. Battery health is very bad; and status is dead; unusable or miss it ' + E = 'E. Battery health is very bad; and status is dead; unusable or miss it' NONE = 'NA. Grade doesn’t exists' + def __str__(self): + return self.name @unique @@ -127,6 +113,9 @@ class BiosAccessRange(Enum): D = 'D. Like B or C, but you had to unlock the BIOS (i.e. by removing the battery)' E = 'E. The device could not be booted through the network.' + def __str__(self): + return self.name + @unique class Orientation(Enum): diff --git a/ereuse_devicehub/resources/event/__init__.py b/ereuse_devicehub/resources/event/__init__.py index 96843230..4155464a 100644 --- a/ereuse_devicehub/resources/event/__init__.py +++ b/ereuse_devicehub/resources/event/__init__.py @@ -109,11 +109,6 @@ class TestConnectivityDef(TestDef): SCHEMA = schemas.TestConnectivity -class TestBatteryDef(TestDef): - VIEW = None - SCHEMA = schemas.TestBattery - - class TestCameraDef(TestDef): VIEW = None SCHEMA = schemas.TestCamera @@ -134,9 +129,9 @@ class TestBiosDef(TestDef): SCHEMA = schemas.TestBios -class TestVisualDef(TestDef): +class VisualTestDef(TestDef): VIEW = None - SCHEMA = schemas.TestVisual + SCHEMA = schemas.VisualTest class RateDef(EventDef): diff --git a/ereuse_devicehub/resources/event/models.py b/ereuse_devicehub/resources/event/models.py index dc3164a3..71be088b 100644 --- a/ereuse_devicehub/resources/event/models.py +++ b/ereuse_devicehub/resources/event/models.py @@ -14,7 +14,7 @@ from collections import Iterable from contextlib import suppress from datetime import datetime, timedelta from decimal import Decimal, ROUND_HALF_EVEN, ROUND_UP -from typing import Optional, Set, Union, Tuple +from typing import Optional, Set, Union from uuid import uuid4 import inflection @@ -40,10 +40,10 @@ from ereuse_devicehub.db import db from ereuse_devicehub.resources.agent.models import Agent from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Desktop, \ Device, Laptop, Server -from ereuse_devicehub.resources.enums import BiosAccessRange, ErasureStandards, \ - PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \ - RatingRange, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \ - TestDataStorageLength, FunctionalityRange, AppearanceRange, BatteryHealthRange +from ereuse_devicehub.resources.enums import AppearanceRange, BatteryHealth, BiosAccessRange, \ + ErasureStandards, FunctionalityRange, PhysicalErasureMethod, PriceSoftware, \ + R_NEGATIVE, R_POSITIVE, RatingRange, ReceiverRole, Severity, SnapshotExpectedEvents, \ + SnapshotSoftware, TestDataStorageLength from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing from ereuse_devicehub.resources.user.models import User @@ -669,7 +669,6 @@ class TestMixin: return Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True) - class MeasureBattery(TestMixin, Test): """A sample of the status of the battery. @@ -708,7 +707,7 @@ class TestDataStorage(TestMixin, Test): assessment = Column(Boolean) reallocated_sector_count = Column(SmallInteger) power_cycle_count = Column(SmallInteger) - reported_uncorrectable_errors = Column(SmallInteger) + _reported_uncorrectable_errors = Column('reported_uncorrectable_errors', Integer) command_timeout = Column(Integer) current_pending_sector_count = Column(SmallInteger) offline_uncorrectable = Column(SmallInteger) @@ -737,6 +736,16 @@ class TestDataStorage(TestMixin, Test): t += self.description return t + @property + def reported_uncorrectable_errors(self): + return self._reported_uncorrectable_errors + + @reported_uncorrectable_errors.setter + def reported_uncorrectable_errors(self, value): + # We assume that a huge number is not meaningful + # So we keep it like this + self._reported_uncorrectable_errors = min(value, db.PSQL_INT_MAX) + class StressTest(TestMixin, Test): """The act of stressing (putting to the maximum capacity) @@ -760,95 +769,84 @@ class TestAudio(TestMixin, Test): """ Test to check all this aspects related with audio functions, Manual Tests?? """ - loudspeaker = Column(Boolean) - loudspeaker.comment = 'Test to determine if the speaker is working properly and what sound quality it has.' - microphone = Column(Boolean) - microphone.comment = 'This evaluate if microphone works correctly' + _speaker = Column('speaker', Boolean) + _speaker.comment = """Whether the speaker works as expected.""" + _microphone = Column('microphone', Boolean) + _microphone.comment = """Whether the microphone works as expected.""" + + @property + def speaker(self): + return self._speaker + + @speaker.setter + def speaker(self, x): + self._speaker = x + self._check() + + @property + def microphone(self): + return self._microphone + + @microphone.setter + def microphone(self, x): + self._microphone = x + self._check() + + def _check(self): + """Sets ``severity`` to ``error`` if any of the variables fail.""" + if not self._speaker or not self._microphone: + self.severity = Severity.Error class TestConnectivity(TestMixin, Test): - """ - Test to check all this aspects related with functionality connections in devices - """ - # TODO Add Severity and return unique score - cellular_network = Column(Boolean) - cellular_network.comment = 'Evaluate if cellular network works properly' - wifi = Column(Boolean) - wifi.comment = 'Evaluate if wifi connection works correctly' - bluetooth = Column(Boolean) - bluetooth.comment = 'Evaluate if bluetooth works' - usb_port = Column(Boolean) - usb_port.comment = 'Evaluate if usb port was detected and charger plug works' - locked = Column(Boolean) - locked.comment = 'Test to check if devices is locked' + """Tests that the device can connect both physically and + wirelessly. - -class TestBattery(TestMixin, Test): + A failing test means that at least one connection of the device + is not working well. A comment should get into more detail. """ - Test battery health, status and length of charge. Minimum X minutes discharging the device - """ - # TODO how to determinate if test PASS depend on battery stat and/or health - battery_stat = Column(Boolean) - battery_stat.comment = """ - Some batteries can report a self-check life status. - """ - battery_health = Column(DBEnum(BatteryHealthRange)) - battery_health.comment = BatteryHealthRange.__doc__ class TestCamera(TestMixin, Test): + """Tests the working conditions of the camera of the device, + specially when taking pictures or recording video. """ - Test to determinate functionality and defects on camera when take pictures or record video - # TODO define when test FAIL - """ - camera = Column(Boolean) - camera.comment = "" class TestKeyboard(TestMixin, Test): - """ - Test to determinate if keyboard layout are and works correctly - # TODO define when test FAIL - """ - keyboard = Column(Boolean) - keyboard.comment = "" + """Whether the keyboard works correctly.""" class TestTrackpad(TestMixin, Test): - """ - Test trackpad works correctly - # TODO define when test FAIL - """ - trackpad = Column(Boolean) - trackpad.comment = "" + """Whether the trackpad works correctly.""" class TestBios(TestMixin, Test): - """ - Test that determinate motherboard no beeps, codes or errors when power on. - And a grade to reflect some possibles difficult to access or modify setting in the BIOS, like password protection.. - """ + """Tests the working condition and grades the usability of the BIOS.""" bios_power_on = Column(Boolean) - bios_power_on.comment = """ - Motherboards do a self check when powering up (R2 p.23), test PASS if no beeps, codes, or errors appears. + bios_power_on.comment = """Whether there are no beeps or error + codes when booting up. + + Reference: R2 standard page 23. """ access_range = Column(DBEnum(BiosAccessRange)) - access_range.comment = 'Range of difficult to access BIOS' + access_range.comment = """Difficulty to modify the boot menu. + + This is used as an usability measure for accessing and modifying + a bios, specially as something as important as modifying the boot + menu.""" -class TestVisual(TestMixin, Test): - """ - Manual rate test its are represented with grade and focuses mainly on - the aesthetic or cosmetic defects of important parts of a device. - Like defects on chassis, display, .. +class VisualTest(TestMixin, Test): + """The act of visually inspecting the appearance and functionality + of the device. """ appearance_range = Column(DBEnum(AppearanceRange)) appearance_range.comment = AppearanceRange.__doc__ functionality_range = Column(DBEnum(FunctionalityRange)) functionality_range.comment = FunctionalityRange.__doc__ labelling = Column(Boolean) - labelling.comment = """Whether there are tags to be removed. - """ + labelling.comment = """Whether there are tags to be removed.""" def __str__(self) -> str: return super().__str__() + '. Appearance {} and functionality {}'.format( @@ -858,29 +856,54 @@ class TestVisual(TestMixin, Test): class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice): - """The act of computing a rate based on different categories: - 1. Quality: the appearance, performance, and functionality - of a device. - - - There are two ways of rating a device: - - 1. When processing the device with Workbench and the Android App. - 2. Anytime after with the Android App or website.X + """The act of computing a rate based on different categories""" + # todo jn: explain in each comment what the rate considers. + N = 2 + """The number of significant digits for rates. + Values are rounded and stored to it. """ - rating = Column(Float(decimal_return_scale=2), check_range('rating', *RATE_POSITIVE)) - rating.comment = """The rating for the content.""" + _rating = Column('rating', Float(decimal_return_scale=N), check_range('rating', *R_POSITIVE)) + _rating.comment = """The rating for the content.""" version = Column(StrictVersionType) version.comment = """The version of the software.""" - appearance = Column(Float(decimal_return_scale=2), check_range('appearance', *RATE_NEGATIVE)) - functionality = Column(Float(decimal_return_scale=2), - check_range('functionality', *RATE_NEGATIVE)) + _appearance = Column('appearance', + Float(decimal_return_scale=N), + check_range('appearance', *R_NEGATIVE)) + _appearance.comment = """""" + _functionality = Column('functionality', + Float(decimal_return_scale=N), + check_range('functionality', *R_NEGATIVE)) + _functionality.comment = """""" + + @property + def rating(self): + return self._rating + + @rating.setter + def rating(self, x): + self._rating = round(max(x, 0), self.N) + + @property + def appearance(self): + return self._appearance + + @appearance.setter + def appearance(self, x): + self._appearance = round(x, self.N) + + @property + def functionality(self): + return self._functionality + + @functionality.setter + def functionality(self, x): + self._functionality = round(x, self.N) @property def rating_range(self) -> RatingRange: - if self.rating: - return RatingRange.from_score(self.rating) + """""" + return RatingRange.from_score(self.rating) if self.rating else None @declared_attr def __mapper_args__(cls): @@ -901,54 +924,82 @@ class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice): @classmethod def compute(cls, device) -> 'RateComputer': - """ - The act of compute general computer rate - """ raise NotImplementedError() -class RateComputer(Rate): - """ - Main class to group by device type: Computer - Computer is broadly extended by ``Desktop``, ``Laptop``, and - ``Server``. - """ - id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True) +class RateMixin: + @declared_attr + def id(cls): + return Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True) - processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE), - comment='Is a test explain cpu component.') - ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE), - comment='RAM memory rate.') - data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE), - comment='Data storage rate, like HHD, SSD.') - graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE), - comment='Graphic card rate.') - def __init__(self, **kwargs) -> None: - super().__init__(**kwargs) +class RateComputer(RateMixin, Rate): + """The act of rating a computer.""" + _processor = Column('processor', + Float(decimal_return_scale=Rate.N), + check_range('processor', *R_POSITIVE)) + _processor.comment = """The rate of the Processor.""" + _ram = Column('ram', Float(decimal_return_scale=Rate.N), check_range('ram', *R_POSITIVE)) + _ram.comment = """The rate of the RAM.""" + _data_storage = Column('data_storage', + Float(decimal_return_scale=Rate.N), + check_range('data_storage', *R_POSITIVE)) + _data_storage.comment = """'Data storage rate, like HHD, SSD.'""" + _graphic_card = Column('graphic_card', + Float(decimal_return_scale=Rate.N), + check_range('graphic_card', *R_POSITIVE)) + _graphic_card.comment = 'Graphic card rate.' + + @property + def processor(self): + return self._processor + + @processor.setter + def processor(self, x): + self._processor = round(x, self.N) + + @property + def ram(self): + return self._ram + + @ram.setter + def ram(self, x): + self._ram = round(x, self.N) + + @property + def data_storage(self): + return self._data_storage + + @data_storage.setter + def data_storage(self, x): + self._data_storage = round(x, self.N) + + @property + def graphic_card(self): + return self._graphic_card + + @graphic_card.setter + def graphic_card(self, x): + self._graphic_card = round(x, self.N) @property def data_storage_range(self): - if self.data_storage: - return RatingRange.from_score(self.data_storage) + return RatingRange.from_score(self.data_storage) if self.data_storage else None @property def ram_range(self): - if self.ram: - return RatingRange.from_score(self.ram) + return RatingRange.from_score(self.ram) if self.ram else None @property def processor_range(self): - if self.processor: - return RatingRange.from_score(self.processor) + return RatingRange.from_score(self.processor) if self.processor else None @property def graphic_card_range(self): - if self.graphic_card: - return RatingRange.from_score(self.graphic_card) + return RatingRange.from_score(self.graphic_card) if self.graphic_card else None @classmethod - def compute(cls, device) -> Tuple['RateComputer', 'Price']: + def compute(cls, device): """ The act of compute general computer rate """ @@ -1004,7 +1055,7 @@ class Price(JoinedWithOneDeviceMixin, EventWithOneDevice): @classmethod def to_price(cls, value: Union[Decimal, float], rounding=ROUND) -> Decimal: """Returns a Decimal value with the correct scale for Price.price.""" - if isinstance(value, float): + if isinstance(value, (float, int)): value = Decimal(value) # equation from marshmallow.fields.Decimal return value.quantize(Decimal((0, (1,), -cls.SCALE)), rounding=rounding) @@ -1088,7 +1139,7 @@ class EreusePrice(Price): self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price) def __init__(self, rating: RateComputer, **kwargs) -> None: - if rating.rating_range == RatingRange.VERY_LOW: + if not rating.rating_range or rating.rating_range == RatingRange.VERY_LOW: raise InvalidRangeForPrice() # We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP) diff --git a/ereuse_devicehub/resources/event/models.pyi b/ereuse_devicehub/resources/event/models.pyi index fd274c80..e6ef5165 100644 --- a/ereuse_devicehub/resources/event/models.pyi +++ b/ereuse_devicehub/resources/event/models.pyi @@ -2,7 +2,7 @@ import ipaddress from datetime import datetime, timedelta from decimal import Decimal from distutils.version import StrictVersion -from typing import Dict, List, Optional, Set, Union +from typing import Dict, List, Optional, Set, Tuple, Union from uuid import UUID from boltons import urlutils @@ -16,9 +16,9 @@ from teal.enums import Country from ereuse_devicehub.resources.agent.models import Agent from ereuse_devicehub.resources.device.models import Component, Computer, Device -from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ErasureStandards, \ - FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RatingSoftware, ReceiverRole, \ - Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength, BiosAccessRange +from ereuse_devicehub.resources.enums import AppearanceRange, ErasureStandards, \ + FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RatingRange, \ + ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength from ereuse_devicehub.resources.models import Thing from ereuse_devicehub.resources.user.models import User @@ -42,7 +42,7 @@ class Event(Thing): severity = ... # type: Column def __init__(self, **kwargs) -> None: - super().__init__(created, updated) + super().__init__(**kwargs) self.id = ... # type: UUID self.name = ... # type: str self.type = ... # type: str @@ -75,7 +75,6 @@ class Event(Thing): class EventWithOneDevice(Event): - def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.device = ... # type: Device @@ -178,8 +177,7 @@ class Install(EventWithOneDevice): self.address = ... # type: Optional[int] -class SnapshotRequest(Mod assert rate_computer.rating == 4.61 -el): +class SnapshotRequest(Model): def __init__(self, **kwargs) -> None: self.id = ... # type: UUID self.request = ... # type: dict @@ -226,6 +224,7 @@ class BenchmarkGraphicCard(BenchmarkWithRate): class Test(EventWithOneDevice): elapsed = ... # type: Column + def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.elapsed = ... # type: Optional[timedelta] @@ -233,6 +232,20 @@ class Test(EventWithOneDevice): class TestDataStorage(Test): + length = ... # type: Column + status = ... # type: Column + lifetime = ... # type: Column + first_error = ... # type: Column + passed_lifetime = ... # type: Column + assessment = ... # type: Column + reallocated_sector_count = ... # type: Column + power_cycle_count = ... # type: Column + reported_uncorrectable_errors = ... # type: Column + command_timeout = ... # type: Column + current_pending_sector_count = ... # type: Column + offline_uncorrectable = ... # type: Column + remaining_lifetime_percentage = ... # type: Column + def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.id = ... # type: UUID @@ -259,70 +272,92 @@ class TestAudio(Test): """ Test to check all this aspects related with audio functions, Manual Tests?? """ - loudspeaker = ... # type: Column - microphone = ... # type: Column + _speaker = ... # type: Column + _microphone = ... # type: Column + + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + self.speaker = ... # type: bool + self.microphone = ... # type: bool class TestConnectivity(Test): - cellular_network = ... # type: Column - wifi = ... # type: Column - bluetooth = ... # type: Column - usb_port = ... # type: Column - locked = ... # type: Column - - -class TestBattery(Test): - battery_stat = ... # type: Column - battery_health = ... # type: Column + pass class TestCamera(Test): - camera = ... # type: Column + pass class TestKeyboard(Test): - keyboard = ... # type: Column + pass class TestTrackpad(Test): - trackpad = ... # type: Column + pass class TestBios(Test): bios_power_on = ... # type: Column - access_range = ... # type: BiosAccessRange + access_range = ... # type: Column -class TestVisual(ManualRate): +class VisualTest(Test): appearance_range = ... # type: AppearanceRange functionality_range = ... # type: FunctionalityRange labelling = ... # type: Column class Rate(EventWithOneDevice): - rating = ... # type: Column - appearance = ... # type: Column - functionality = ... # type: Column + N = 2 + _rating = ... # type: Column + _appearance = ... # type: Column + _functionality = ... # type: Column version = ... # type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.rating = ... # type: float - self.software = ... # type: RatingSoftware self.version = ... # type: StrictVersion self.appearance = ... # type: float self.functionality = ... # type: float - self.rating_range = ... # type: str + + @property + def rating_range(self) -> RatingRange: + pass class RateComputer(Rate): - id = ... - processor = ... - ram = ... - data_storage = ... + _processor = ... # type: Column + _ram = ... # type: Column + _data_storage = ... # type: Column + _graphic_card = ... # type: Column + + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + self.processor = ... # type: float + self.ram = ... # type: float + self.data_storage = ... # type: float + self.graphic_card = ... # type: float @classmethod - def compute(cls, device): + def compute(cls, device: Device) -> Tuple[RateComputer, EreusePrice]: + pass + + @property + def data_storage_range(self) -> Optional[RatingRange]: + pass + + @property + def ram_range(self) -> Optional[RatingRange]: + pass + + @property + def processor_range(self) -> Optional[RatingRange]: + pass + + @property + def graphic_card_range(self) -> Optional[RatingRange]: pass diff --git a/ereuse_devicehub/resources/event/rate/rate.py b/ereuse_devicehub/resources/event/rate/rate.py index eca005c4..3e11cb9f 100644 --- a/ereuse_devicehub/resources/event/rate/rate.py +++ b/ereuse_devicehub/resources/event/rate/rate.py @@ -1,6 +1,5 @@ -from typing import Iterable - import math +from typing import Iterable from ereuse_devicehub.resources.device.models import Device diff --git a/ereuse_devicehub/resources/event/rate/workbench/v1_0.py b/ereuse_devicehub/resources/event/rate/workbench/v1_0.py index 9f7a78d3..ebd53e80 100644 --- a/ereuse_devicehub/resources/event/rate/workbench/v1_0.py +++ b/ereuse_devicehub/resources/event/rate/workbench/v1_0.py @@ -1,18 +1,20 @@ from enum import Enum from itertools import groupby -from typing import Iterable +from typing import Dict, Iterable, Tuple -from ereuse_devicehub.resources.device.models import Computer, DataStorage, Processor, RamModule +from ereuse_devicehub.resources.device.models import Computer, DataStorage, Processor, \ + RamModule from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \ - RateComputer, TestVisual, BenchmarkProcessorSysbench -# todo if no return assign then rate_c = 1 is assigned -# todo fix corner cases, like components characteristics == None + BenchmarkProcessorSysbench, RateComputer, VisualTest from ereuse_devicehub.resources.event.rate.rate import BaseRate class RateAlgorithm(BaseRate): - """ - Rate all components in Computer + """The algorithm that generates the Rate v1.0. + + Do not call directly this class, but use + :meth:`ereuse_devicehub.resources.event.models.RateComputer.compute`, + which then calls this. """ class Range(Enum): @@ -43,48 +45,43 @@ class RateAlgorithm(BaseRate): Processor.t: ('processor', ProcessorRate()), RamModule.t: ('ram', RamRate()), DataStorage.t: ('data_storage', DataStorageRate()) - } + } # type: Dict[str, Tuple[str, BaseRate]] - def compute(self, device: Computer): + def compute(self, device: Computer) -> RateComputer: + """Generates a new + :class:`ereuse_devicehub.resources.event.models.RateComputer` + for the passed-in device. """ - Compute RateComputer is a rate (score) ranging from 0 to 4.7 - that represents estimating value of use of desktop and laptop computer components. - """ - if not isinstance(device, Computer): # todo can be an assert? - raise CannotRate('Can only rate computers.') + assert isinstance(device, Computer), 'Can only rate computers' + + try: + test_visual = device.last_event_of(VisualTest) + except LookupError: + raise CannotRate('You need a visual test.') rate = RateComputer() - rate.processor = rate.data_storage = rate.ram = 1 # Init - # Group cpus, rams, storages and compute their rate + # Group cpus, rams, storage and compute their rate # Treat the same way with HardDrive and SolidStateDrive like (DataStorage) clause = lambda x: DataStorage.t if isinstance(x, DataStorage) else x.t c = (c for c in device.components if clause(c) in set(self.RATES.keys())) for type, components in groupby(sorted(c, key=clause), key=clause): if type == Processor.t: # ProcessorRate.compute expects only 1 processor components = next(components) - field, rate_cls = self.RATES[type] # type: str, BaseRate - result = rate_cls.compute(components, rate) + field, rate_cls = self.RATES[type] + result = rate_cls.compute(components) if result: setattr(rate, field, result) - # TODO is necessary check if TestVisual exists?? cause StopIteration Error - try: - test_visual = next(e for e in device.events if isinstance(e, TestVisual)) - except StopIteration: - raise CannotRate('You need a visual test.') rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram) rate.appearance = self.Appearance.from_devicehub(test_visual.appearance_range).value - rate.functionality = self.Functionality.from_devicehub(test_visual.functionality_range).value + rate.functionality = self.Functionality.from_devicehub( + test_visual.functionality_range).value - rate.rating = round(max(rate_components + rate.functionality + rate.appearance, 0), 2) - rate.appearance = round(rate.appearance, 2) - rate.functionality = round(rate.functionality, 2) - rate.processor = round(rate.processor, 2) - rate.ram = round(rate.ram, 2) - rate.data_storage = round(rate.data_storage, 2) + rate.rating = rate_components + rate.functionality + rate.appearance device.events_one.add(rate) + assert 0 <= rate.rating <= 4.7, 'Rate ranges from 0 to 4.7' return rate @@ -101,18 +98,20 @@ class ProcessorRate(BaseRate): # Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz, score = 23406.92 but results inan score of 17503. DEFAULT_SCORE = 4000 - def compute(self, processor: Processor, rate: RateComputer): + def compute(self, processor: Processor): """ Compute processor rate Obs: cores and speed are possible NULL value :return: result is a rate (score) of Processor characteristics """ - # todo for processor_device in processors; more than one processor + # todo jn? for processor_device in processors; more than one processor cores = processor.cores or self.DEFAULT_CORES speed = processor.speed or self.DEFAULT_SPEED - # todo fix StopIteration if don't exists BenchmarkProcessor - benchmark_cpu = next(e for e in processor.events if - isinstance(e, BenchmarkProcessor) and not isinstance(e, BenchmarkProcessorSysbench)) - # todo fix if benchmark_cpu.rate == 0 + # todo jn? fix StopIteration if don't exists BenchmarkProcessor + benchmark_cpu = next( + e for e in reversed(processor.events) + if isinstance(e, BenchmarkProcessor) and not isinstance(e, BenchmarkProcessorSysbench) + ) + # todo jn? fix if benchmark_cpu.rate == 0 benchmark_cpu = benchmark_cpu.rate or self.DEFAULT_SCORE # STEP: Fusion components @@ -129,8 +128,6 @@ class ProcessorRate(BaseRate): processor_rate = self.rate_lin(processor_norm) if processor_norm >= self.CLOG: processor_rate = self.rate_log(processor_norm) - - assert processor_rate, 'Could not rate processor.' return processor_rate @@ -146,7 +143,7 @@ class RamRate(BaseRate): # ram.size.weight; ram.speed.weight; RAM_WEIGHTS = 0.7, 0.3 - def compute(self, ram_devices: Iterable[RamModule], rate: RateComputer): + def compute(self, ram_devices: Iterable[RamModule]): """ Obs: RamModule.speed is possible NULL value & size != NULL or NOT?? :return: result is a rate (score) of all RamModule components @@ -203,7 +200,7 @@ class DataStorageRate(BaseRate): # drive.size.weight; drive.readingSpeed.weight; drive.writingSpeed.weight; DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25 - def compute(self, data_storage_devices: Iterable[DataStorage], rate: RateComputer): + def compute(self, data_storage_devices: Iterable[DataStorage]): """ Obs: size != NULL and 0 value & read_speed and write_speed != NULL :return: result is a rate (score) of all DataStorage devices @@ -215,7 +212,7 @@ class DataStorageRate(BaseRate): # STEP: Filtering, data cleaning and merging of component parts for storage in data_storage_devices: # We assume all hdd snapshots have BenchmarkDataStorage - benchmark = next(e for e in storage.events if isinstance(e, BenchmarkDataStorage)) + benchmark = storage.last_event_of(BenchmarkDataStorage) # prevent NULL values _size = storage.size or 0 size += _size diff --git a/ereuse_devicehub/resources/event/schemas.py b/ereuse_devicehub/resources/event/schemas.py index dc9677c4..0f12e7f3 100644 --- a/ereuse_devicehub/resources/event/schemas.py +++ b/ereuse_devicehub/resources/event/schemas.py @@ -13,8 +13,8 @@ from ereuse_devicehub.resources import enums from ereuse_devicehub.resources.agent import schemas as s_agent from ereuse_devicehub.resources.device import schemas as s_device from ereuse_devicehub.resources.enums import AppearanceRange, BiosAccessRange, FunctionalityRange, \ - PhysicalErasureMethod, PriceSoftware, RATE_POSITIVE, RatingRange, ReceiverRole, \ - Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength, BatteryHealthRange + PhysicalErasureMethod, R_POSITIVE, RatingRange, ReceiverRole, \ + Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength from ereuse_devicehub.resources.event import models as m from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE from ereuse_devicehub.resources.schemas import Thing @@ -176,38 +176,24 @@ class StressTest(Test): class TestAudio(Test): __doc__ = m.TestAudio.__doc__ - loudspeaker = Boolean() - microphone = Boolean() + speaker = Boolean(description=m.TestAudio._speaker.comment) + microphone = Boolean(description=m.TestAudio._microphone.comment) class TestConnectivity(Test): __doc__ = m.TestConnectivity.__doc__ - cellular_network = Boolean() - wifi = Boolean() - bluetooth = Boolean() - usb_port = Boolean() - locked = Boolean() - - -class TestBattery(Test): - __doc__ = m.TestBattery.__doc__ - battery_stat = Boolean() - battery_health = EnumField(BatteryHealthRange, data_key='batteryHealthRange') class TestCamera(Test): __doc__ = m.TestCamera.__doc__ - camera = Boolean() class TestKeyboard(Test): __doc__ = m.TestKeyboard.__doc__ - keyboard = Boolean() class TestTrackpad(Test): __doc__ = m.TestTrackpad.__doc__ - trackpad = Boolean() class TestBios(Test): @@ -216,8 +202,8 @@ class TestBios(Test): access_range = EnumField(BiosAccessRange, data_key='accessRange') -class TestVisual(Test): - __doc__ = m.TestVisual.__doc__ +class VisualTest(Test): + __doc__ = m.VisualTest.__doc__ appearance_range = EnumField(AppearanceRange, data_key='appearanceRange') functionality_range = EnumField(FunctionalityRange, data_key='functionalityRange') labelling = Boolean() @@ -225,14 +211,21 @@ class TestVisual(Test): class Rate(EventWithOneDevice): __doc__ = m.Rate.__doc__ - rating = Integer(validate=Range(*RATE_POSITIVE), + rating = Integer(validate=Range(*R_POSITIVE), dump_only=True, - description=m.Rate.rating.comment) + description=m.Rate._rating.comment) version = Version(dump_only=True, description=m.Rate.version.comment) - appearance = Integer(validate=Range(-3, 5), dump_only=True) - functionality = Integer(validate=Range(-3, 5), dump_only=True) - rating_range = EnumField(RatingRange, dump_only=True, data_key='ratingRange') + appearance = Integer(validate=Range(enums.R_NEGATIVE), + dump_only=True, + description=m.Rate._appearance.comment) + functionality = Integer(validate=Range(enums.R_NEGATIVE), + dump_only=True, + description=m.Rate._functionality.comment) + rating_range = EnumField(RatingRange, + dump_only=True, + data_key='ratingRange', + description=m.Rate.rating_range.__doc__) class RateComputer(Rate): diff --git a/ereuse_devicehub/resources/event/views.py b/ereuse_devicehub/resources/event/views.py index b8794eaa..72819176 100644 --- a/ereuse_devicehub/resources/event/views.py +++ b/ereuse_devicehub/resources/event/views.py @@ -1,4 +1,3 @@ -from contextlib import suppress from distutils.version import StrictVersion from typing import List from uuid import UUID @@ -11,8 +10,7 @@ from teal.resource import View from ereuse_devicehub.db import db from ereuse_devicehub.resources.device.models import Component, Computer from ereuse_devicehub.resources.enums import SnapshotSoftware -from ereuse_devicehub.resources.event.models import Event, Snapshot, Rate, RateComputer, InvalidRangeForPrice, \ - EreusePrice +from ereuse_devicehub.resources.event.models import Event, RateComputer, Snapshot from ereuse_devicehub.resources.event.rate.workbench.v1_0 import CannotRate SUPPORTED_WORKBENCH = StrictVersion('11.0') @@ -94,7 +92,6 @@ class EventView(View): if price: snapshot.events.add(price) - db.session.add(snapshot) db.session().final_flush() ret = self.schema.jsonify(snapshot) # transform it back diff --git a/ereuse_devicehub/resources/lot/dag.sql b/ereuse_devicehub/resources/lot/dag.sql index 3f4b4840..dd4a94c8 100644 --- a/ereuse_devicehub/resources/lot/dag.sql +++ b/ereuse_devicehub/resources/lot/dag.sql @@ -1,109 +1,115 @@ CREATE OR REPLACE FUNCTION add_edge(parent_id uuid, child_id uuid) - /* Adds an edge between ``parent`` and ``child``. + /* Adds an edge between ``parent`` and ``child``. - Designed to work with Directed Acyclic Graphs (DAG) - (or said in another way, trees with multiple parents without cycles). + Designed to work with Directed Acyclic Graphs (DAG) + (or said in another way, trees with multiple parents without cycles). - This method will raise an exception if: - - Parent is the same as child. - - Child contains the parent. - - Edge parent - child already exists. + This method will raise an exception if: + - Parent is the same as child. + - Child contains the parent. + - Edge parent - child already exists. - Influenced by: - - https://www.codeproject.com/Articles/22824/A-Model-to-Represent-Directed-Acyclic-Graphs-DAG - - http://patshaughnessy.net/2017/12/12/installing-the-postgres-ltree-extension - - https://en.wikipedia.org/wiki/Directed_acyclic_graph - */ - RETURNS void AS $$ + Influenced by: + - https://www.codeproject.com/Articles/22824/A-Model-to-Represent-Directed-Acyclic-Graphs-DAG + - http://patshaughnessy.net/2017/12/12/installing-the-postgres-ltree-extension + - https://en.wikipedia.org/wiki/Directed_acyclic_graph + */ + RETURNS void AS +$$ DECLARE - parent text := replace(CAST(parent_id as text), '-', '_'); - child text := replace(CAST(child_id as text), '-', '_'); + parent text := replace(CAST(parent_id as text), '-', '_'); + child text := replace(CAST(child_id as text), '-', '_'); BEGIN - if parent = child - then - raise exception 'Cannot create edge: the parent is the same as the child.'; - end if; + if parent = child + then + raise exception 'Cannot create edge: the parent is the same as the child.'; + end if; - if EXISTS ( - SELECT 1 FROM path where path.path ~ CAST('*.' || child || '.*.' || parent || '.*' as lquery) - ) - then - raise exception 'Cannot create edge: child already contains parent.'; - end if; + if EXISTS( + SELECT 1 + FROM path + where path.path ~ CAST('*.' || child || '.*.' || parent || '.*' as lquery) + ) + then + raise exception 'Cannot create edge: child already contains parent.'; + end if; - -- We have two subgraphs: the parent subgraph that goes from the parent to the root, - -- and the child subgraph, going from the child (which is the root of this subgraph) - -- to all the leafs. - -- We do the cartesian product from all the paths of the parent subgraph that end in the parent - -- WITH all the paths that start from the child that end to its leafs. - insert into path (lot_id, path) ( - select distinct lot_id, fp.path || subpath(path.path, index(path.path, text2ltree(child))) - from path, (select path.path from path where path.path ~ CAST('*.' || parent AS lquery)) as fp - where path.path ~ CAST('*.' || child || '.*' AS lquery) - ); - -- Cleanup: old paths that start with the child (that where used above in the cartesian product) - -- have became a subset of the result of the cartesian product, thus being redundant. - delete from path where path.path ~ CAST(child || '.*' AS lquery); + -- We have two subgraphs: the parent subgraph that goes from the parent to the root, + -- and the child subgraph, going from the child (which is the root of this subgraph) + -- to all the leafs. + -- We do the cartesian product from all the paths of the parent subgraph that end in the parent + -- WITH all the paths that start from the child that end to its leafs. + insert into path (lot_id, path) ( + select distinct lot_id, fp.path || subpath(path.path, index(path.path, text2ltree(child))) + from path, + (select path.path from path where path.path ~ CAST('*.' || parent AS lquery)) as fp + where path.path ~ CAST('*.' || child || '.*' AS lquery) + ); + -- Cleanup: old paths that start with the child (that where used above in the cartesian product) + -- have became a subset of the result of the cartesian product, thus being redundant. + delete from path where path.path ~ CAST(child || '.*' AS lquery); END $$ -LANGUAGE plpgsql; + LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION delete_edge(parent_id uuid, child_id uuid) - /* Deletes an edge between ``parent`` and ``child``. + /* Deletes an edge between ``parent`` and ``child``. - Designed to work with DAG (See ``add_edge`` function). + Designed to work with DAG (See ``add_edge`` function). - This method will raise an exception if the relationship does not - exist. - */ - RETURNS void AS $$ + This method will raise an exception if the relationship does not + exist. + */ + RETURNS void AS +$$ DECLARE - parent text := replace(CAST(parent_id as text), '-', '_'); - child text := replace(CAST(child_id as text), '-', '_'); - number int; + parent text := replace(CAST(parent_id as text), '-', '_'); + child text := replace(CAST(child_id as text), '-', '_'); + number int; BEGIN - -- to delete we remove from the path of the descendants of the child - -- (and the child) any ancestor coming from this edge. - -- When we added the edge we did a cartesian product. When removing - -- this part of the path we will have duplicate paths. + -- to delete we remove from the path of the descendants of the child + -- (and the child) any ancestor coming from this edge. + -- When we added the edge we did a cartesian product. When removing + -- this part of the path we will have duplicate paths. - -- don't check uniqueness for path key until we delete duplicates - SET CONSTRAINTS path_unique DEFERRED; + -- don't check uniqueness for path key until we delete duplicates + SET CONSTRAINTS path_unique DEFERRED; - -- remove everything above the child lot_id in the path - -- this creates duplicates on path and lot_id - update path - set path = subpath(path, index(path, text2ltree(child))) - where path ~ CAST('*.' || parent || '.' || child || '.*' AS lquery); + -- remove everything above the child lot_id in the path + -- this creates duplicates on path and lot_id + update path + set path = subpath(path, index(path, text2ltree(child))) + where path ~ CAST('*.' || parent || '.' || child || '.*' AS lquery); - -- remove duplicates - -- we need an id field exclusively for this operation - -- from https://wiki.postgresql.org/wiki/Deleting_duplicates - DELETE - FROM path - WHERE id IN (SELECT id - FROM (SELECT id, ROW_NUMBER() OVER (partition BY lot_id, path) AS rnum FROM path) t - WHERE t.rnum > 1); + -- remove duplicates + -- we need an id field exclusively for this operation + -- from https://wiki.postgresql.org/wiki/Deleting_duplicates + DELETE + FROM path + WHERE id IN (SELECT id + FROM (SELECT id, ROW_NUMBER() OVER (partition BY lot_id, path) AS rnum + FROM path) t + WHERE t.rnum > 1); - -- re-activate uniqueness check and perform check - -- todo we should put this in a kind-of finally clause - SET CONSTRAINTS path_unique IMMEDIATE; + -- re-activate uniqueness check and perform check + -- todo we should put this in a kind-of finally clause + SET CONSTRAINTS path_unique IMMEDIATE; - -- After the update the one of the paths of the child will be - -- containing only the child. - -- This can only be when the child has no parent at all. - -- In case the child has more than one parent, remove this path - -- (note that we want it to remove it too from descendants of this - -- child, ex. 'child_id'.'desc1') - select COUNT(1) into number from path where lot_id = child_id; - IF number > 1 - THEN - delete from path where path <@ text2ltree(child); - end if; + -- After the update the one of the paths of the child will be + -- containing only the child. + -- This can only be when the child has no parent at all. + -- In case the child has more than one parent, remove this path + -- (note that we want it to remove it too from descendants of this + -- child, ex. 'child_id'.'desc1') + select COUNT(1) into number from path where lot_id = child_id; + IF number > 1 + THEN + delete from path where path <@ text2ltree(child); + end if; END $$ -LANGUAGE plpgsql; + LANGUAGE plpgsql; diff --git a/ereuse_devicehub/resources/tag/schema.py b/ereuse_devicehub/resources/tag/schema.py index 22a492d5..8c2c355b 100644 --- a/ereuse_devicehub/resources/tag/schema.py +++ b/ereuse_devicehub/resources/tag/schema.py @@ -1,5 +1,5 @@ -from sqlalchemy.util import OrderedSet from marshmallow.fields import Boolean +from sqlalchemy.util import OrderedSet from teal.marshmallow import SanitizedStr, URL from ereuse_devicehub.marshmallow import NestedOn diff --git a/tests/files/asus-1001pxd.snapshot.yaml b/tests/files/asus-1001pxd.snapshot.yaml index a8e64814..723db9e2 100644 --- a/tests/files/asus-1001pxd.snapshot.yaml +++ b/tests/files/asus-1001pxd.snapshot.yaml @@ -118,7 +118,7 @@ { "appearanceRange": "A", "functionalityRange": "A", - "type": "TestVisual" + "type": "VisualTest" } ], "manufacturer": "ASUSTeK Computer INC.", diff --git a/tests/files/basic.snapshot.yaml b/tests/files/basic.snapshot.yaml index ecbd8779..fd976e83 100644 --- a/tests/files/basic.snapshot.yaml +++ b/tests/files/basic.snapshot.yaml @@ -10,7 +10,7 @@ device: model: d1ml manufacturer: d1mr events: - - type: TestVisual + - type: VisualTest appearanceRange: A functionalityRange: B components: diff --git a/tests/files/desktop-9644w8n-lenovo-0169622.snapshot.yaml b/tests/files/desktop-9644w8n-lenovo-0169622.snapshot.yaml index 0b98a32b..815757b5 100644 --- a/tests/files/desktop-9644w8n-lenovo-0169622.snapshot.yaml +++ b/tests/files/desktop-9644w8n-lenovo-0169622.snapshot.yaml @@ -92,7 +92,7 @@ { "appearanceRange": "D", "functionalityRange": "D", - "type": "TestVisual" + "type": "VisualTest" }, { "elapsed": 300, diff --git a/tests/files/laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot.yaml b/tests/files/laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot.yaml index bbf31071..bacf45dd 100644 --- a/tests/files/laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot.yaml +++ b/tests/files/laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot.yaml @@ -137,7 +137,7 @@ { "appearanceRange": "A", "functionalityRange": "A", - "type": "TestVisual" + "type": "VisualTest" }, { "elapsed": 300, diff --git a/tests/test_basic.py b/tests/test_basic.py index d40f0ad1..f882f51e 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -42,4 +42,4 @@ def test_api_docs(client: Client): 'scheme': 'basic', 'name': 'Authorization' } - assert len(docs['definitions']) == 102 + assert len(docs['definitions']) == 101 diff --git a/tests/test_device.py b/tests/test_device.py index c2c69189..db2a7665 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -1,5 +1,4 @@ import datetime -from datetime import timedelta from uuid import UUID import pytest @@ -23,7 +22,7 @@ from ereuse_devicehub.resources.device.sync import MismatchBetweenTags, Mismatch from ereuse_devicehub.resources.enums import ComputerChassis, DisplayTech, Severity, \ SnapshotSoftware from ereuse_devicehub.resources.event import models as m -from ereuse_devicehub.resources.event.models import Remove, Test +from ereuse_devicehub.resources.event.models import Remove, TestConnectivity from ereuse_devicehub.resources.tag.model import Tag from ereuse_devicehub.resources.user import User from tests import conftest @@ -391,14 +390,14 @@ def test_get_device(app: Devicehub, user: UserClient): ]) db.session.add(pc) # todo test is an abstract class. replace with another one - db.session.add(Test(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, _ = user.get(res=d.Device, item=1) assert len(pc['events']) == 1 - assert pc['events'][0]['type'] == 'Test' + assert pc['events'][0]['type'] == 'TestConnectivity' assert pc['events'][0]['device'] == 1 assert pc['events'][0]['severity'] == 'Info' assert UUID(pc['events'][0]['author']) diff --git a/tests/test_event.py b/tests/test_event.py index b58ddd00..fa2aa7bb 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -362,7 +362,6 @@ def test_manual_rate_after_workbench_rate(user: UserClient): s = file('real-hp.snapshot.11') snapshot, _ = user.post(s, res=models.Snapshot) device, _ = user.get(res=Device, item=snapshot['device']['id']) - # TODO assert 'B' == device['rate']['appearanceRange'] assert device['rate'] == 1 user.post({ @@ -378,3 +377,27 @@ def test_manual_rate_after_workbench_rate(user: UserClient): @pytest.mark.xfail(reson='Develop an algorithm that can make rates only from manual rates') def test_manual_rate_without_workbench_rate(user: UserClient): pass + + +@pytest.mark.xfail(reson='develop') +def test_measure_battery(): + """Tests the MeasureBattery.""" + # todo jn + + +@pytest.mark.xfail(reson='develop') +def test_test_camera(): + """Tests the TestCamera.""" + # todo jn + + +@pytest.mark.xfail(reson='develop') +def test_test_keyboard(): + """Tests the TestKeyboard.""" + # todo jn + + +@pytest.mark.xfail(reson='develop') +def test_test_trackpad(): + """Tests the TestTrackpad.""" + # todo jn diff --git a/tests/test_rate.py b/tests/test_rate.py index 3407cf45..204ebe2c 100644 --- a/tests/test_rate.py +++ b/tests/test_rate.py @@ -9,8 +9,8 @@ from ereuse_devicehub.resources.device.models import Computer, Desktop, HardDriv RamModule from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, \ FunctionalityRange -from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, \ - BenchmarkProcessor, RateComputer, TestVisual, Snapshot +from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \ + RateComputer, Snapshot, VisualTest from ereuse_devicehub.resources.event.rate.workbench.v1_0 import CannotRate from tests import conftest from tests.conftest import file @@ -28,16 +28,11 @@ def test_workbench_rate_db(): db.session.commit() -@pytest.mark.xfail(reason='AggreagteRate only takes data from WorkbenchRate as for now') +@pytest.mark.xfail(reason='ComputerRate V1 can only be triggered from Workbench snapshot software') def test_rate_workbench_then_manual(): - """Checks that a new Rate is generated with a new rate - value when a TestVisual is performed after performing a - RateComputer. - - The new Rate needs to be computed by the values of - the appearance and funcitonality grade of TestVisual. + """Checks that a new Rate is generated for a snapshot + that is not from Workbench. """ - pass @pytest.mark.usefixtures(conftest.app_context.__name__) @@ -60,11 +55,9 @@ def test_rate(): } # Add test visual with functionality and appearance range - visual_test = TestVisual() - visual_test.appearance_range = AppearanceRange.A - visual_test.functionality_range = FunctionalityRange.A - - pc.events_one.add(visual_test) + VisualTest(appearance_range=AppearanceRange.A, + functionality_range=FunctionalityRange.A, + device=pc) rate, price = RateComputer.compute(pc) # events = pc.events @@ -120,3 +113,14 @@ def test_no_rate_if_device_is_not_computer(user: UserClient): device = file('keyboard.snapshot') user.post(device, res=Snapshot) assert CannotRate + + +@pytest.mark.xfail(reason='Test not developed') +def test_multiple_rates(user: UserClient): + """Tests submitting two rates from Workbench, + ensuring that the tests / benchmarks... + from the first rate do not contaminate the second rate. + + This ensures that rates only takes the last version of events + and components (in case device has new components, for example). + """ diff --git a/tests/test_rate_workbench_v1.py b/tests/test_rate_workbench_v1.py index 19191384..4040d8bd 100644 --- a/tests/test_rate_workbench_v1.py +++ b/tests/test_rate_workbench_v1.py @@ -18,7 +18,7 @@ import pytest from ereuse_devicehub.resources.device.models import Desktop, HardDrive, Processor, RamModule from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, FunctionalityRange from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \ - RateComputer, TestVisual + VisualTest from ereuse_devicehub.resources.event.rate.workbench.v1_0 import DataStorageRate, ProcessorRate, \ RamRate, RateAlgorithm @@ -32,31 +32,31 @@ def test_rate_data_storage_rate(): hdd_1969 = HardDrive(size=476940) hdd_1969.events_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8)) - data_storage_rate = DataStorageRate().compute([hdd_1969], RateComputer()) + data_storage_rate = DataStorageRate().compute([hdd_1969]) - assert math.isclose(data_storage_rate, 4.02, rel_tol=0.001), 'DataStorageRate returns incorrect value(rate)' + assert math.isclose(data_storage_rate, 4.02, rel_tol=0.001) hdd_3054 = HardDrive(size=476940) hdd_3054.events_one.add(BenchmarkDataStorage(read_speed=158, write_speed=34.7)) # calculate DataStorage Rate - data_storage_rate = DataStorageRate().compute([hdd_3054], RateComputer()) + data_storage_rate = DataStorageRate().compute([hdd_3054]) - assert math.isclose(data_storage_rate, 4.07, rel_tol=0.001), 'DataStorageRate returns incorrect value(rate)' + assert math.isclose(data_storage_rate, 4.07, rel_tol=0.001) hdd_81 = HardDrive(size=76319) hdd_81.events_one.add(BenchmarkDataStorage(read_speed=72.2, write_speed=24.3)) - data_storage_rate = DataStorageRate().compute([hdd_81], RateComputer()) + data_storage_rate = DataStorageRate().compute([hdd_81]) - assert math.isclose(data_storage_rate, 2.61, rel_tol=0.001), 'DataStorageRate returns incorrect value(rate)' + assert math.isclose(data_storage_rate, 2.61, rel_tol=0.001) hdd_1556 = HardDrive(size=152587) hdd_1556.events_one.add(BenchmarkDataStorage(read_speed=78.1, write_speed=24.4)) - data_storage_rate = DataStorageRate().compute([hdd_1556], RateComputer()) + data_storage_rate = DataStorageRate().compute([hdd_1556]) - assert math.isclose(data_storage_rate, 3.70, rel_tol=0.001), 'DataStorageRate returns incorrect value(rate)' + assert math.isclose(data_storage_rate, 3.70, rel_tol=0.001) def test_rate_data_storage_size_is_null(): @@ -69,7 +69,7 @@ def test_rate_data_storage_size_is_null(): hdd_null = HardDrive(size=None) hdd_null.events_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0)) - data_storage_rate = DataStorageRate().compute([hdd_null], RateComputer()) + data_storage_rate = DataStorageRate().compute([hdd_null]) assert data_storage_rate is None @@ -79,7 +79,7 @@ def test_rate_no_data_storage(): """ hdd_null = HardDrive() hdd_null.events_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0)) - data_storage_rate = DataStorageRate().compute([hdd_null], RateComputer()) + data_storage_rate = DataStorageRate().compute([hdd_null]) assert data_storage_rate is None @@ -94,7 +94,7 @@ def test_rate_ram_rate(): ram1 = RamModule(size=2048, speed=1333) - ram_rate = RamRate().compute([ram1], RateComputer()) + ram_rate = RamRate().compute([ram1]) # todo rel_tol >= 0.002 assert math.isclose(ram_rate, 2.02, rel_tol=0.002), 'RamRate returns incorrect value(rate)' @@ -109,7 +109,7 @@ def test_rate_ram_rate_2modules(): ram1 = RamModule(size=4096, speed=1600) ram2 = RamModule(size=2048, speed=1067) - ram_rate = RamRate().compute([ram1, ram2], RateComputer()) + ram_rate = RamRate().compute([ram1, ram2]) assert math.isclose(ram_rate, 3.79, rel_tol=0.001), 'RamRate returns incorrect value(rate)' @@ -125,7 +125,7 @@ def test_rate_ram_rate_4modules(): ram3 = RamModule(size=512, speed=667) ram4 = RamModule(size=512, speed=533) - ram_rate = RamRate().compute([ram1, ram2, ram3, ram4], RateComputer()) + ram_rate = RamRate().compute([ram1, ram2, ram3, ram4]) # todo rel_tol >= 0.002 assert math.isclose(ram_rate, 1.993, rel_tol=0.001), 'RamRate returns incorrect value(rate)' @@ -138,7 +138,7 @@ def test_rate_ram_module_size_is_0(): ram0 = RamModule(size=0, speed=888) - ram_rate = RamRate().compute([ram0], RateComputer()) + ram_rate = RamRate().compute([ram0]) assert ram_rate is None @@ -150,13 +150,13 @@ def test_rate_ram_speed_is_null(): ram0 = RamModule(size=2048, speed=None) - ram_rate = RamRate().compute([ram0], RateComputer()) + ram_rate = RamRate().compute([ram0]) assert math.isclose(ram_rate, 1.85, rel_tol=0.002), 'RamRate returns incorrect value(rate)' ram0 = RamModule(size=1024, speed=None) - ram_rate = RamRate().compute([ram0], RateComputer()) + ram_rate = RamRate().compute([ram0]) # todo rel_tol >= 0.004 assert math.isclose(ram_rate, 1.25, rel_tol=0.004), 'RamRate returns incorrect value(rate)' @@ -168,7 +168,7 @@ def test_rate_no_ram_module(): """ ram0 = RamModule() - ram_rate = RamRate().compute([ram0], RateComputer()) + ram_rate = RamRate().compute([ram0]) assert ram_rate is None @@ -184,9 +184,9 @@ def test_rate_processor_rate(): # add score processor benchmark cpu.events_one.add(BenchmarkProcessor(rate=3192.34)) - processor_rate = ProcessorRate().compute(cpu, RateComputer()) + processor_rate = ProcessorRate().compute(cpu) - assert math.isclose(processor_rate, 1, rel_tol=0.001), 'ProcessorRate returns incorrect value(rate)' + assert math.isclose(processor_rate, 1, rel_tol=0.001) def test_rate_processor_rate_2cores(): @@ -199,17 +199,17 @@ def test_rate_processor_rate_2cores(): # add score processor benchmark cpu.events_one.add(BenchmarkProcessor(rate=27136.44)) - processor_rate = ProcessorRate().compute(cpu, RateComputer()) + processor_rate = ProcessorRate().compute(cpu) - assert math.isclose(processor_rate, 3.95, rel_tol=0.001), 'ProcessorRate returns incorrect value(rate)' + assert math.isclose(processor_rate, 3.95, rel_tol=0.001) cpu = Processor(cores=2, speed=3.3) cpu.events_one.add(BenchmarkProcessor(rate=26339.48)) - processor_rate = ProcessorRate().compute(cpu, RateComputer()) + processor_rate = ProcessorRate().compute(cpu) # todo rel_tol >= 0.002 - assert math.isclose(processor_rate, 3.93, rel_tol=0.002), 'ProcessorRate returns incorrect value(rate)' + assert math.isclose(processor_rate, 3.93, rel_tol=0.002) def test_rate_processor_with_null_cores(): @@ -220,10 +220,10 @@ def test_rate_processor_with_null_cores(): # todo try without BenchmarkProcessor, StopIteration problem cpu.events_one.add(BenchmarkProcessor()) - processor_rate = ProcessorRate().compute(cpu, RateComputer()) + processor_rate = ProcessorRate().compute(cpu) # todo rel_tol >= 0.003 - assert math.isclose(processor_rate, 1.38, rel_tol=0.003), 'ProcessorRate returns incorrect value(rate)' + assert math.isclose(processor_rate, 1.38, rel_tol=0.003) def test_rate_processor_with_null_speed(): @@ -233,9 +233,9 @@ def test_rate_processor_with_null_speed(): cpu = Processor(cores=1, speed=None) cpu.events_one.add(BenchmarkProcessor(rate=0)) - processor_rate = ProcessorRate().compute(cpu, RateComputer()) + processor_rate = ProcessorRate().compute(cpu) - assert math.isclose(processor_rate, 1.06, rel_tol=0.001), 'ProcessorRate returns incorrect value(rate)' + assert math.isclose(processor_rate, 1.06, rel_tol=0.001) def test_rate_computer_rate(): @@ -324,11 +324,9 @@ def test_rate_computer_rate(): cpu } # Add test visual with functionality and appearance range - visual_test = TestVisual() - visual_test.appearance_range = AppearanceRange.A - visual_test.functionality_range = FunctionalityRange.A - - pc_test.events_one.add(visual_test) + VisualTest(appearance_range=AppearanceRange.A, + functionality_range=FunctionalityRange.A, + device=pc_test) # Compute all components rates and general rating rate_pc = RateAlgorithm().compute(pc_test) @@ -353,10 +351,9 @@ def test_rate_computer_rate(): cpu } # Add test visual with functionality and appearance range - visual_test = TestVisual() - visual_test.appearance_range = AppearanceRange.B - visual_test.functionality_range = FunctionalityRange.A - pc_test.events_one.add(visual_test) + VisualTest(appearance_range=AppearanceRange.B, + functionality_range=FunctionalityRange.A, + device=pc_test) # Compute all components rates and general rating rate_pc = RateAlgorithm().compute(pc_test) @@ -384,12 +381,9 @@ def test_rate_computer_rate(): cpu } # Add test visual with functionality and appearance range - visual_test = TestVisual() - visual_test.appearance_range = AppearanceRange.C - visual_test.functionality_range = FunctionalityRange.A - - pc_test.events_one.add(visual_test) - + VisualTest(appearance_range=AppearanceRange.C, + functionality_range=FunctionalityRange.A, + device=pc_test) # Compute all components rates and general rating rate_pc = RateAlgorithm().compute(pc_test) @@ -399,7 +393,7 @@ def test_rate_computer_rate(): assert math.isclose(rate_pc.processor, 1, rel_tol=0.001) - assert math.isclose(rate_pc.rating, 1.58, rel_tol=0.001) + assert rate_pc.rating == 1.57 # Create a new Computer with components characteristics of pc with id = 798 pc_test = Desktop(chassis=ComputerChassis.Tower) @@ -413,11 +407,9 @@ def test_rate_computer_rate(): cpu } # Add test visual with functionality and appearance range - visual_test = TestVisual() - visual_test.appearance_range = AppearanceRange.B - visual_test.functionality_range = FunctionalityRange.A - - pc_test.events_one.add(visual_test) + VisualTest(appearance_range=AppearanceRange.B, + functionality_range=FunctionalityRange.A, + device=pc_test) # Compute all components rates and general rating rate_pc = RateAlgorithm().compute(pc_test) diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index 6d86574b..e984af63 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -17,8 +17,8 @@ from ereuse_devicehub.resources.device.models import SolidStateDrive from ereuse_devicehub.resources.device.sync import MismatchBetweenProperties, \ MismatchBetweenTagsAndHid from ereuse_devicehub.resources.enums import ComputerChassis, SnapshotSoftware -from ereuse_devicehub.resources.event.models import BenchmarkProcessor, \ - EraseSectors, Event, Snapshot, SnapshotRequest, RateComputer, TestVisual, BenchmarkDataStorage +from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \ + EraseSectors, Event, RateComputer, Snapshot, SnapshotRequest, VisualTest from ereuse_devicehub.resources.tag import Tag from ereuse_devicehub.resources.user.models import User from tests.conftest import file @@ -70,7 +70,7 @@ def test_snapshot_post(user: UserClient): snapshot = snapshot_and_check(user, file('basic.snapshot'), event_types=( BenchmarkProcessor.t, - TestVisual.t, + VisualTest.t, RateComputer.t ), perform_second_snapshot=False) @@ -116,15 +116,17 @@ def test_snapshot_component_add_remove(user: UserClient): # (represented with their S/N) should be: # PC 1: p1c1s, p1c2s, p1c3s. PC 2: ø s1 = file('1-device-with-components.snapshot') - # TODO if dont put len([event_types]) = 1 != len(event_types) = 18 is BenchmarkProcessor (str 18 chars); why?? - snapshot1 = snapshot_and_check(user, s1, event_types=('BenchmarkProcessor',), perform_second_snapshot=False) + snapshot1 = snapshot_and_check(user, + s1, + event_types=(BenchmarkProcessor.t,), + perform_second_snapshot=False) pc1_id = snapshot1['device']['id'] pc1, _ = user.get(res=m.Device, item=pc1_id) # Parent contains components assert tuple(c['serialNumber'] for c in pc1['components']) == ('p1c1s', 'p1c2s', 'p1c3s') # Components contain parent assert all(c['parent'] == pc1_id for c in pc1['components']) - # pc has Snapshot as event + # pc has two events: Snapshot and the BenchmarkProcessor # TODO change assert to len(pc1['events']) == 2 cause we add BenchmarkProcessor event assert len(pc1['events']) == 2 # TODO pc1['events'][0]['type'] == BenchmarkProcessor.t @@ -154,7 +156,8 @@ def test_snapshot_component_add_remove(user: UserClient): assert tuple(e['type'] for e in pc2['events']) == ('Snapshot',) # p1c2s has two Snapshots, a Remove and an Add p1c2s, _ = user.get(res=m.Device, item=pc2['components'][0]['id']) - assert tuple(e['type'] for e in p1c2s['events']) == ('BenchmarkProcessor', 'Snapshot', 'Snapshot', 'Remove') + assert tuple(e['type'] for e in p1c2s['events']) == ( + 'BenchmarkProcessor', 'Snapshot', 'Snapshot', 'Remove') # We register the first device again, but removing motherboard # and moving processor from the second device to the first. @@ -243,11 +246,7 @@ def test_snapshot_tag_inner_tag(tag_id: str, user: UserClient, app: Devicehub): b['device']['tags'] = [{'type': 'Tag', 'id': tag_id}] snapshot_and_check(user, b, - event_types=( - RateComputer.t, - BenchmarkProcessor.t, - TestVisual.t - ), perform_second_snapshot=False) + event_types=(RateComputer.t, BenchmarkProcessor.t, VisualTest.t)) with app.app_context(): tag = Tag.query.one() # type: Tag assert tag.device_id == 1, 'Tag should be linked to the first device' @@ -315,15 +314,17 @@ def test_erase_privacy_standards(user: UserClient): EraseSectors.t, BenchmarkDataStorage.t, BenchmarkProcessor.t - ), perform_second_snapshot=True) + )) erase = next(e for e in snapshot['events'] if e['type'] == EraseSectors.t) assert '2018-06-01T07:12:06+00:00' == erase['endTime'] storage = next(e for e in snapshot['components'] if e['type'] == SolidStateDrive.t) storage, _ = user.get(res=m.Device, item=storage['id']) # Let's get storage events too # order: creation time ascending - erasure1, benchmark_data_storage1, _snapshot1, erasure2, benchmark_data_storage2, _snapshot2 = storage['events'] + erasure1, benchmark_data_storage1, _snapshot1, erasure2, benchmark_data_storage2, _snapshot2 = \ + storage['events'] assert erasure1['type'] == erasure2['type'] == 'EraseSectors' - assert benchmark_data_storage1['type'] == benchmark_data_storage2['type'] == 'BenchmarkDataStorage' + assert benchmark_data_storage1['type'] == benchmark_data_storage2[ + 'type'] == 'BenchmarkDataStorage' assert _snapshot1['type'] == _snapshot2['type'] == 'Snapshot' get_snapshot, _ = user.get(res=Event, item=_snapshot2['id']) assert get_snapshot['events'][0]['endTime'] == '2018-06-01T07:12:06+00:00' @@ -381,7 +382,7 @@ def test_snapshot_computer_monitor(user: UserClient): @pytest.mark.xfail(reason='Not implemented yet, new rate is need it') def test_snapshot_mobile_smartphone_imei_manual_rate(user: UserClient): s = file('smartphone.snapshot') - snapshot = snapshot_and_check(user, s, event_types=('TestVisual',)) + snapshot = snapshot_and_check(user, s, event_types=('VisualTest',)) mobile, _ = user.get(res=m.Device, item=snapshot['device']['id']) assert mobile['imei'] == 3568680000414120 # todo check that manual rate has been created @@ -429,7 +430,7 @@ def assert_similar_components(components1: List[dict], components2: List[dict]): def snapshot_and_check(user: UserClient, input_snapshot: dict, - event_types: Tuple[str] = tuple(), + event_types: Tuple[str, ...] = tuple(), perform_second_snapshot=True) -> dict: """ Performs a Snapshot and then checks if the result is ok: diff --git a/tests/test_workbench.py b/tests/test_workbench.py index 838121a2..90a1ddba 100644 --- a/tests/test_workbench.py +++ b/tests/test_workbench.py @@ -36,7 +36,7 @@ def test_workbench_server_condensed(user: UserClient): snapshot, _ = user.post(res=em.Snapshot, data=s) events = snapshot['events'] assert {(event['type'], event['device']) for event in events} == { - ('RateComputer', 1), # Only (RateComputer, 1), delete (Rate, 1) + ('RateComputer', 1), ('BenchmarkProcessorSysbench', 5), ('StressTest', 1), ('EraseSectors', 6), @@ -47,7 +47,7 @@ def test_workbench_server_condensed(user: UserClient): ('BenchmarkDataStorage', 6), ('BenchmarkDataStorage', 7), ('TestDataStorage', 6), - ('TestVisual', 1) + ('VisualTest', 1) } assert snapshot['closed'] assert snapshot['severity'] == 'Info' @@ -62,11 +62,9 @@ def test_workbench_server_condensed(user: UserClient): assert device['rate']['closed'] assert device['rate']['severity'] == 'Info' assert device['rate']['rating'] == 0 - assert device['rate']['type'] == 'RateComputer' # New in rate v2 - # new asserts get in TestVisual event info; why change in every execution device['events'][X] - # assert device['events'][0]['appearanceRange'] == 'A' - # assert device['events'][0]['functionalityRange'] == 'B' - # TODO add appearance and functionality Range in device[rate] + assert device['rate']['type'] == 'RateComputer' + assert device['events'][0]['appearanceRange'] == 'A' + assert device['events'][0]['functionalityRange'] == 'B' assert device['tags'][0]['id'] == 'tag1' @@ -154,8 +152,8 @@ def test_real_hp_11(user: UserClient): 'TestDataStorage', 'BenchmarkRamSysbench', 'StressTest', - 'TestBios', # New in rate v2 - 'TestVisual' # New in rate v2 + 'TestBios', + 'VisualTest' } assert len(list(e['type'] for e in snapshot['events'])) == 10 assert pc['networkSpeeds'] == [1000, None], 'Device has no WiFi' @@ -188,7 +186,6 @@ def test_snapshot_real_eee_1001pxd(user: UserClient): assert pc['networkSpeeds'] == [100, 0], 'Although it has WiFi we do not know the speed' assert pc['rate'] rate = pc['rate'] - # new asserts get in TestVisual event info; why change pc['events'][X] # assert pc['events'][0]['appearanceRange'] == 'A' # assert pc['events'][0]['functionalityRange'] == 'B' # TODO add appearance and functionality Range in device[rate]