Fix imprecisions in rate algorithms and small bugfixes

This commit is contained in:
Xavier Bustamante Talavera 2019-05-10 11:58:38 +02:00
parent 16d00256df
commit 58be2162f7
9 changed files with 60 additions and 85 deletions

View File

@ -841,9 +841,9 @@ class VisualTest(TestMixin, Test):
"""The act of visually inspecting the appearance and functionality """The act of visually inspecting the appearance and functionality
of the device. of the device.
""" """
appearance_range = Column(DBEnum(AppearanceRange)) appearance_range = Column(DBEnum(AppearanceRange), nullable=False)
appearance_range.comment = AppearanceRange.__doc__ appearance_range.comment = AppearanceRange.__doc__
functionality_range = Column(DBEnum(FunctionalityRange)) functionality_range = Column(DBEnum(FunctionalityRange), nullable=False)
functionality_range.comment = FunctionalityRange.__doc__ functionality_range.comment = FunctionalityRange.__doc__
labelling = Column(Boolean) labelling = Column(Boolean)
labelling.comment = """Whether there are tags to be removed.""" labelling.comment = """Whether there are tags to be removed."""

View File

@ -303,10 +303,16 @@ class TestBios(Test):
class VisualTest(Test): class VisualTest(Test):
appearance_range = ... # type: AppearanceRange appearance_range = ... # type: Column
functionality_range = ... # type: FunctionalityRange functionality_range = ... # type: Column
labelling = ... # 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): class Rate(EventWithOneDevice):
N = 2 N = 2
@ -335,10 +341,10 @@ class RateComputer(Rate):
def __init__(self, **kwargs) -> None: def __init__(self, **kwargs) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)
self.processor = ... # type: float self.processor = ... # type: Optional[float]
self.ram = ... # type: float self.ram = ... # type: Optional[float]
self.data_storage = ... # type: float self.data_storage = ... # type: Optional[float]
self.graphic_card = ... # type: float self.graphic_card = ... # type: Optional[float]
@classmethod @classmethod
def compute(cls, device: Device) -> Tuple[RateComputer, EreusePrice]: def compute(cls, device: Device) -> Tuple[RateComputer, EreusePrice]:

View File

@ -1,4 +1,4 @@
from enum import Enum from enum import Enum, unique
from itertools import groupby from itertools import groupby
from typing import Dict, Iterable, Tuple from typing import Dict, Iterable, Tuple
@ -17,26 +17,21 @@ class RateAlgorithm(BaseRate):
which then calls this. which then calls this.
""" """
class Range(Enum): @unique
@classmethod class Appearance(Enum):
def from_devicehub(cls, r: Enum):
return getattr(cls, r.name) if r else cls.NONE
class Appearance(Range):
Z = 0.5 Z = 0.5
A = 0.3 A = 0.3
B = 0 B = 0
C = -0.2 C = -0.2
D = -0.5 D = -0.5
E = -1.0 E = -1.0
NONE = -0.3
class Functionality(Range): @unique
class Functionality(Enum):
A = 0.4 A = 0.4
B = -0.5 B = -0.5
C = -0.75 C = -0.75
D = -1 D = -1
NONE = -0.3
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
@ -55,7 +50,7 @@ class RateAlgorithm(BaseRate):
assert isinstance(device, Computer), 'Can only rate computers' assert isinstance(device, Computer), 'Can only rate computers'
try: try:
test_visual = device.last_event_of(VisualTest) visual_test = device.last_event_of(VisualTest)
except LookupError: except LookupError:
raise CannotRate('You need a visual test.') raise CannotRate('You need a visual test.')
@ -75,10 +70,8 @@ class RateAlgorithm(BaseRate):
setattr(rate, field, result) setattr(rate, field, result)
rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram) 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.appearance = self.Appearance[visual_test.appearance_range.name].value
rate.functionality = self.Functionality.from_devicehub( rate.functionality = self.Functionality[visual_test.functionality_range.name].value
test_visual.functionality_range).value
rate.rating = rate_components + rate.functionality + rate.appearance rate.rating = rate_components + rate.functionality + rate.appearance
device.events_one.add(rate) device.events_one.add(rate)
assert 0 <= rate.rating <= 4.7, 'Rate ranges from 0 to 4.7' assert 0 <= rate.rating <= 4.7, 'Rate ranges from 0 to 4.7'
@ -94,6 +87,7 @@ class ProcessorRate(BaseRate):
DEFAULT_CORES = 1 DEFAULT_CORES = 1
DEFAULT_SPEED = 1.6 DEFAULT_SPEED = 1.6
# In case of i2, i3,.. result penalized. # 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. # Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz, score = 23406.92 but results inan score of 17503.
DEFAULT_SCORE = 4000 DEFAULT_SCORE = 4000
@ -103,15 +97,12 @@ class ProcessorRate(BaseRate):
Obs: cores and speed are possible NULL value Obs: cores and speed are possible NULL value
:return: result is a rate (score) of Processor characteristics :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 cores = processor.cores or self.DEFAULT_CORES
speed = processor.speed or self.DEFAULT_SPEED speed = processor.speed or self.DEFAULT_SPEED
# todo jn? fix StopIteration if don't exists BenchmarkProcessor
benchmark_cpu = next( benchmark_cpu = next(
e for e in reversed(processor.events) e for e in reversed(processor.events)
if isinstance(e, BenchmarkProcessor) and not isinstance(e, BenchmarkProcessorSysbench) 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 benchmark_cpu = benchmark_cpu.rate or self.DEFAULT_SCORE
# STEP: Fusion components # STEP: Fusion components

View File

@ -204,8 +204,10 @@ class TestBios(Test):
class VisualTest(Test): class VisualTest(Test):
__doc__ = m.VisualTest.__doc__ __doc__ = m.VisualTest.__doc__
appearance_range = EnumField(AppearanceRange, data_key='appearanceRange') appearance_range = EnumField(AppearanceRange, required=True, data_key='appearanceRange')
functionality_range = EnumField(FunctionalityRange, data_key='functionalityRange') functionality_range = EnumField(FunctionalityRange,
required=True,
data_key='functionalityRange')
labelling = Boolean() labelling = Boolean()

View File

@ -68,6 +68,7 @@ class EventView(View):
assert not device.events_one assert not device.events_one
assert all(not c.events_one for c in components) if components else True assert all(not c.events_one for c in components) if components else True
db_device, remove_events = resource_def.sync.run(device, components) db_device, remove_events = resource_def.sync.run(device, components)
del device # Do not use device anymore
snapshot.device = db_device snapshot.device = db_device
snapshot.events |= remove_events | events_device # Set events to snapshot snapshot.events |= remove_events | events_device # Set events to snapshot
# commit will change the order of the components by what # commit will change the order of the components by what
@ -84,7 +85,7 @@ class EventView(View):
# Compute ratings # Compute ratings
if snapshot.software == SnapshotSoftware.Workbench: if snapshot.software == SnapshotSoftware.Workbench:
try: try:
rate_computer, price = RateComputer.compute(device) rate_computer, price = RateComputer.compute(db_device)
except CannotRate: except CannotRate:
pass pass
else: else:

View File

@ -351,34 +351,6 @@ def test_erase_physical():
db.session.commit() 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') @pytest.mark.xfail(reson='develop')
def test_measure_battery(): def test_measure_battery():
"""Tests the MeasureBattery.""" """Tests the MeasureBattery."""

View File

@ -5,12 +5,12 @@ import pytest
from ereuse_devicehub.client import UserClient from ereuse_devicehub.client import UserClient
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.resources.device.models import Computer, Desktop, HardDrive, Processor, \ from ereuse_devicehub.resources.device.models import Computer, Desktop, Device, HardDrive, \
RamModule Processor, RamModule
from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, \ from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, \
FunctionalityRange FunctionalityRange
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \ 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 ereuse_devicehub.resources.event.rate.workbench.v1_0 import CannotRate
from tests import conftest from tests import conftest
from tests.conftest import file from tests.conftest import file
@ -28,19 +28,32 @@ def test_workbench_rate_db():
db.session.commit() db.session.commit()
@pytest.mark.xfail(reason='ComputerRate V1 can only be triggered from Workbench snapshot software') @pytest.mark.xfail(reson='Adapt rate algorithm to re-compute by passing a manual rate.')
def test_rate_workbench_then_manual(): def test_manual_rate_after_workbench_rate(user: UserClient):
"""Checks that a new Rate is generated for a snapshot """Perform a WorkbenchRate and then update the device with a ManualRate.
that is not from Workbench.
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__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_rate(): def test_price_from_rate():
"""Test generating an Rate for a given PC / components / """Tests the price generated from the rate."""
RateComputer ensuring results and relationships between
pc - rate - RateComputer - price.
"""
pc = Desktop(chassis=ComputerChassis.Tower) pc = Desktop(chassis=ComputerChassis.Tower)
hdd = HardDrive(size=476940) 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 Checks if a rate is calculated from a snapshot without visual test
""" """
# Upload a basic snapshot # Upload a basic snapshot
device = file('basic.snapshot') s = file('basic.snapshot')
# Delete snapshot device events # Delete snapshot device events
del device['device']['events'] del s['device']['events']
user.post(device, res=Snapshot) snapshot, _ = user.post(s, res=Snapshot)
device, _ = user.get(res=Device, item=snapshot['device']['id'])
# How to assert CannotRate Exception # How to assert CannotRate Exception
assert CannotRate assert 'rate' not in snapshot['device']
def test_no_rate_if_device_is_not_computer(user: UserClient): def test_no_rate_if_device_is_not_computer(user: UserClient):

View File

@ -96,7 +96,6 @@ def test_rate_ram_rate():
ram_rate = RamRate().compute([ram1]) 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)' 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]) 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)' 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]) 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)' 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) processor_rate = ProcessorRate().compute(cpu)
# todo rel_tol >= 0.002
assert math.isclose(processor_rate, 3.93, 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 Test with processor device have null number of cores
""" """
cpu = Processor(cores=None, speed=3.3) cpu = Processor(cores=None, speed=3.3)
# todo try without BenchmarkProcessor, StopIteration problem
cpu.events_one.add(BenchmarkProcessor()) cpu.events_one.add(BenchmarkProcessor())
processor_rate = ProcessorRate().compute(cpu) processor_rate = ProcessorRate().compute(cpu)
# todo rel_tol >= 0.003
assert math.isclose(processor_rate, 1.38, rel_tol=0.003) assert math.isclose(processor_rate, 1.38, rel_tol=0.003)

View File

@ -168,7 +168,7 @@ def test_real_toshiba_11(user: UserClient):
snapshot, _ = user.post(res=em.Snapshot, data=s) 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, Checks the values of the device, components,
events and their relationships of a real pc. 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) 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 = { SNAPSHOTS_NEED_ID = {
'box-xavier.snapshot.json', 'box-xavier.snapshot.json',
'custom.lshw.snapshot.json', 'custom.lshw.snapshot.json',