refactor score to new rate version structure

This commit is contained in:
nad 2019-04-23 21:27:31 +02:00
parent 75e1817e9c
commit 880b5e706e
12 changed files with 165 additions and 134 deletions

View File

@ -39,6 +39,7 @@ class DevicehubConfig(Config):
}
API_DOC_CLASS_DISCRIMINATOR = 'type'
# TODO is necessary??
WORKBENCH_RATE_VERSION = StrictVersion('1.0')
PHOTOBOX_RATE_VERSION = StrictVersion('1.0')
"""

View File

@ -175,10 +175,10 @@ class Device(Thing):
@property
def rate(self):
"""The last AggregateRate of the device."""
"""The last Rate of the device."""
with suppress(LookupError, ValueError):
from ereuse_devicehub.resources.event.models import AggregateRate
return self.last_event_of(AggregateRate)
from ereuse_devicehub.resources.event.models import Rate
return self.last_event_of(Rate)
@property
def price(self):
@ -756,32 +756,3 @@ class Manufacturer(db.Model):
'COPY common.manufacturer FROM STDIN (FORMAT csv)',
f
)
class Battery(Component):
"""
Battery component, mobile device
"""
size = Column(Float(decimal_return_scale=2))
size.comment = 'Battery size; Units: 2000 mAh'
voltage = Column()
voltage.comment = 'Battery voltage; Units: 3,5 V'
technology = Column()
technology.comment = 'Battery technology used; Ex: Li-Ti'
charge_counter = Column()
charge_counter.comment = 'Number of time that battery has been charged'
wireless = Column(Boolean, nullable=False, default=False)
wireless.comment = 'If battery can charging wireless'
health = Column(DBEnum)
health.comment = 'Battery health indicator'
status = Column(DBEnum)
status.comment = 'Battery status indicator'
class Camera(Component):
"""
Component camera in mobile devices
"""
resolution = Column(Float(decimal_return_scale=2))
resolution.comment = 'Camera resolution; Units: Megapixels'

View File

@ -73,7 +73,7 @@ class Device(Thing):
pass
@property
def rate(self) -> Optional[e.AggregateRate]:
def rate(self) -> Optional[e.Rate]:
pass
@property

View File

@ -37,7 +37,7 @@ class Device(Thing):
many=True,
dump_only=True,
description='The lots where this device is directly under.')
rate = NestedOn('AggregateRate', dump_only=True, description=m.Device.rate.__doc__)
rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__)
price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__)
trading = EnumField(states.Trading, dump_only=True, description=m.Device.trading.__doc__)
physical = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__)

View File

@ -70,7 +70,7 @@ class AggregateRatingVersions(Enum):
v1 = StrictVersion('1.0')
"""
This version is set to aggregate :class:`ereuse_devicehub.resources.
event.models.WorkbenchRate` version X and :class:`ereuse_devicehub.
event.models.RateComputer` version X and :class:`ereuse_devicehub.
resources.event.models.PhotoboxRate` version Y.
"""
@ -89,9 +89,6 @@ class AppearanceRange(Enum):
E = 'E. Is unacceptable; severity visual damage, missing essential parts'
NONE = 'NA. Grade doesnt exists'
def __str__(self):
return self.name
APPEARANCE_RANGE = 0.5, -0.3
@ -105,8 +102,6 @@ class FunctionalityRange(Enum):
D = 'D. Chassis severity usage problems. All buttons, screen or camera don\'t work; broken or unusable it'
NONE = 'NA. Grade doesnt exists'
def __str__(self):
return self.name
FUNCTIONALITY_RANGE = -0.3, 0.4
@ -122,8 +117,6 @@ class BatteryHealthRange(Enum):
E = 'E. Battery health is very bad; and status is dead; unusable or miss it '
NONE = 'NA. Grade doesnt exists'
def __str__(self):
return self.name
@unique
@ -135,9 +128,6 @@ 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):

View File

@ -84,7 +84,59 @@ class BenchmarkRamSysbenchDef(BenchmarkWithRateDef):
SCHEMA = schemas.BenchmarkRamSysbench
# TODO add test defs
class TestDef(EventDef):
VIEW = None
SCHEMA = schemas.Test
class TestDataStorageDef(TestDef):
VIEW = None
SCHEMA = schemas.TestDataStorage
class StressTestDef(TestDef):
VIEW = None
SCHEMA = schemas.StressTest
class TestAudioDef(TestDef):
VIEW = None
SCHEMA = schemas.TestAudio
class TestConnectivityDef(TestDef):
VIEW = None
SCHEMA = schemas.TestConnectivity
class TestBatteryDef(TestDef):
VIEW = None
SCHEMA = schemas.TestBattery
class TestCameraDef(TestDef):
VIEW = None
SCHEMA = schemas.TestCamera
class TestKeyboardDef(TestDef):
VIEW = None
SCHEMA = schemas.TestKeyboard
class TestTrackpadDef(TestDef):
VIEW = None
SCHEMA = schemas.TestTrackpad
class TestBiosDef(TestDef):
VIEW = None
SCHEMA = schemas.TestBios
class TestVisualDef(TestDef):
VIEW = None
SCHEMA = schemas.TestVisual
class RateDef(EventDef):
@ -126,21 +178,6 @@ class SnapshotDef(EventDef):
self.sync = Sync()
class TestDef(EventDef):
VIEW = None
SCHEMA = schemas.Test
class TestDataStorageDef(TestDef):
VIEW = None
SCHEMA = schemas.TestDataStorage
class StressTestDef(TestDef):
VIEW = None
SCHEMA = schemas.StressTest
class ToRepairDef(EventDef):
VIEW = None
SCHEMA = schemas.ToRepair

View File

@ -1,3 +1,15 @@
"""
This file contains all events can apply to a device and is sorted according to a structure based on:
* Generic Events
* Benchmarks
* Tests
* Rates
* Prices
Within the above general classes are subclasses in A order.
"""
from collections import Iterable
from datetime import datetime, timedelta
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_UP
@ -571,6 +583,13 @@ class Benchmark(JoinedWithOneDeviceMixin, EventWithOneDevice):
return args
class BenchmarkMixin:
# noinspection PyMethodParameters
@declared_attr
def id(cls):
return Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
class BenchmarkDataStorage(Benchmark):
"""Benchmarks the data storage unit reading and writing speeds."""
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
@ -607,6 +626,9 @@ class BenchmarkProcessorSysbench(BenchmarkProcessor):
class BenchmarkRamSysbench(BenchmarkWithRate):
"""Benchmarks a RAM by using the ram benchmarking
utility of `sysbench <https://github.com/akopytov/sysbench>`_.
"""
pass
@ -621,7 +643,6 @@ class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
Testing errors and warnings are easily taken in
:attr:`ereuse_devicehub.resources.device.models.Device.working`.
"""
elapsed = Column(Interval, nullable=False)
@declared_attr
def __mapper_args__(cls):
@ -638,7 +659,14 @@ class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
return args
class TestDataStorage(Test):
class TestMixin:
# noinspection PyMethodParameters
@declared_attr
def id(cls):
return Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
class TestDataStorage(TestMixin, Test):
"""
The act of testing the data storage.
@ -650,7 +678,6 @@ class TestDataStorage(Test):
The test takes to other SMART values indicators of the overall health
of the data storage.
"""
id = Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
length = Column(DBEnum(TestDataStorageLength), nullable=False) # todo from type
status = Column(Unicode(), check_lower('status'), nullable=False)
lifetime = Column(Interval)
@ -662,6 +689,7 @@ class TestDataStorage(Test):
current_pending_sector_count = Column(SmallInteger)
offline_uncorrectable = Column(SmallInteger)
remaining_lifetime_percentage = Column(SmallInteger)
elapsed = Column(Interval, nullable=False)
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
@ -686,11 +714,12 @@ class TestDataStorage(Test):
return t
class StressTest(Test):
class StressTest(TestMixin, Test):
"""The act of stressing (putting to the maximum capacity)
a device for an amount of minutes. If the device is not in great
condition won't probably survive such test.
"""
elapsed = Column(Interval, nullable=False)
@validates('elapsed')
def is_minute_and_bigger_than_1_minute(self, _, value: timedelta):
@ -703,7 +732,7 @@ class StressTest(Test):
return '{}. Computing for {}'.format(self.severity, self.elapsed)
class TestAudio(Test):
class TestAudio(TestMixin, Test):
"""
Test to check all this aspects related with audio functions, Manual Tests??
"""
@ -713,7 +742,7 @@ class TestAudio(Test):
microphone.comment = 'This evaluate if microphone works correctly'
class TestConnectivity(Test):
class TestConnectivity(TestMixin, Test):
"""
Test to check all this aspects related with functionality connections in devices
"""
@ -724,18 +753,18 @@ class TestConnectivity(Test):
wifi.comment = 'Evaluate if wifi connection works correctly'
bluetooth = Column(Boolean)
bluetooth.comment = 'Evaluate if bluetooth works'
usb_port = Column(Boolean())
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'
class TestBattery(Test):
class TestBattery(TestMixin, Test):
"""
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 = Column(Boolean)
battery_stat.comment = """
Some batteries can report a self-check life status.
"""
@ -743,7 +772,7 @@ class TestBattery(Test):
battery_health.comment = BatteryHealthRange.__doc__
class TestCamera(Test):
class TestCamera(TestMixin, Test):
"""
Test to determinate functionality and defects on camera when take pictures or record video
# TODO define when test FAIL
@ -752,39 +781,38 @@ class TestCamera(Test):
camera.comment = ""
class TestKeyboard(Test):
class TestKeyboard(TestMixin, Test):
"""
Test to determinate if keyboard layout are and works correctly
# TODO define when test FAIL
"""
keyboard = Column(Boolean)
keyboard.comment = ""
class TestTrackpad(Test):
"""Test trackpad works correctly"""
class TestTrackpad(TestMixin, Test):
"""
Test trackpad works correctly
# TODO define when test FAIL
"""
trackpad = Column(Boolean)
trackpad.comment = ""
class TestBios(Test):
class TestBios(TestMixin, Test):
"""
Test that determinate motherboard no beeps, codes or errors when power on
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..
"""
bios_power_on = Column(Boolean())
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.
"""
class TestBiosDifficulty:
"""
Test to determinate a grade to reflect some possibles difficult to access or modify setting in the BIOS, like password protection..
"""
bios_access_range = Column(DBEnum(BiosAccessRange))
bios_access_range.comment = 'Range of difficult to access BIOS'
class TestVisual(Test):
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.
@ -849,8 +877,7 @@ class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""
The act of compute general computer rate
"""
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import rate_algorithm
return rate_algorithm.compute(device)
raise NotImplementedError()
class RateComputer(Rate):
@ -886,6 +913,14 @@ class RateComputer(Rate):
if self.processor:
return RatingRange.from_score(self.processor)
@classmethod
def compute(cls, device) -> 'RateComputer':
"""
The act of compute general computer rate
"""
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import rate_algorithm
return rate_algorithm.compute(device)
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of setting a trading price for the device.
@ -913,7 +948,7 @@ class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
version = Column(StrictVersionType)
version.comment = """The version of the software, or None."""
rating_id = Column(UUID(as_uuid=True), ForeignKey(Rate.id))
rating_id.comment = """The AggregateRate used to auto-compute
rating_id.comment = """The Rate used to auto-compute
this price, if it has not been set manually."""
rating = relationship(Rate,
backref=backref('price',

View File

@ -224,9 +224,10 @@ class BenchmarkGraphicCard(BenchmarkWithRate):
class Test(EventWithOneDevice):
elapsed = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.elapsed = ... # type: timedelta
self.elapsed = ... # type: Optional[timedelta]
self.success = ... # type: bool
@ -288,9 +289,6 @@ class TestTrackpad(Test):
class TestBios(Test):
bios_power_on = ... # type: Column
class TestBiosDifficulty:
bios_access_range = ... # type: BiosAccessRange
@ -321,6 +319,10 @@ class RateComputer(Rate):
ram = ...
data_storage = ...
@classmethod
def compute(cls, device) -> 'RateComputer':
pass
class Price(EventWithOneDevice):
SCALE = ...
@ -338,7 +340,7 @@ class Price(EventWithOneDevice):
self.currency = ... # type: Currency
self.software = ... # type: PriceSoftware
self.version = ... # type: StrictVersion
self.rating = ... # type: AggregateRate
self.rating = ... # type: Rate
@classmethod
def to_price(cls, value: Union[Decimal, float], rounding=ROUND) -> Decimal:
@ -360,7 +362,7 @@ class EreusePrice(Price):
self.standard = ... # type: EreusePrice.Type
self.warranty2 = ... # type: EreusePrice.Type
def __init__(self, rating: AggregateRate, **kwargs) -> None:
def __init__(self, rating: Rate, **kwargs) -> None:
super().__init__(**kwargs)
self.retailer = ... # type: EreusePrice.Service
self.platform = ... # type: EreusePrice.Service

View File

@ -1,25 +0,0 @@
from ereuse_devicehub.resources.device.models import Device
from ereuse_devicehub.resources.event.models import RateComputer
from ereuse_devicehub.resources.event.rate.workbench import v1_0
RATE_TYPES = {
RateComputer: {
'1.0': v1_0.Rate()
}
}
def rate(device: Device, version):
"""
Rates the passed-in ``rate`` using values from the rate itself
and the ``device``.
This method mutates ``rate``.
:param device: The device to use as a model.
:param rate: A half-filled rate.
"""
assert cls in RATE_TYPES, 'Rate type {} not supported.'.format(cls)
assert str(rate.version) in RATE_TYPES[cls], \
'Rate version {} not supported.'.format(rate.version)
RATE_TYPES[cls][str(rate.version)].compute(device)

View File

@ -53,7 +53,7 @@ class RateAlgorithm(BaseRate):
This mutates "rate".
"""
assert isinstance(device, (Desktop, Laptop, Server))
assert isinstance(device, Computer)
rate = RateComputer()
@ -70,7 +70,7 @@ class RateAlgorithm(BaseRate):
result = rate_cls.compute(components, rate)
if result:
setattr(rate, field, result)
# TODO is necessary check if TestVisual exists?? cause StopIteration Error
test_visual = next(e for e in device.events if isinstance(e, TestVisual))
rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram)
@ -212,7 +212,7 @@ class DataStorageRate(BaseRate):
# STEP: Filtering, data cleaning and merging of component parts
for storage in data_storage_devices:
# todo fix StopIteration if don't exists BenchmarkDataStorage
# We assume all hdd snapshots have BenchmarkDataStorage
benchmark = next(e for e in storage.events if isinstance(e, BenchmarkDataStorage))
# prevent NULL values
_size = storage.size or 0

View File

@ -14,7 +14,7 @@ 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
Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength, BatteryHealthRange
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
@ -143,11 +143,11 @@ class BenchmarkGraphicCard(BenchmarkWithRate):
class Test(EventWithOneDevice):
__doc__ = m.Test.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
class TestDataStorage(Test):
__doc__ = m.TestDataStorage.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
length = EnumField(TestDataStorageLength, required=True)
status = SanitizedStr(lower=True, validate=Length(max=STR_SIZE), required=True)
lifetime = TimeDelta(precision=TimeDelta.HOURS)
@ -163,6 +163,7 @@ class TestDataStorage(Test):
class StressTest(Test):
__doc__ = m.StressTest.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
class TestAudio(Test):
@ -173,22 +174,41 @@ class TestAudio(Test):
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 TestBios:
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):
__doc__ = m.TestBios.__doc__
bios_power_on = Boolean()
bios_access_range = EnumField(BiosAccessRange, data_key='accessRange')
class TestBiosDifficulty:
__doc__ = m.TestBiosDifficulty.__doc__
bios_access_range = EnumField(BiosAccessRange, dump_only=True, data_key='biosAccessRange')
class TestVisual():
class TestVisual(Test):
__doc__ = m.TestVisual.__doc__
appearance_range = EnumField(AppearanceRange, dump_only=True, data_key='appearanceRange')
functionality_range = EnumField(FunctionalityRange, dump_only=True, data_key='functionalityRange')

View File

@ -10,7 +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
from ereuse_devicehub.resources.event.models import Event, Snapshot, Rate, RateComputer
SUPPORTED_WORKBENCH = StrictVersion('11.0')
@ -81,9 +81,9 @@ class EventView(View):
snapshot.events |= events
# Compute ratings
for rate in (e for e in events_device if isinstance(e, Rate)):
rates = rate.ratings()
snapshot.events |= rates
if isinstance(device, Computer):
rate_computer = RateComputer.compute(device)
snapshot.events.add(rate_computer)
db.session.add(snapshot)
db.session().final_flush()