From 58be2162f72d6773eb9f692f4d5a518dbc05cba4 Mon Sep 17 00:00:00 2001 From: Xavier Bustamante Talavera Date: Fri, 10 May 2019 11:58:38 +0200 Subject: [PATCH] Fix imprecisions in rate algorithms and small bugfixes --- ereuse_devicehub/resources/event/models.py | 4 +- ereuse_devicehub/resources/event/models.pyi | 18 +++++--- .../resources/event/rate/workbench/v1_0.py | 27 ++++------- ereuse_devicehub/resources/event/schemas.py | 6 ++- ereuse_devicehub/resources/event/views.py | 3 +- tests/test_event.py | 28 ----------- tests/test_rate.py | 46 ++++++++++++------- tests/test_rate_workbench_v1.py | 6 --- tests/test_workbench.py | 7 +-- 9 files changed, 60 insertions(+), 85 deletions(-) diff --git a/ereuse_devicehub/resources/event/models.py b/ereuse_devicehub/resources/event/models.py index 71be088b..fae4303a 100644 --- a/ereuse_devicehub/resources/event/models.py +++ b/ereuse_devicehub/resources/event/models.py @@ -841,9 +841,9 @@ class VisualTest(TestMixin, Test): """The act of visually inspecting the appearance and functionality of the device. """ - appearance_range = Column(DBEnum(AppearanceRange)) + appearance_range = Column(DBEnum(AppearanceRange), nullable=False) appearance_range.comment = AppearanceRange.__doc__ - functionality_range = Column(DBEnum(FunctionalityRange)) + functionality_range = Column(DBEnum(FunctionalityRange), nullable=False) functionality_range.comment = FunctionalityRange.__doc__ labelling = Column(Boolean) labelling.comment = """Whether there are tags to be removed.""" diff --git a/ereuse_devicehub/resources/event/models.pyi b/ereuse_devicehub/resources/event/models.pyi index e6ef5165..f3af7551 100644 --- a/ereuse_devicehub/resources/event/models.pyi +++ b/ereuse_devicehub/resources/event/models.pyi @@ -303,10 +303,16 @@ class TestBios(Test): class VisualTest(Test): - appearance_range = ... # type: AppearanceRange - functionality_range = ... # type: FunctionalityRange + appearance_range = ... # type: Column + functionality_range = ... # type: Column labelling = ... # type: Column + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + self.appearance_range = ... # type: AppearanceRange + self.functionality_range = ... # type: FunctionalityRange + self.labelling = ... # type: Optional[bool] + class Rate(EventWithOneDevice): N = 2 @@ -335,10 +341,10 @@ class RateComputer(Rate): 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 + self.processor = ... # type: Optional[float] + self.ram = ... # type: Optional[float] + self.data_storage = ... # type: Optional[float] + self.graphic_card = ... # type: Optional[float] @classmethod def compute(cls, device: Device) -> Tuple[RateComputer, EreusePrice]: diff --git a/ereuse_devicehub/resources/event/rate/workbench/v1_0.py b/ereuse_devicehub/resources/event/rate/workbench/v1_0.py index ebd53e80..5b47a91c 100644 --- a/ereuse_devicehub/resources/event/rate/workbench/v1_0.py +++ b/ereuse_devicehub/resources/event/rate/workbench/v1_0.py @@ -1,4 +1,4 @@ -from enum import Enum +from enum import Enum, unique from itertools import groupby from typing import Dict, Iterable, Tuple @@ -17,26 +17,21 @@ class RateAlgorithm(BaseRate): which then calls this. """ - class Range(Enum): - @classmethod - def from_devicehub(cls, r: Enum): - return getattr(cls, r.name) if r else cls.NONE - - class Appearance(Range): + @unique + class Appearance(Enum): Z = 0.5 A = 0.3 B = 0 C = -0.2 D = -0.5 E = -1.0 - NONE = -0.3 - class Functionality(Range): + @unique + class Functionality(Enum): A = 0.4 B = -0.5 C = -0.75 D = -1 - NONE = -0.3 def __init__(self) -> None: super().__init__() @@ -55,7 +50,7 @@ class RateAlgorithm(BaseRate): assert isinstance(device, Computer), 'Can only rate computers' try: - test_visual = device.last_event_of(VisualTest) + visual_test = device.last_event_of(VisualTest) except LookupError: raise CannotRate('You need a visual test.') @@ -75,10 +70,8 @@ class RateAlgorithm(BaseRate): setattr(rate, field, result) 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.appearance = self.Appearance[visual_test.appearance_range.name].value + rate.functionality = self.Functionality[visual_test.functionality_range.name].value 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' @@ -94,6 +87,7 @@ class ProcessorRate(BaseRate): DEFAULT_CORES = 1 DEFAULT_SPEED = 1.6 + # In case of i2, i3,.. result penalized. # Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz, score = 23406.92 but results inan score of 17503. DEFAULT_SCORE = 4000 @@ -103,15 +97,12 @@ class ProcessorRate(BaseRate): Obs: cores and speed are possible NULL value :return: result is a rate (score) of Processor characteristics """ - # 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 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 diff --git a/ereuse_devicehub/resources/event/schemas.py b/ereuse_devicehub/resources/event/schemas.py index 0f12e7f3..0aaee983 100644 --- a/ereuse_devicehub/resources/event/schemas.py +++ b/ereuse_devicehub/resources/event/schemas.py @@ -204,8 +204,10 @@ class TestBios(Test): class VisualTest(Test): __doc__ = m.VisualTest.__doc__ - appearance_range = EnumField(AppearanceRange, data_key='appearanceRange') - functionality_range = EnumField(FunctionalityRange, data_key='functionalityRange') + appearance_range = EnumField(AppearanceRange, required=True, data_key='appearanceRange') + functionality_range = EnumField(FunctionalityRange, + required=True, + data_key='functionalityRange') labelling = Boolean() diff --git a/ereuse_devicehub/resources/event/views.py b/ereuse_devicehub/resources/event/views.py index 72819176..2f642cb6 100644 --- a/ereuse_devicehub/resources/event/views.py +++ b/ereuse_devicehub/resources/event/views.py @@ -68,6 +68,7 @@ class EventView(View): assert not device.events_one assert all(not c.events_one for c in components) if components else True db_device, remove_events = resource_def.sync.run(device, components) + del device # Do not use device anymore snapshot.device = db_device snapshot.events |= remove_events | events_device # Set events to snapshot # commit will change the order of the components by what @@ -84,7 +85,7 @@ class EventView(View): # Compute ratings if snapshot.software == SnapshotSoftware.Workbench: try: - rate_computer, price = RateComputer.compute(device) + rate_computer, price = RateComputer.compute(db_device) except CannotRate: pass else: diff --git a/tests/test_event.py b/tests/test_event.py index fa2aa7bb..a6d7d5a7 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -351,34 +351,6 @@ def test_erase_physical(): db.session.commit() -@pytest.mark.xfail(reson='Adapt rate algorithm to re-compute by passing a manual rate.') -def test_manual_rate_after_workbench_rate(user: UserClient): - """Perform a WorkbenchRate and then update the device with a ManualRate. - - Devicehub must make the final rate with the first workbench rate - plus the new manual rate, without considering the appearance / - functionality values of the workbench rate. - """ - s = file('real-hp.snapshot.11') - snapshot, _ = user.post(s, res=models.Snapshot) - device, _ = user.get(res=Device, item=snapshot['device']['id']) - assert 'B' == device['rate']['appearanceRange'] - assert device['rate'] == 1 - user.post({ - 'type': 'ManualRate', - 'device': device['id'], - 'appearanceRange': 'A', - 'functionalityRange': 'A' - }, res=models.Event) - device, _ = user.get(res=Device, item=snapshot['device']['id']) - assert 'A' == device['rate']['appearanceRange'] - - -@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.""" diff --git a/tests/test_rate.py b/tests/test_rate.py index 204ebe2c..640eb20c 100644 --- a/tests/test_rate.py +++ b/tests/test_rate.py @@ -5,12 +5,12 @@ import pytest from ereuse_devicehub.client import UserClient from ereuse_devicehub.db import db -from ereuse_devicehub.resources.device.models import Computer, Desktop, HardDrive, Processor, \ - RamModule +from ereuse_devicehub.resources.device.models import Computer, Desktop, Device, HardDrive, \ + Processor, RamModule from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, \ FunctionalityRange from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \ - RateComputer, Snapshot, VisualTest + Event, RateComputer, Snapshot, VisualTest from ereuse_devicehub.resources.event.rate.workbench.v1_0 import CannotRate from tests import conftest from tests.conftest import file @@ -28,19 +28,32 @@ def test_workbench_rate_db(): db.session.commit() -@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 for a snapshot - that is not from Workbench. +@pytest.mark.xfail(reson='Adapt rate algorithm to re-compute by passing a manual rate.') +def test_manual_rate_after_workbench_rate(user: UserClient): + """Perform a WorkbenchRate and then update the device with a ManualRate. + + Devicehub must make the final rate with the first workbench rate + plus the new manual rate, without considering the appearance / + functionality values of the workbench rate. """ + s = file('real-hp.snapshot.11') + snapshot, _ = user.post(s, res=Snapshot) + device, _ = user.get(res=Device, item=snapshot['device']['id']) + assert 'B' == device['rate']['appearanceRange'] + assert device['rate'] == 1 + user.post({ + 'type': 'ManualRate', + 'device': device['id'], + 'appearanceRange': 'A', + 'functionalityRange': 'A' + }, res=Event) + device, _ = user.get(res=Device, item=snapshot['device']['id']) + assert 'A' == device['rate']['appearanceRange'] @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_rate(): - """Test generating an Rate for a given PC / components / - RateComputer ensuring results and relationships between - pc - rate - RateComputer - price. - """ +def test_price_from_rate(): + """Tests the price generated from the rate.""" pc = Desktop(chassis=ComputerChassis.Tower) hdd = HardDrive(size=476940) @@ -97,12 +110,13 @@ def test_no_rate_if_no_visual_test(user: UserClient): Checks if a rate is calculated from a snapshot without visual test """ # Upload a basic snapshot - device = file('basic.snapshot') + s = file('basic.snapshot') # Delete snapshot device events - del device['device']['events'] - user.post(device, res=Snapshot) + del s['device']['events'] + snapshot, _ = user.post(s, res=Snapshot) + device, _ = user.get(res=Device, item=snapshot['device']['id']) # How to assert CannotRate Exception - assert CannotRate + assert 'rate' not in snapshot['device'] def test_no_rate_if_device_is_not_computer(user: UserClient): diff --git a/tests/test_rate_workbench_v1.py b/tests/test_rate_workbench_v1.py index 4040d8bd..07501f47 100644 --- a/tests/test_rate_workbench_v1.py +++ b/tests/test_rate_workbench_v1.py @@ -96,7 +96,6 @@ def test_rate_ram_rate(): 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)' @@ -127,7 +126,6 @@ def test_rate_ram_rate_4modules(): 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)' @@ -158,7 +156,6 @@ def test_rate_ram_speed_is_null(): 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)' @@ -208,7 +205,6 @@ def test_rate_processor_rate_2cores(): processor_rate = ProcessorRate().compute(cpu) - # todo rel_tol >= 0.002 assert math.isclose(processor_rate, 3.93, rel_tol=0.002) @@ -217,12 +213,10 @@ def test_rate_processor_with_null_cores(): Test with processor device have null number of cores """ cpu = Processor(cores=None, speed=3.3) - # todo try without BenchmarkProcessor, StopIteration problem cpu.events_one.add(BenchmarkProcessor()) processor_rate = ProcessorRate().compute(cpu) - # todo rel_tol >= 0.003 assert math.isclose(processor_rate, 1.38, rel_tol=0.003) diff --git a/tests/test_workbench.py b/tests/test_workbench.py index 90a1ddba..6631a135 100644 --- a/tests/test_workbench.py +++ b/tests/test_workbench.py @@ -168,7 +168,7 @@ def test_real_toshiba_11(user: UserClient): snapshot, _ = user.post(res=em.Snapshot, data=s) -def test_snapshot_real_eee_1001pxd(user: UserClient): +def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient): """ Checks the values of the device, components, events and their relationships of a real pc. @@ -297,11 +297,6 @@ def test_real_eee_1000h(user: UserClient): snapshot, _ = user.post(res=em.Snapshot, data=s) -@pytest.mark.xfail(reason='We do not have a snapshot file to use') -def test_real_full_with_workbench_rate(user: UserClient): - pass - - SNAPSHOTS_NEED_ID = { 'box-xavier.snapshot.json', 'custom.lshw.snapshot.json',