Adding benchmarks and test [Ratev2]

This commit is contained in:
nad 2019-03-10 20:41:10 +01:00
parent 0f508a1d59
commit 147b86f889
4 changed files with 526 additions and 782 deletions

View file

@ -106,6 +106,24 @@ class AppearanceRange(Enum):
return self.name
@unique
class AppearanceRangev2(Enum):
"""Based on usage condition of a device and its functionality aspects/characteristics."""
Z = 'Z. The device is new'
A = 'A. Is like new; without visual damage'
B = 'B. Is in really good condition; small visual damage in difficult places to spot'
C = 'C. Is in good condition; small visual damage in parts that are easy to spot, minor cosmetic blemishes on cabinet)'
D = 'D. Is acceptable; visual damage in visible parts, major cosmetic blemishes on cabinet, missing cosmetic parts..'
E = 'E. Is unacceptable; considerable visual damage, missing essential parts,.'
NONE = 'NA. Grade doesnt exists'
def __str__(self):
return self.name
APPEARANCE_RANGE = 0.5, -0.3
@unique
class FunctionalityRange(Enum):
"""Grades the defects of a device that affect its usage."""
@ -134,6 +152,20 @@ class FunctionalityRangev2(Enum):
def __str__(self):
return self.name
@unique
class BatteryHealthRange(Enum):
"""Grade the battery health status, depending on self report Android system"""
A = 'A. The battery health is very good'
B = 'B. Battery health is good'
C = 'C. Battery health is overheat / over voltage status but can stand the minimum duration'
D = 'D. Battery health is bad; cant stand the minimum duration time'
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
class Bios(Enum):
"""How difficult it has been to set the bios to boot from the network."""

View file

@ -31,7 +31,7 @@ from ereuse_devicehub.resources.device.models import Component, Computer, DataSt
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ErasureStandards, \
FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \
RatingRange, RatingSoftware, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \
TestDataStorageLength, FUNCTIONALITY_RANGE, FunctionalityRangev2
TestDataStorageLength, FUNCTIONALITY_RANGE, FunctionalityRangev2, AppearanceRangev2, BatteryHealthRange
from ereuse_devicehub.resources.event.rate.workbench.v2_0 import QualityRate, FunctionalityRate
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
from ereuse_devicehub.resources.user.models import User
@ -553,6 +553,220 @@ class SnapshotRequest(db.Model):
cascade=CASCADE_OWN))
class Benchmark(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of gauging the performance of a device."""
elapsed = Column(Interval)
@declared_attr
def __mapper_args__(cls):
"""
Defines inheritance.
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
extensions/declarative/api.html
#sqlalchemy.ext.declarative.declared_attr>`_
"""
args = {POLYMORPHIC_ID: cls.t}
if cls.t == 'Benchmark':
args[POLYMORPHIC_ON] = cls.type
return args
class BenchmarkDataStorage(Benchmark):
"""Benchmarks the data storage unit reading and writing speeds."""
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
read_speed = Column(Float(decimal_return_scale=2), nullable=False)
write_speed = Column(Float(decimal_return_scale=2), nullable=False)
def __str__(self) -> str:
return 'Read: {} MB/s, write: {} MB/s'.format(self.read_speed, self.write_speed)
class BenchmarkWithRate(Benchmark):
"""The act of benchmarking a device with a single rate."""
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
rate = Column(Float, nullable=False)
def __str__(self) -> str:
return '{} points'.format(self.rate)
class BenchmarkProcessor(BenchmarkWithRate):
"""Benchmarks a processor by executing `BogoMips
<https://en.wikipedia.org/wiki/BogoMips>`_. Note that this is not
a reliable way of rating processors and we keep it for compatibility
purposes.
"""
pass
class BenchmarkProcessorSysbench(BenchmarkProcessor):
"""Benchmarks a processor by using the processor benchmarking
utility of `sysbench <https://github.com/akopytov/sysbench>`_.
"""
pass
class BenchmarkRamSysbench(BenchmarkWithRate):
pass
class BenchmarkGraphicCard(BenchmarkWithRate):
pass
class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of testing the physical condition of a device and its
components.
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):
"""
Defines inheritance.
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
extensions/declarative/api.html
#sqlalchemy.ext.declarative.declared_attr>`_
"""
args = {POLYMORPHIC_ID: cls.t}
if cls.t == 'Test':
args[POLYMORPHIC_ON] = cls.type
return args
class TestDataStorage(Test):
"""
The act of testing the data storage.
Testing is done using the `S.M.A.R.T self test
<https://en.wikipedia.org/wiki/S.M.A.R.T.#Self-tests>`_. Note
that not all data storage units, specially some new PCIe ones, do not
support SMART testing.
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)
assessment = Column(Boolean)
reallocated_sector_count = Column(SmallInteger)
power_cycle_count = Column(SmallInteger)
reported_uncorrectable_errors = Column(SmallInteger)
command_timeout = Column(Integer)
current_pending_sector_count = Column(SmallInteger)
offline_uncorrectable = Column(SmallInteger)
remaining_lifetime_percentage = Column(SmallInteger)
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
# Define severity
# As of https://www.backblaze.com/blog/hard-drive-smart-stats/ and
# https://www.backblaze.com/blog-smart-stats-2014-8.html
# We can guess some future disk failures by analyzing some SMART data.
if self.severity is None:
# Test finished successfully
if not self.assessment:
self.severity = Severity.Error
elif self.current_pending_sector_count and self.current_pending_sector_count > 40 \
or self.reallocated_sector_count and self.reallocated_sector_count > 10:
self.severity = Severity.Warning
def __str__(self) -> str:
t = inflection.humanize(self.status)
if self.lifetime:
t += ' with a lifetime of {:.1f} years.'.format(self.lifetime.days / 365)
t += self.description
return t
class StressTest(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.
"""
@validates('elapsed')
def is_minute_and_bigger_than_1_minute(self, _, value: timedelta):
seconds = value.total_seconds()
assert not bool(seconds % 60)
assert seconds >= 60
return value
def __str__(self) -> str:
return '{}. Computing for {}'.format(self.severity, self.elapsed)
class TestAudio(Test):
"""
Test to check all this aspects related with audio functions, Manual Tests??
"""
loudspeaker = Column(BDEnum(LoudspeakerRange))
loudspeaker.comment = 'Range to determine if the speaker is working properly and what sound quality it has.'
microphone = Column(Boolean)
microphone.comment = 'This evaluate if microphone works correctly'
class TestConnectivity(Test):
"""
Test to check all this aspects related with functionality connections in devices
"""
SIM = Column(Boolean)
SIM.comment = 'Evaluate if SIM works'
wifi = Column(Boolean)
wifi.comment = 'Evaluate if wifi connection works correctly'
bluetooth = Column(Boolean)
bluetooth.comment = 'Evaluate if bluetooth works'
usb = Column(DBEnum(USBPortRange))
usb.comment = 'Evaluate if usb port was detected and charger plug works'
class TestBattery(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.comment = """
Some batteries can report a self-check life status.
"""
battery_health = Column(DBEnum(BatteryHealthRange))
battery_health.comment = BatteryHealthRange.__doc__
class TestBios(Test):
"""
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.comment = """
Motherboards do a self check when powering up (R2 p.23), test PASS if no beeps, codes, or errors appears.
"""
# TODO Eum(BiosAccesRange)
bios_access_range = Column(BDEnum(BiosAccessRange))
bios_access_range.comment = 'Range of difficult to acces BIOS'
class TestVisual(ManualRate):
"""
Special test that its aspects are represented with grade and focuses mainly on
the aesthetic or cosmetic defects of important parts of a device.
Like defects on chassis, display, ..
"""
# TODO Consider if add some new var in appearance aspect??
appearance_range = Column(DBEnum(AppearanceRangev2))
appearance_range.comment = AppearanceRangev2.__doc__
class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of grading the appearance, performance, and functionality
of a device.
@ -651,50 +865,13 @@ class ManualRate(IndividualRate):
raise NotImplementedError()
class ComputerRate(ManualRate):
id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id), primary_key=True)
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE))
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE))
data_storage = Column(Float(decimal_return_scale=2),
check_range('data_storage', *RATE_POSITIVE))
graphic_card = Column(Float(decimal_return_scale=2),
check_range('graphic_card', *RATE_POSITIVE))
bios = Column(Float(decimal_return_scale=2),
check_range('bios', *RATE_POSITIVE))
bios_range = Column(DBEnum(Bios))
bios_range.comment = Bios.__doc__
# TODO is necessary?
class WorkbenchComputer(ManualRate):
pass
# todo ensure for WorkbenchRate version and software are not None when inserting them
def ratings(self):
"""
Computes all the possible rates taking this rating as a model.
Returns a set of ratings, including this one, which is mutated,
and the final :class:`.AggregateRate`.
"""
from ereuse_devicehub.resources.event.rate.main import main
return main(self, **app.config.get_namespace('WORKBENCH_RATE_'))
@property
def data_storage_range(self):
if self.data_storage:
return RatingRange.from_score(self.data_storage)
@property
def ram_range(self):
if self.ram:
return RatingRange.from_score(self.ram)
@property
def processor_range(self):
if self.processor:
return RatingRange.from_score(self.processor)
@property
def graphic_card_range(self):
if self.graphic_card:
return RatingRange.from_score(self.graphic_card)
class WorkbenchMobile(ManualRate):
pass
class AggregateRate(Rate):
@ -782,7 +959,7 @@ class AggregateRate(Rate):
return self.workbench.labelling
@classmethod
def from_workbench_rate(cls, rate: QualityRateComputer):
def from_workbench_rate(cls, rate: QualityRate):
aggregate = cls()
aggregate.rating = rate.rating
aggregate.software = rate.software
@ -793,189 +970,88 @@ class AggregateRate(Rate):
return aggregate
class EreusePrice(Price):
"""The act of setting a price by guessing it using the eReuse.org
algorithm.
This algorithm states that the price is the use value of the device
(represented by its last :class:`.Rate`) multiplied by a constants
value agreed by a circuit or platform.
class QualityRate(Rate):
"""
MULTIPLIER = {
Desktop: 20,
Laptop: 30
}
class Type:
def __init__(self, percentage: float, price: Decimal) -> None:
# see https://stackoverflow.com/a/29651462 for the - 0.005
self.amount = EreusePrice.to_price(price * Decimal(percentage))
self.percentage = EreusePrice.to_price(price * Decimal(percentage))
self.percentage = round(percentage - 0.005, 2)
class Service:
REFURBISHER, PLATFORM, RETAILER = 0, 1, 2
STANDARD, WARRANTY2 = 'STD', 'WR2'
SCHEMA = {
Desktop: {
RatingRange.HIGH: {
STANDARD: (0.35125, 0.204375, 0.444375),
WARRANTY2: (0.47425, 0.275875, 0.599875)
},
RatingRange.MEDIUM: {
STANDARD: (0.385, 0.2558333333, 0.3591666667),
WARRANTY2: (0.539, 0.3581666667, 0.5028333333)
},
RatingRange.LOW: {
STANDARD: (0.5025, 0.30875, 0.18875),
},
},
Laptop: {
RatingRange.HIGH: {
STANDARD: (0.3469230769, 0.195, 0.4580769231),
WARRANTY2: (0.4522307692, 0.2632307692, 0.6345384615)
},
RatingRange.MEDIUM: {
STANDARD: (0.382, 0.1735, 0.4445),
WARRANTY2: (0.5108, 0.2429, 0.6463)
},
RatingRange.LOW: {
STANDARD: (0.4528571429, 0.2264285714, 0.3207142857),
}
}
}
SCHEMA[Server] = SCHEMA[Desktop]
def __init__(self, device, rating_range, role, price: Decimal) -> None:
cls = device.__class__ if device.__class__ != Server else Desktop
rate = self.SCHEMA[cls][rating_range]
self.standard = EreusePrice.Type(rate[self.STANDARD][role], price)
if self.WARRANTY2 in rate:
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
def __init__(self, rating: AggregateRate, **kwargs) -> None:
if rating.rating_range == RatingRange.VERY_LOW:
raise ValueError('Cannot compute price for Range.VERY_LOW')
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts
price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP)
super().__init__(rating=rating,
device=rating.device,
price=price,
software=kwargs.pop('software', app.config['PRICE_SOFTWARE']),
version=kwargs.pop('version', app.config['PRICE_VERSION']),
**kwargs)
self._compute()
@orm.reconstructor
def _compute(self):
The act of compute performance (quality) a device
"""
Calculates eReuse.org prices when initializing the
instance from the price and other properties.
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE),
comment='Is a test explain cpu component.')
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE),
comment='RAM memory rate.')
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
comment='Data storage rate, like HHD, SSD.')
""" MOBILE QUALITY RATE """
display = Column(Float(decimal_return_scale=2), check_range('display', *RATE_POSITIVE))
display.comment = 'Display rate, screen resolution and size to calculate PPI and convert in score'
battery = Column(Float(decimal_return_scale=2), check_range('battery', *RATE_POSITIVE),
comment='Battery rate is related with capacity and its health')
camera = Column(Float(decimal_return_scale=2), check_range('camera', *RATE_POSITIVE),
comment='Camera rate take into account resolution')
""" COMPUTER QUALITY RATE """
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
comment='Graphic card score in performance, amount of memory and benchmark result')
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
comment='Network adapter rate, take it speed limit')
bios = Column(Float(decimal_return_scale=2), check_range('bios', *RATE_POSITIVE))
bios_range = Column(DBEnum(Bios))
bios_range.comment = Bios.__doc__
@property
def ram_range(self):
return self.workbench.ram_range
@property
def processor_range(self):
return self.workbench.processor_range
@property
def display_range(self):
return self.workbench.data_storage_range
@property
def data_storage_range(self):
return self.workbench.data_storage_range
@property
def battery_range(self):
return self.workbench.ram_range
@property
def camera_range(self):
return self.workbench_mobile.camera_range
@property
def graphic_card_range(self):
return self.workbench_mobil.graphic_card_range
@property
def network_adapter_range(self):
return self.workbench_mobil.network_adapter_range
@property
def bios_range(self):
return self.workbench_mobil.bios_range
class FunctionalityRate(Rate):
"""
self.refurbisher = self._service(self.Service.REFURBISHER)
self.retailer = self._service(self.Service.RETAILER)
self.platform = self._service(self.Service.PLATFORM)
if hasattr(self.refurbisher, 'warranty2'):
self.warranty2 = round(self.refurbisher.warranty2.amount
+ self.retailer.warranty2.amount
+ self.platform.warranty2.amount, 2)
The act of compute functionality characteristics of a device.
Two functionality variables, functionality rate (float) and functionality range (Enum)
def _service(self, role):
return self.Service(self.device, self.rating.rating_range, role, self.price)
class EreusePrice(Price):
"""The act of setting a price by guessing it using the eReuse.org
algorithm.
This algorithm states that the price is the use value of the device
(represented by its last :class:`.Rate`) multiplied by a constants
value agreed by a circuit or platform.
"""
MULTIPLIER = {
Desktop: 20,
Laptop: 30
}
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
functionality = Column(Float(decimal_return_scale=2), check_range('functionality', *FUNCTIONALITY_RANGE))
functionality.comment = 'Functionality rate of a device'
class Type:
def __init__(self, percentage: float, price: Decimal) -> None:
# see https://stackoverflow.com/a/29651462 for the - 0.005
self.amount = EreusePrice.to_price(price * Decimal(percentage))
self.percentage = EreusePrice.to_price(price * Decimal(percentage))
self.percentage = round(percentage - 0.005, 2)
class Service:
REFURBISHER, PLATFORM, RETAILER = 0, 1, 2
STANDARD, WARRANTY2 = 'STD', 'WR2'
SCHEMA = {
Desktop: {
RatingRange.HIGH: {
STANDARD: (0.35125, 0.204375, 0.444375),
WARRANTY2: (0.47425, 0.275875, 0.599875)
},
RatingRange.MEDIUM: {
STANDARD: (0.385, 0.2558333333, 0.3591666667),
WARRANTY2: (0.539, 0.3581666667, 0.5028333333)
},
RatingRange.LOW: {
STANDARD: (0.5025, 0.30875, 0.18875),
},
},
Laptop: {
RatingRange.HIGH: {
STANDARD: (0.3469230769, 0.195, 0.4580769231),
WARRANTY2: (0.4522307692, 0.2632307692, 0.6345384615)
},
RatingRange.MEDIUM: {
STANDARD: (0.382, 0.1735, 0.4445),
WARRANTY2: (0.5108, 0.2429, 0.6463)
},
RatingRange.LOW: {
STANDARD: (0.4528571429, 0.2264285714, 0.3207142857),
}
}
}
SCHEMA[Server] = SCHEMA[Desktop]
def __init__(self, device, rating_range, role, price: Decimal) -> None:
cls = device.__class__ if device.__class__ != Server else Desktop
rate = self.SCHEMA[cls][rating_range]
self.standard = EreusePrice.Type(rate[self.STANDARD][role], price)
if self.WARRANTY2 in rate:
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
def __init__(self, rating: AggregateRate, **kwargs) -> None:
if rating.rating_range == RatingRange.VERY_LOW:
raise ValueError('Cannot compute price for Range.VERY_LOW')
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts
price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP)
super().__init__(rating=rating,
device=rating.device,
price=price,
software=kwargs.pop('software', app.config['PRICE_SOFTWARE']),
version=kwargs.pop('version', app.config['PRICE_VERSION']),
**kwargs)
self._compute()
@orm.reconstructor
def _compute(self):
"""
Calculates eReuse.org prices when initializing the
instance from the price and other properties.
"""
self.refurbisher = self._service(self.Service.REFURBISHER)
self.retailer = self._service(self.Service.RETAILER)
self.platform = self._service(self.Service.PLATFORM)
if hasattr(self.refurbisher, 'warranty2'):
self.warranty2 = round(self.refurbisher.warranty2.amount
+ self.retailer.warranty2.amount
+ self.platform.warranty2.amount, 2)
def _service(self, role):
return self.Service(self.device, self.rating.rating_range, role, self.price)
functionality_range = Column(DBEnum(FunctionalityRangev2))
functionality_range.comment = FunctionalityRangev2.__doc__
class ResultRate(Rate):
class FinalRate(Rate):
"""The act of grading the appearance, quality (performance), and functionality
of a device.
@ -987,7 +1063,7 @@ class ResultRate(Rate):
5. ``Cost of repair``.
There are types of rating a device:
There are different types of rating a device:
1. Rate Quality
2. Rate Functionality
@ -1003,17 +1079,22 @@ class ResultRate(Rate):
"""
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
quality_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
quality_id = Column(UUID(as_uuid=True), ForeignKey(QualityRate.id))
quality_id.comment = """The Quality Rate used to generate this
aggregation, or None if none used.
"""
quality = relationship(QualityRate, )
func_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
func_id.comment = """The Functionality Rate used to generate this
functionality_id = Column(UUID(as_uuid=True), ForeignKey(FunctionalityRate.id))
functionality_id.comment = """The Functionality Rate used to generate this
aggregation, or None if none used.
"""
functionality = relationship(FunctionalityRate, )
final_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
# TODO is necessary?? create a AppearanceRate..
appearance = relationship(TestVisual, )
final_id = Column(UUID(as_uuid=True), ForeignKey(FinalRate.id))
final_id.comment = """The Final Rate used to generate this
aggregation, or None if none used.
"""
@ -1058,6 +1139,8 @@ class ResultRate(Rate):
collection_class=OrderedSet),
primaryjoin=workbench_mobile_id == QualityRateMobile.id)
# TODO Add more source that global rate can use it
def __init__(self, *args, **kwargs) -> None:
kwargs.setdefault('version', StrictVersion('1.0'))
super().__init__(*args, **kwargs)
@ -1081,11 +1164,11 @@ class ResultRate(Rate):
pass
@classmethod
def functionality_category(cls, quality: QualityRate):
def functionality_category(cls, functionality: FunctionalityRate):
pass
@classmethod
def appearance_category(cls, quality: QualityRate):
def appearance_category(cls, appearance: ManualRate):
pass
@classmethod
@ -1151,103 +1234,6 @@ class ResultRate(Rate):
def labelling(self):
return self.workbench.labelling
@classmethod
def from_workbench_rate(cls, rate: QualityRateComputer):
aggregate = cls()
aggregate.rating = rate.rating
aggregate.software = rate.software
aggregate.appearance = rate.appearance
aggregate.functionality = rate.functionality
aggregate.device = rate.device
aggregate.workbench = rate
return aggregate
class BenchmarkRate:
"""
Common class to group by benchmark rate classes
"""
class BenchmarkQuality(BenchmarkRate):
"""
Computes quality benchmarks results to aggregate in result rate
"""
cpu_sysbench = Column(Float(decimal_return_scale=2))
cpu_sysbench.comment = 'Benchmark processor component with sysbench tool'
ram_sysbench = Column(Float(decimal_return_scale=2))
ram_sysbench.comment = 'Benchmark RAM component'
# gpu_sysbench = Column(Float(decimal_return_scale=2)) todo how to do?
data_storage_sysbench = Column(Float(decimal_return_scale=2))
data_storage_sysbench.comment = 'Benchmark data storage component with sysbench tool'
data_storage_smart = Column(Float(decimal_return_scale=2))
data_storage_smart.comment = 'Benchmark data storage component with SMART tool'
class FunctionalityTest(Test):
"""
Class where are generic devices functionality aspects
"""
class TestAudio(FunctionalityTest):
"""
Test to check all this aspects related with audio fucntions
"""
loudspeaker = Column(BDEnum(LoudspeakerRange))
loudspeaker.comment = 'Range to determine if the speaker is working properly and what sound quality it has.'
microphone = Column(Boolean)
microphone.comment = 'This evaluate if microphone works correctly'
class TestConnectivity(FunctionalityTest):
"""
Test to check all this aspects related with functionality connections in devices
"""
SIM = Column(Boolean)
SIM.comment = 'Evaluate if SIM works'
wifi = Column(Boolean)
wifi.comment = 'Evaluate if wifi connection works correctly'
bluetooth = Column(Boolean)
bluetooth.comment = 'Evaluate if bluetooth works'
usb = Column(DBEnum())
usb.comment = 'Evaluate if usb port was detected and charger plug works'
class TestBattery(FunctionalityTest):
"""
Test of length of charge. Source R2: Minimum X minutes discharging the device
"""
battery_duration = Column(Boolean())
battery_duration.comment = ''
class TestBios(FunctionalityTest):
"""
Test that determinate if is difficult to access BIOS, like need password, are protected..
"""
bios_range = Column(DBEnum())
bios_range.comment = 'Range of difficult to acces BIOS'
class TestApperance():
"""
Class with appearance characteristics
"""
chassis_defects_range = Column(BDEnum(ChassisRange))
chassis_defects_range.comment = 'Range to determinate cosmetic defects on chassis like scratches'
class TestVisual(TestAppearance):
"""
Check aesthetics or cosmetic aspects. Like defects on chassis, display, ..
"""
camera_defects_range = Column(BDEnum(CameraRange))
camera_defects_range = 'Range to determinate cosmetic defects on camera'
display_defects_range = Column(BDEnum(DisplayRange))
display_defects_range = 'Range to determinate cosmetic defects on display'
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of setting a trading price for the device.
@ -1316,150 +1302,95 @@ class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
return '{0:0.2f} {1}'.format(self.price, self.currency)
class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of testing the physical condition of a device and its
components.
class EreusePrice(Price):
"""The act of setting a price by guessing it using the eReuse.org
algorithm.
Testing errors and warnings are easily taken in
:attr:`ereuse_devicehub.resources.device.models.Device.working`.
This algorithm states that the price is the use value of the device
(represented by its last :class:`.Rate`) multiplied by a constants
value agreed by a circuit or platform.
"""
elapsed = Column(Interval, nullable=False)
MULTIPLIER = {
Desktop: 20,
Laptop: 30
}
@declared_attr
def __mapper_args__(cls):
class Type:
def __init__(self, percentage: float, price: Decimal) -> None:
# see https://stackoverflow.com/a/29651462 for the - 0.005
self.amount = EreusePrice.to_price(price * Decimal(percentage))
self.percentage = EreusePrice.to_price(price * Decimal(percentage))
self.percentage = round(percentage - 0.005, 2)
class Service:
REFURBISHER, PLATFORM, RETAILER = 0, 1, 2
STANDARD, WARRANTY2 = 'STD', 'WR2'
SCHEMA = {
Desktop: {
RatingRange.HIGH: {
STANDARD: (0.35125, 0.204375, 0.444375),
WARRANTY2: (0.47425, 0.275875, 0.599875)
},
RatingRange.MEDIUM: {
STANDARD: (0.385, 0.2558333333, 0.3591666667),
WARRANTY2: (0.539, 0.3581666667, 0.5028333333)
},
RatingRange.LOW: {
STANDARD: (0.5025, 0.30875, 0.18875),
},
},
Laptop: {
RatingRange.HIGH: {
STANDARD: (0.3469230769, 0.195, 0.4580769231),
WARRANTY2: (0.4522307692, 0.2632307692, 0.6345384615)
},
RatingRange.MEDIUM: {
STANDARD: (0.382, 0.1735, 0.4445),
WARRANTY2: (0.5108, 0.2429, 0.6463)
},
RatingRange.LOW: {
STANDARD: (0.4528571429, 0.2264285714, 0.3207142857),
}
}
}
SCHEMA[Server] = SCHEMA[Desktop]
def __init__(self, device, rating_range, role, price: Decimal) -> None:
cls = device.__class__ if device.__class__ != Server else Desktop
rate = self.SCHEMA[cls][rating_range]
self.standard = EreusePrice.Type(rate[self.STANDARD][role], price)
if self.WARRANTY2 in rate:
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
def __init__(self, rating: AggregateRate, **kwargs) -> None:
if rating.rating_range == RatingRange.VERY_LOW:
raise ValueError('Cannot compute price for Range.VERY_LOW')
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts
price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP)
super().__init__(rating=rating,
device=rating.device,
price=price,
software=kwargs.pop('software', app.config['PRICE_SOFTWARE']),
version=kwargs.pop('version', app.config['PRICE_VERSION']),
**kwargs)
self._compute()
@orm.reconstructor
def _compute(self):
"""
Defines inheritance.
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
extensions/declarative/api.html
#sqlalchemy.ext.declarative.declared_attr>`_
Calculates eReuse.org prices when initializing the
instance from the price and other properties.
"""
args = {POLYMORPHIC_ID: cls.t}
if cls.t == 'Test':
args[POLYMORPHIC_ON] = cls.type
return args
self.refurbisher = self._service(self.Service.REFURBISHER)
self.retailer = self._service(self.Service.RETAILER)
self.platform = self._service(self.Service.PLATFORM)
if hasattr(self.refurbisher, 'warranty2'):
self.warranty2 = round(self.refurbisher.warranty2.amount
+ self.retailer.warranty2.amount
+ self.platform.warranty2.amount, 2)
class TestDataStorage(Test):
"""
The act of testing the data storage.
Testing is done using the `S.M.A.R.T self test
<https://en.wikipedia.org/wiki/S.M.A.R.T.#Self-tests>`_. Note
that not all data storage units, specially some new PCIe ones, do not
support SMART testing.
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)
assessment = Column(Boolean)
reallocated_sector_count = Column(SmallInteger)
power_cycle_count = Column(SmallInteger)
reported_uncorrectable_errors = Column(SmallInteger)
command_timeout = Column(Integer)
current_pending_sector_count = Column(SmallInteger)
offline_uncorrectable = Column(SmallInteger)
remaining_lifetime_percentage = Column(SmallInteger)
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
# Define severity
# As of https://www.backblaze.com/blog/hard-drive-smart-stats/ and
# https://www.backblaze.com/blog-smart-stats-2014-8.html
# We can guess some future disk failures by analyzing some SMART data.
if self.severity is None:
# Test finished successfully
if not self.assessment:
self.severity = Severity.Error
elif self.current_pending_sector_count and self.current_pending_sector_count > 40 \
or self.reallocated_sector_count and self.reallocated_sector_count > 10:
self.severity = Severity.Warning
def __str__(self) -> str:
t = inflection.humanize(self.status)
if self.lifetime:
t += ' with a lifetime of {:.1f} years.'.format(self.lifetime.days / 365)
t += self.description
return t
class StressTest(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.
"""
@validates('elapsed')
def is_minute_and_bigger_than_1_minute(self, _, value: timedelta):
seconds = value.total_seconds()
assert not bool(seconds % 60)
assert seconds >= 60
return value
def __str__(self) -> str:
return '{}. Computing for {}'.format(self.severity, self.elapsed)
class Benchmark(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of gauging the performance of a device."""
elapsed = Column(Interval)
@declared_attr
def __mapper_args__(cls):
"""
Defines inheritance.
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
extensions/declarative/api.html
#sqlalchemy.ext.declarative.declared_attr>`_
"""
args = {POLYMORPHIC_ID: cls.t}
if cls.t == 'Benchmark':
args[POLYMORPHIC_ON] = cls.type
return args
class BenchmarkDataStorage(Benchmark):
"""Benchmarks the data storage unit reading and writing speeds."""
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
read_speed = Column(Float(decimal_return_scale=2), nullable=False)
write_speed = Column(Float(decimal_return_scale=2), nullable=False)
def __str__(self) -> str:
return 'Read: {} MB/s, write: {} MB/s'.format(self.read_speed, self.write_speed)
class BenchmarkWithRate(Benchmark):
"""The act of benchmarking a device with a single rate."""
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
rate = Column(Float, nullable=False)
def __str__(self) -> str:
return '{} points'.format(self.rate)
class BenchmarkProcessor(BenchmarkWithRate):
"""Benchmarks a processor by executing `BogoMips
<https://en.wikipedia.org/wiki/BogoMips>`_. Note that this is not
a reliable way of rating processors and we keep it for compatibility
purposes.
"""
pass
class BenchmarkProcessorSysbench(BenchmarkProcessor):
"""Benchmarks a processor by using the processor benchmarking
utility of `sysbench <https://github.com/akopytov/sysbench>`_.
"""
class BenchmarkRamSysbench(BenchmarkWithRate):
pass
def _service(self, role):
return self.Service(self.device, self.rating.rating_range, role, self.price)
class ToRepair(EventWithMultipleDevices):

View file

@ -2,7 +2,6 @@ from enum import Enum
from typing import Iterable
from ereuse_devicehub.resources.device.models import DataStorage, Processor, RamModule, Device
from ereuse_devicehub.resources.enums import RatingRange
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, WorkbenchRate
from ereuse_devicehub.resources.event.rate.rate import BaseRate
@ -93,7 +92,7 @@ class FunctionalityRate(BaseRate):
LOUDSPEAKER_WEIGHT = 0.15
MICROPHONE_WEIGHT = 0.15
def compute(self, FunctionalityDevice: FunctionalityRate):
def compute(self, FunctionalityRate: FunctionalityRate):
"""
:param FunctionalityDevice: List[Boolean]
@ -116,6 +115,7 @@ class FunctionalityRate(BaseRate):
class Appearance(Range):
"""
APPEARANCE GRADE [0.5,-0.5]
Enum(AppearanceRangev2)
"""
Z = 0.5
@ -127,166 +127,7 @@ class Appearance(Range):
NONE = -0.3
class QualityRate(Rate):
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE),
comment='Is a test explain cpu component.')
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE),
comment='RAM memory rate.')
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
comment='Data storage rate, like HHD, SSD.')
@property
def ram_range(self):
return self.workbench.ram_range
@property
def processor_range(self):
return self.workbench.processor_range
@property
def display_range(self):
return self.workbench.data_storage_range
@property
def data_storage_range(self):
return self.workbench.data_storage_range
@property
def battery_range(self):
return self.workbench.ram_range
@property
def camera_range(self):
return self.workbench_mobile.camera_range
@property
def graphic_card_range(self):
return self.workbench_mobil.graphic_card_range
class QualityRateComputer(QualityRate):
id = Column(UUID(as_uuid=True), ForeignKey(QualityRate.id), primary_key=True)
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE),
comment='Is a test explain cpu component.')
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE),
comment='RAM memory rate.')
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
comment='Data storage rate, like HHD, SSD.')
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
comment='Graphic card score in performance, amount of memory and benchmark result')
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
comment='Network adapter rate, take it speed limit')
bios = Column(Float(decimal_return_scale=2), check_range('bios', *RATE_POSITIVE))
bios_range = Column(DBEnum(Bios))
bios_range.comment = Bios.__doc__
# todo ensure for WorkbenchRate version and software are not None when inserting them
def ratings(self):
"""
#Computes all the possible rates taking this rating as a model.
#Returns a set of ratings, including this one, which is mutated,
#and the final :class:`.AggregateRate`.
"""
from ereuse_devicehub.resources.event.rate.main import main
return main(self, **app.config.get_namespace('WORKBENCH_RATE_'))
@property
def graphic_card_range(self):
if self.graphic_card:
return RatingRange.from_score(self.graphic_card)
@property
def network_adapter_range(self):
return self.workbench_mobil.network_adapter_range
@property
def bios_range(self):
return self.workbench_mobil.bios_range
class QualityRateMobile(QualityRate):
id = Column(UUID(as_uuid=True), ForeignKey(QualityRate.id), primary_key=True)
display = Column(Float(decimal_return_scale=2), check_range('display', *RATE_POSITIVE))
display.comment = 'Display rate, screen resolution and size to calculate PPI and convert in score'
battery = Column(Float(decimal_return_scale=2), check_range('battery', *RATE_POSITIVE),
comment='Battery rate is related with capacity and its health')
camera = Column(Float(decimal_return_scale=2), check_range('camera', *RATE_POSITIVE),
comment='Camera rate take into account resolution')
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
comment='Graphic card score in performance, amount of memory and benchmark result')
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
comment='Network adapter rate, take it speed limit')
bios = Column(Float(decimal_return_scale=2), check_range('bios', *RATE_POSITIVE))
bios_range = Column(DBEnum(Bios))
bios_range.comment = Bios.__doc__
# todo ensure for WorkbenchRate version and software are not None when inserting them
def ratings(self):
"""
#Computes all the possible rates taking this rating as a model.
"""
from ereuse_devicehub.resources.event.rate.main import main
return main(self, **app.config.get_namespace('WORKBENCH_RATE_'))
@property
def display_range(self):
if self.data_storage:
return RatingRange.from_score(self.data_storage)
@property
def battery_range(self):
if self.ram:
return RatingRange.from_score(self.ram)
@property
def camera_range(self):
if self.processor:
return RatingRange.from_score(self.processor)
@property
def graphic_card_range(self):
if self.graphic_card:
return RatingRange.from_score(self.graphic_card)
class FunctionalityRate(Rate):
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
functionality = Column(Float(decimal_return_scale=2), check_range('functionality', *FUNCTIONALITY_RANGE))
functionality.comment = 'Functionality rate of a device'
functionality_range = Column(DBEnum(FunctionalityRangev2))
functionality_range.comment = FunctionalityRangev2.__doc__
connectivity = Column(Float(decimal_return_scale=2),
comment='This punctuation covers a series of aspects related to connectivity.')
audio = Column(Float(decimal_return_scale=2), comment='Take into account loudspeaker and microphone')
@property
def connectivity_rate(self):
yield
@property
def audio_rate(self):
yield
@property
def test_buttonse(self):
yield
@classmethod
def test_camera_defects(self):
yield
class ResultRate(Rate):
class FinalRate(Rate):
"""The act of grading the appearance, quality (performance), and functionality
of a device.
@ -297,37 +138,19 @@ class ResultRate(Rate):
4. ``Market value``.
5. ``Cost of repair``.
There are types of rating a device:
1. Rate Quality
2. Rate Functionality
3. Rate Final
List of source where can input information of rating a device:
1. When processing the device with Workbench Computer/Mobile.
2. Using the Android App (through Scan).
3.
4. Anytime after manually written in a form in the website.
"""
quality_rate = QualityRate()
functionality_rate = FunctionalityRate()
appearance_rate = Appearance()
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
quality_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
quality_id.comment = """The Quality Rate used to generate this
aggregation, or None if none used.
"""
final_rate = None
func_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
func_id.comment = """The Functionality Rate used to generate this
aggregation, or None if none used.
"""
final_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
final_id.comment = """The Final Rate used to generate this
aggregation, or None if none used.
"""
# TODO Develop merge rate aspects (categories)
""" MANUAL INPUT """
manual_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
@ -408,7 +231,6 @@ class ResultRate(Rate):
pass
class DisplayRate(QualityRate):
"""
Calculate a DisplayRate

View file

@ -109,6 +109,77 @@ class StepRandom(Step):
__doc__ = m.StepRandom.__doc__
class Benchmark(EventWithOneDevice):
__doc__ = m.Benchmark.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
class BenchmarkDataStorage(Benchmark):
__doc__ = m.BenchmarkDataStorage.__doc__
read_speed = Float(required=True, data_key='readSpeed')
write_speed = Float(required=True, data_key='writeSpeed')
class BenchmarkWithRate(Benchmark):
__doc__ = m.BenchmarkWithRate.__doc__
rate = Float(required=True)
class BenchmarkProcessor(BenchmarkWithRate):
__doc__ = m.BenchmarkProcessor.__doc__
class BenchmarkProcessorSysbench(BenchmarkProcessor):
__doc__ = m.BenchmarkProcessorSysbench.__doc__
class BenchmarkRamSysbench(BenchmarkWithRate):
__doc__ = m.BenchmarkRamSysbench.__doc__
class BenchmarkGraphicCard(BenchmarkWithRate):
__doc__ = m.BenchmarkGraphicCard.__doc__
class Test(EventWithOneDevice):
__doc__ = m.Test.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
class TestDataStorage(Test):
__doc__ = m.TestDataStorage.__doc__
length = EnumField(TestDataStorageLength, required=True)
status = SanitizedStr(lower=True, validate=Length(max=STR_SIZE), required=True)
lifetime = TimeDelta(precision=TimeDelta.HOURS)
assessment = Boolean()
reallocated_sector_count = Integer(data_key='reallocatedSectorCount')
power_cycle_count = Integer(data_key='powerCycleCount')
reported_uncorrectable_errors = Integer(data_key='reportedUncorrectableErrors')
command_timeout = Integer(data_key='commandTimeout')
current_pending_sector_count = Integer(data_key='currentPendingSectorCount')
offline_uncorrectable = Integer(data_key='offlineUncorrectable')
remaining_lifetime_percentage = Integer(data_key='remainingLifetimePercentage')
class StressTest(Test):
__doc__ = m.StressTest.__doc__
class TestAudio(Test):
__doc__ = m.TestAudio.__doc__
class TestConnectivity(Test):
__doc__ = m.TestConnectivity.__doc__
class TestBattery(Test):
__doc__ = m.TestBattery.__doc__
class TestVisual(ManualRate):
__doc__ = m.TestVisual.__doc__
class Rate(EventWithOneDevice):
__doc__ = m.Rate.__doc__
@ -142,7 +213,7 @@ class ManualRate(IndividualRate):
labelling = Boolean(description=m.ManualRate.labelling.comment)
class WorkbenchRate(ManualRate):
class WorkbencComputer(ManualRate):
__doc__ = m.WorkbenchRate.__doc__
processor = Float()
ram = Float()
@ -158,26 +229,8 @@ class WorkbenchRate(ManualRate):
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
"""
WORKBENCH RATE COMPUTER
Adaptation of old class WorkbenchRate
"""
class WorkbenchRateComputer(ManualRate):
__doc__ = m.WorkbenchRate.__doc__
processor = Float()
ram = Float()
data_storage = Float()
graphic_card = Float()
bios = Float()
bios_range = EnumField(Bios,
description=m.WorkbenchComputerRate.bios_range.comment,
data_key='biosRange')
data_storage_range = EnumField(RatingRange, dump_only=True, data_key='dataStorageRange')
ram_range = EnumField(RatingRange, dump_only=True, data_key='ramRange')
processor_range = EnumField(RatingRange, dump_only=True, data_key='processorRange')
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
class WorkbenchMobile(ManualRate):
pass
class AggregateRate(Rate):
@ -210,6 +263,9 @@ class AggregateRate(Rate):
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
""" RATE v2 CODE"""
class QualityRate(Rate):
__doc__ = m.QualityRate.__doc__
@ -217,37 +273,17 @@ class QualityRate(Rate):
processor = Float(dump_only=True, description=m.QualityRate.processor.comment)
data_storage = Float(dump_only=True, description=m.QualityRate.data_storage.comment)
graphic_card = Float(dump_only=True, description=m.QualityRate.processor.comment)
network_adapter = Float(dump_only=True, description=m.QualityRate.network_adapter.comment)
""" New class for WorkbenchRate in Rate v2"""
display = Float(dump_only=True, description=m.QualityRate.display.comment)
battery = Float(dump_only=True, description=m.QualityRate.batter.comment)
camera = Float(dump_only=True, description=m.QualityRate.camera.comment)
class QualityRateComputer(Rate):
__doc__ = m.QualityRateComputer.__doc__
""" List of components appears in general quality rate a device
ram = Float(dump_only=True, description=m.QualityRateComputer.ram.comment)
processor = Float(dump_only=True, description=m.QualityRateComputer.processor.comment)
data_storage = Float(dump_only=True, description=m.QualityRateComputer.data_storage.comment)
"""
graphic_card = Float(dump_only=True, description=m.QualityRateComputer.processor.comment)
network_adapter = Float(dump_only=True, description=m.QualityRateComputer.network_adapter.comment)
class QualityRateMobile(Rate):
__doc__ = m.QualityRateMobile.__doc__
""" List of components appears in general quality rate a device
ram = Float(dump_only=True, description=m.QualityRateMobile.ram.comment)
processor = Float(dump_only=True, description=m.QualityRateMobile.processor.comment)
data_storage = Float(dump_only=True, description=m.QualityRateMobile.data_storage.comment)
"""
display = Float(dump_only=True, description=m.QualityRateMobile.display.comment)
battery = Float(dump_only=True, description=m.QualityRateMobile.batter.comment)
camera = Float(dump_only=True, description=m.QualityRateMobile.camera.comment)
bios = EnumField(Bios, dump_only=True)
bios_range = EnumField(Bios,
description=m.WorkbenchRate.bios_range.comment,
data_key='biosRange')
class FunctionalityRate(Rate):
@ -255,49 +291,28 @@ class FunctionalityRate(Rate):
functionality = EnumField(dump_only=True, description=m.FunctionalityRate.functionality.comment)
functionality_range = EnumField(dump_only=True, description=m.FunctionalityRate.functionality_range.comment)
connectivity_rate = EnumField(dump_only=True, description=m.FunctionalityRate.connectivity_rate.comment)
audio_rate = EnumField(dump_only=True, description=m.FunctionalityRate.audio_rate.comment)
# TODO Finish input rates (internal and external sources) - Whats really interesting to save in BD?? Whichs aspects?
class FinalRate(Rate):
__doc__ = m.FinalRate.__doc__
quality = NestedOn(QualityRate, dump_only=True,
description=m.QualityRate.quality_id.comment)
functionality = NestedOn(FunctionalityRate, dump_only=True,
description=m.FunctionalityRange.functionality_id.comment)
appearance = NestedOn(TestVisual, dump_only=True)
workbench_computer = NestedOn(WorkbenchComputer, dump_only=True,
description=m.ResultRate.workbench_computer_id.comment)
workbench_mobile = NestedOn(WorkbenchMobile, dump_only=True,
description=m.ResultRate.workbench_mobile_id.comment)
bios = EnumField(Bios, dump_only=True)
bios_range = EnumField(Bios,
description=m.WorkbenchRate.bios_range.comment,
data_key='biosRange')
# TODO Finish input rates (internal and external sources) - Whats really interesting to save in BD?? Whichs aspects?
class ResultRate(Rate):
__doc__ = m.ResultRate.__doc__
# TODO ask for what to do NestedOn??
workbench_computer = NestedOn(WorkbenchRateComputer, dump_only=True,
description=m.ResultRate.workbench_computer_id.comment)
workbench_mobile = NestedOn(WorkbenchRateMobile, dump_only=True,
description=m.ResultRate.workbench_mobile_id.comment)
manual_computer = NestedOn(ManualRateComputer,
dump_only=True,
description=m.ResultRate.manual_computer_id.comment)
processor = Float(dump_only=True)
ram = Float(dump_only=True)
data_storage = Float(dump_only=True)
graphic_card = Float(dump_only=True)
bios = EnumField(Bios, dump_only=True)
bios_range = EnumField(Bios,
description=m.WorkbenchRate.bios_range.comment,
data_key='biosRange')
appearance_range = EnumField(AppearanceRange,
appearance_range = EnumField(AppearanceRangev2,
required=True,
data_key='appearanceRange',
data_key='appearanceRangev2',
description=m.ManualRate.appearance_range.comment)
functionality_range = EnumField(FunctionalityRange,
functionality_range = EnumField(FunctionalityRangev2,
required=True,
data_key='functionalityRange',
data_key='functionalityRangev2',
description=m.ManualRate.functionality_range.comment)
labelling = Boolean(description=m.ManualRate.labelling.comment)
data_storage_range = EnumField(RatingRange, dump_only=True, data_key='dataStorageRange')
@ -306,10 +321,6 @@ class ResultRate(Rate):
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
class Price(EventWithOneDevice):
__doc__ = m.Price.__doc__
currency = EnumField(Currency, required=True, description=m.Price.currency.comment)
@ -413,58 +424,6 @@ class Snapshot(EventWithOneDevice):
field_names=['elapsed'])
class Test(EventWithOneDevice):
__doc__ = m.Test.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
class TestDataStorage(Test):
__doc__ = m.TestDataStorage.__doc__
length = EnumField(TestDataStorageLength, required=True)
status = SanitizedStr(lower=True, validate=Length(max=STR_SIZE), required=True)
lifetime = TimeDelta(precision=TimeDelta.HOURS)
assessment = Boolean()
reallocated_sector_count = Integer(data_key='reallocatedSectorCount')
power_cycle_count = Integer(data_key='powerCycleCount')
reported_uncorrectable_errors = Integer(data_key='reportedUncorrectableErrors')
command_timeout = Integer(data_key='commandTimeout')
current_pending_sector_count = Integer(data_key='currentPendingSectorCount')
offline_uncorrectable = Integer(data_key='offlineUncorrectable')
remaining_lifetime_percentage = Integer(data_key='remainingLifetimePercentage')
class StressTest(Test):
__doc__ = m.StressTest.__doc__
class Benchmark(EventWithOneDevice):
__doc__ = m.Benchmark.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
class BenchmarkDataStorage(Benchmark):
__doc__ = m.BenchmarkDataStorage.__doc__
read_speed = Float(required=True, data_key='readSpeed')
write_speed = Float(required=True, data_key='writeSpeed')
class BenchmarkWithRate(Benchmark):
__doc__ = m.BenchmarkWithRate.__doc__
rate = Float(required=True)
class BenchmarkProcessor(BenchmarkWithRate):
__doc__ = m.BenchmarkProcessor.__doc__
class BenchmarkProcessorSysbench(BenchmarkProcessor):
__doc__ = m.BenchmarkProcessorSysbench.__doc__
class BenchmarkRamSysbench(BenchmarkWithRate):
__doc__ = m.BenchmarkRamSysbench.__doc__
class ToRepair(EventWithMultipleDevices):
__doc__ = m.ToRepair.__doc__