diff --git a/ereuse_devicehub/config.py b/ereuse_devicehub/config.py index 79a998f8..251f72ce 100644 --- a/ereuse_devicehub/config.py +++ b/ereuse_devicehub/config.py @@ -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') """ diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 6aa654ba..e320fa74 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -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' diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index 962e4901..3c16e17b 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -73,7 +73,7 @@ class Device(Thing): pass @property - def rate(self) -> Optional[e.AggregateRate]: + def rate(self) -> Optional[e.Rate]: pass @property diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index 9b3eb5b2..7623be43 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -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__) diff --git a/ereuse_devicehub/resources/enums.py b/ereuse_devicehub/resources/enums.py index 0534351e..5cda6956 100644 --- a/ereuse_devicehub/resources/enums.py +++ b/ereuse_devicehub/resources/enums.py @@ -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 doesn’t 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 doesn’t 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 doesn’t 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): diff --git a/ereuse_devicehub/resources/event/__init__.py b/ereuse_devicehub/resources/event/__init__.py index 9612ac83..96843230 100644 --- a/ereuse_devicehub/resources/event/__init__.py +++ b/ereuse_devicehub/resources/event/__init__.py @@ -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 diff --git a/ereuse_devicehub/resources/event/models.py b/ereuse_devicehub/resources/event/models.py index fcd67dfe..ac226942 100644 --- a/ereuse_devicehub/resources/event/models.py +++ b/ereuse_devicehub/resources/event/models.py @@ -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 `_. + """ 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', diff --git a/ereuse_devicehub/resources/event/models.pyi b/ereuse_devicehub/resources/event/models.pyi index c481f04e..fece6f7a 100644 --- a/ereuse_devicehub/resources/event/models.pyi +++ b/ereuse_devicehub/resources/event/models.pyi @@ -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 diff --git a/ereuse_devicehub/resources/event/rate/main.py b/ereuse_devicehub/resources/event/rate/main.py deleted file mode 100644 index 0de3fccb..00000000 --- a/ereuse_devicehub/resources/event/rate/main.py +++ /dev/null @@ -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) diff --git a/ereuse_devicehub/resources/event/rate/workbench/v1_0.py b/ereuse_devicehub/resources/event/rate/workbench/v1_0.py index 759df128..a3187f76 100644 --- a/ereuse_devicehub/resources/event/rate/workbench/v1_0.py +++ b/ereuse_devicehub/resources/event/rate/workbench/v1_0.py @@ -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 diff --git a/ereuse_devicehub/resources/event/schemas.py b/ereuse_devicehub/resources/event/schemas.py index 6bddabc5..49c6829b 100644 --- a/ereuse_devicehub/resources/event/schemas.py +++ b/ereuse_devicehub/resources/event/schemas.py @@ -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') diff --git a/ereuse_devicehub/resources/event/views.py b/ereuse_devicehub/resources/event/views.py index 2c50a0c9..a7fd500a 100644 --- a/ereuse_devicehub/resources/event/views.py +++ b/ereuse_devicehub/resources/event/views.py @@ -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()