Fix imprecisions in rate algorithms and small bugfixes
This commit is contained in:
parent
16d00256df
commit
58be2162f7
|
@ -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."""
|
||||||
|
|
|
@ -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]:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
Reference in a new issue