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
|
||||
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."""
|
||||
|
|
|
@ -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]:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
Reference in a new issue