Adding benchmarks and test [Ratev2]
This commit is contained in:
parent
0f508a1d59
commit
147b86f889
|
@ -106,6 +106,24 @@ class AppearanceRange(Enum):
|
||||||
return self.name
|
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 doesn’t exists'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
APPEARANCE_RANGE = 0.5, -0.3
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class FunctionalityRange(Enum):
|
class FunctionalityRange(Enum):
|
||||||
"""Grades the defects of a device that affect its usage."""
|
"""Grades the defects of a device that affect its usage."""
|
||||||
|
@ -134,6 +152,20 @@ class FunctionalityRangev2(Enum):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
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; can’t stand the minimum duration time'
|
||||||
|
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
|
@unique
|
||||||
class Bios(Enum):
|
class Bios(Enum):
|
||||||
"""How difficult it has been to set the bios to boot from the network."""
|
"""How difficult it has been to set the bios to boot from the network."""
|
||||||
|
|
|
@ -31,7 +31,7 @@ from ereuse_devicehub.resources.device.models import Component, Computer, DataSt
|
||||||
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ErasureStandards, \
|
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ErasureStandards, \
|
||||||
FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \
|
FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \
|
||||||
RatingRange, RatingSoftware, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \
|
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.event.rate.workbench.v2_0 import QualityRate, FunctionalityRate
|
||||||
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
|
@ -553,6 +553,220 @@ class SnapshotRequest(db.Model):
|
||||||
cascade=CASCADE_OWN))
|
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):
|
class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||||
"""The act of grading the appearance, performance, and functionality
|
"""The act of grading the appearance, performance, and functionality
|
||||||
of a device.
|
of a device.
|
||||||
|
@ -651,50 +865,13 @@ class ManualRate(IndividualRate):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class ComputerRate(ManualRate):
|
# TODO is necessary?
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id), primary_key=True)
|
class WorkbenchComputer(ManualRate):
|
||||||
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE))
|
pass
|
||||||
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 ensure for WorkbenchRate version and software are not None when inserting them
|
|
||||||
|
|
||||||
def ratings(self):
|
class WorkbenchMobile(ManualRate):
|
||||||
"""
|
pass
|
||||||
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 AggregateRate(Rate):
|
class AggregateRate(Rate):
|
||||||
|
@ -782,7 +959,7 @@ class AggregateRate(Rate):
|
||||||
return self.workbench.labelling
|
return self.workbench.labelling
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_workbench_rate(cls, rate: QualityRateComputer):
|
def from_workbench_rate(cls, rate: QualityRate):
|
||||||
aggregate = cls()
|
aggregate = cls()
|
||||||
aggregate.rating = rate.rating
|
aggregate.rating = rate.rating
|
||||||
aggregate.software = rate.software
|
aggregate.software = rate.software
|
||||||
|
@ -793,189 +970,88 @@ class AggregateRate(Rate):
|
||||||
return aggregate
|
return aggregate
|
||||||
|
|
||||||
|
|
||||||
class EreusePrice(Price):
|
class QualityRate(Rate):
|
||||||
"""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 = {
|
The act of compute performance (quality) a device
|
||||||
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):
|
|
||||||
"""
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
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 = {
|
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||||
Desktop: 20,
|
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE),
|
||||||
Laptop: 30
|
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.')
|
||||||
|
|
||||||
class Type:
|
""" MOBILE QUALITY RATE """
|
||||||
def __init__(self, percentage: float, price: Decimal) -> None:
|
display = Column(Float(decimal_return_scale=2), check_range('display', *RATE_POSITIVE))
|
||||||
# see https://stackoverflow.com/a/29651462 for the - 0.005
|
display.comment = 'Display rate, screen resolution and size to calculate PPI and convert in score'
|
||||||
self.amount = EreusePrice.to_price(price * Decimal(percentage))
|
battery = Column(Float(decimal_return_scale=2), check_range('battery', *RATE_POSITIVE),
|
||||||
self.percentage = EreusePrice.to_price(price * Decimal(percentage))
|
comment='Battery rate is related with capacity and its health')
|
||||||
self.percentage = round(percentage - 0.005, 2)
|
camera = Column(Float(decimal_return_scale=2), check_range('camera', *RATE_POSITIVE),
|
||||||
|
comment='Camera rate take into account resolution')
|
||||||
|
|
||||||
class Service:
|
""" COMPUTER QUALITY RATE """
|
||||||
REFURBISHER, PLATFORM, RETAILER = 0, 1, 2
|
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
|
||||||
STANDARD, WARRANTY2 = 'STD', 'WR2'
|
comment='Graphic card score in performance, amount of memory and benchmark result')
|
||||||
SCHEMA = {
|
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
|
||||||
Desktop: {
|
comment='Network adapter rate, take it speed limit')
|
||||||
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:
|
bios = Column(Float(decimal_return_scale=2), check_range('bios', *RATE_POSITIVE))
|
||||||
cls = device.__class__ if device.__class__ != Server else Desktop
|
bios_range = Column(DBEnum(Bios))
|
||||||
rate = self.SCHEMA[cls][rating_range]
|
bios_range.comment = Bios.__doc__
|
||||||
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:
|
@property
|
||||||
if rating.rating_range == RatingRange.VERY_LOW:
|
def ram_range(self):
|
||||||
raise ValueError('Cannot compute price for Range.VERY_LOW')
|
return self.workbench.ram_range
|
||||||
# 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
|
@property
|
||||||
def _compute(self):
|
def processor_range(self):
|
||||||
"""
|
return self.workbench.processor_range
|
||||||
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):
|
@property
|
||||||
return self.Service(self.device, self.rating.rating_range, role, self.price)
|
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 ResultRate(Rate):
|
class FunctionalityRate(Rate):
|
||||||
|
"""
|
||||||
|
The act of compute functionality characteristics of a device.
|
||||||
|
Two functionality variables, functionality rate (float) and functionality range (Enum)
|
||||||
|
|
||||||
|
"""
|
||||||
|
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__
|
||||||
|
|
||||||
|
|
||||||
|
class FinalRate(Rate):
|
||||||
"""The act of grading the appearance, quality (performance), and functionality
|
"""The act of grading the appearance, quality (performance), and functionality
|
||||||
of a device.
|
of a device.
|
||||||
|
|
||||||
|
@ -987,7 +1063,7 @@ class ResultRate(Rate):
|
||||||
5. ``Cost of repair``.
|
5. ``Cost of repair``.
|
||||||
|
|
||||||
|
|
||||||
There are types of rating a device:
|
There are different types of rating a device:
|
||||||
|
|
||||||
1. Rate Quality
|
1. Rate Quality
|
||||||
2. Rate Functionality
|
2. Rate Functionality
|
||||||
|
@ -1003,17 +1079,22 @@ class ResultRate(Rate):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
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
|
quality_id.comment = """The Quality Rate used to generate this
|
||||||
aggregation, or None if none used.
|
aggregation, or None if none used.
|
||||||
"""
|
"""
|
||||||
|
quality = relationship(QualityRate, )
|
||||||
|
|
||||||
func_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
functionality_id = Column(UUID(as_uuid=True), ForeignKey(FunctionalityRate.id))
|
||||||
func_id.comment = """The Functionality Rate used to generate this
|
functionality_id.comment = """The Functionality Rate used to generate this
|
||||||
aggregation, or None if none used.
|
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
|
final_id.comment = """The Final Rate used to generate this
|
||||||
aggregation, or None if none used.
|
aggregation, or None if none used.
|
||||||
"""
|
"""
|
||||||
|
@ -1058,6 +1139,8 @@ class ResultRate(Rate):
|
||||||
collection_class=OrderedSet),
|
collection_class=OrderedSet),
|
||||||
primaryjoin=workbench_mobile_id == QualityRateMobile.id)
|
primaryjoin=workbench_mobile_id == QualityRateMobile.id)
|
||||||
|
|
||||||
|
# TODO Add more source that global rate can use it
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
kwargs.setdefault('version', StrictVersion('1.0'))
|
kwargs.setdefault('version', StrictVersion('1.0'))
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -1081,11 +1164,11 @@ class ResultRate(Rate):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def functionality_category(cls, quality: QualityRate):
|
def functionality_category(cls, functionality: FunctionalityRate):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def appearance_category(cls, quality: QualityRate):
|
def appearance_category(cls, appearance: ManualRate):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -1151,103 +1234,6 @@ class ResultRate(Rate):
|
||||||
def labelling(self):
|
def labelling(self):
|
||||||
return self.workbench.labelling
|
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):
|
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||||
"""The act of setting a trading price for the device.
|
"""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)
|
return '{0:0.2f} {1}'.format(self.price, self.currency)
|
||||||
|
|
||||||
|
|
||||||
class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
class EreusePrice(Price):
|
||||||
"""The act of testing the physical condition of a device and its
|
"""The act of setting a price by guessing it using the eReuse.org
|
||||||
components.
|
algorithm.
|
||||||
|
|
||||||
Testing errors and warnings are easily taken in
|
This algorithm states that the price is the use value of the device
|
||||||
:attr:`ereuse_devicehub.resources.device.models.Device.working`.
|
(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
|
class Type:
|
||||||
def __mapper_args__(cls):
|
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.
|
Calculates eReuse.org prices when initializing the
|
||||||
|
instance from the price and other properties.
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
|
||||||
extensions/declarative/api.html
|
|
||||||
#sqlalchemy.ext.declarative.declared_attr>`_
|
|
||||||
"""
|
"""
|
||||||
args = {POLYMORPHIC_ID: cls.t}
|
self.refurbisher = self._service(self.Service.REFURBISHER)
|
||||||
if cls.t == 'Test':
|
self.retailer = self._service(self.Service.RETAILER)
|
||||||
args[POLYMORPHIC_ON] = cls.type
|
self.platform = self._service(self.Service.PLATFORM)
|
||||||
return args
|
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):
|
||||||
class TestDataStorage(Test):
|
return self.Service(self.device, self.rating.rating_range, role, self.price)
|
||||||
"""
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class ToRepair(EventWithMultipleDevices):
|
class ToRepair(EventWithMultipleDevices):
|
||||||
|
|
|
@ -2,7 +2,6 @@ from enum import Enum
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device.models import DataStorage, Processor, RamModule, Device
|
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.models import BenchmarkDataStorage, WorkbenchRate
|
||||||
from ereuse_devicehub.resources.event.rate.rate import BaseRate
|
from ereuse_devicehub.resources.event.rate.rate import BaseRate
|
||||||
|
|
||||||
|
@ -93,7 +92,7 @@ class FunctionalityRate(BaseRate):
|
||||||
LOUDSPEAKER_WEIGHT = 0.15
|
LOUDSPEAKER_WEIGHT = 0.15
|
||||||
MICROPHONE_WEIGHT = 0.15
|
MICROPHONE_WEIGHT = 0.15
|
||||||
|
|
||||||
def compute(self, FunctionalityDevice: FunctionalityRate):
|
def compute(self, FunctionalityRate: FunctionalityRate):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param FunctionalityDevice: List[Boolean]
|
:param FunctionalityDevice: List[Boolean]
|
||||||
|
@ -116,6 +115,7 @@ class FunctionalityRate(BaseRate):
|
||||||
class Appearance(Range):
|
class Appearance(Range):
|
||||||
"""
|
"""
|
||||||
APPEARANCE GRADE [0.5,-0.5]
|
APPEARANCE GRADE [0.5,-0.5]
|
||||||
|
Enum(AppearanceRangev2)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Z = 0.5
|
Z = 0.5
|
||||||
|
@ -127,166 +127,7 @@ class Appearance(Range):
|
||||||
NONE = -0.3
|
NONE = -0.3
|
||||||
|
|
||||||
|
|
||||||
class QualityRate(Rate):
|
class FinalRate(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):
|
|
||||||
"""The act of grading the appearance, quality (performance), and functionality
|
"""The act of grading the appearance, quality (performance), and functionality
|
||||||
of a device.
|
of a device.
|
||||||
|
|
||||||
|
@ -297,37 +138,19 @@ class ResultRate(Rate):
|
||||||
4. ``Market value``.
|
4. ``Market value``.
|
||||||
5. ``Cost of repair``.
|
5. ``Cost of repair``.
|
||||||
|
|
||||||
|
|
||||||
There are types of rating a device:
|
There are types of rating a device:
|
||||||
|
|
||||||
1. Rate Quality
|
1. Rate Quality
|
||||||
2. Rate Functionality
|
2. Rate Functionality
|
||||||
3. Rate Final
|
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)
|
final_rate = None
|
||||||
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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
func_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
# TODO Develop merge rate aspects (categories)
|
||||||
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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
""" MANUAL INPUT """
|
""" MANUAL INPUT """
|
||||||
manual_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
manual_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
||||||
|
@ -408,7 +231,6 @@ class ResultRate(Rate):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DisplayRate(QualityRate):
|
class DisplayRate(QualityRate):
|
||||||
"""
|
"""
|
||||||
Calculate a DisplayRate
|
Calculate a DisplayRate
|
||||||
|
|
|
@ -109,6 +109,77 @@ class StepRandom(Step):
|
||||||
__doc__ = m.StepRandom.__doc__
|
__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):
|
class Rate(EventWithOneDevice):
|
||||||
__doc__ = m.Rate.__doc__
|
__doc__ = m.Rate.__doc__
|
||||||
|
@ -142,7 +213,7 @@ class ManualRate(IndividualRate):
|
||||||
labelling = Boolean(description=m.ManualRate.labelling.comment)
|
labelling = Boolean(description=m.ManualRate.labelling.comment)
|
||||||
|
|
||||||
|
|
||||||
class WorkbenchRate(ManualRate):
|
class WorkbencComputer(ManualRate):
|
||||||
__doc__ = m.WorkbenchRate.__doc__
|
__doc__ = m.WorkbenchRate.__doc__
|
||||||
processor = Float()
|
processor = Float()
|
||||||
ram = Float()
|
ram = Float()
|
||||||
|
@ -158,26 +229,8 @@ class WorkbenchRate(ManualRate):
|
||||||
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
|
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
|
||||||
|
|
||||||
|
|
||||||
"""
|
class WorkbenchMobile(ManualRate):
|
||||||
WORKBENCH RATE COMPUTER
|
pass
|
||||||
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 AggregateRate(Rate):
|
class AggregateRate(Rate):
|
||||||
|
@ -210,6 +263,9 @@ class AggregateRate(Rate):
|
||||||
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
|
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
|
||||||
|
|
||||||
|
|
||||||
|
""" RATE v2 CODE"""
|
||||||
|
|
||||||
|
|
||||||
class QualityRate(Rate):
|
class QualityRate(Rate):
|
||||||
__doc__ = m.QualityRate.__doc__
|
__doc__ = m.QualityRate.__doc__
|
||||||
|
|
||||||
|
@ -217,37 +273,17 @@ class QualityRate(Rate):
|
||||||
processor = Float(dump_only=True, description=m.QualityRate.processor.comment)
|
processor = Float(dump_only=True, description=m.QualityRate.processor.comment)
|
||||||
data_storage = Float(dump_only=True, description=m.QualityRate.data_storage.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)
|
||||||
|
|
||||||
|
bios = EnumField(Bios, dump_only=True)
|
||||||
class QualityRateComputer(Rate):
|
bios_range = EnumField(Bios,
|
||||||
__doc__ = m.QualityRateComputer.__doc__
|
description=m.WorkbenchRate.bios_range.comment,
|
||||||
|
data_key='biosRange')
|
||||||
""" 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)
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionalityRate(Rate):
|
class FunctionalityRate(Rate):
|
||||||
|
@ -255,49 +291,28 @@ class FunctionalityRate(Rate):
|
||||||
|
|
||||||
functionality = EnumField(dump_only=True, description=m.FunctionalityRate.functionality.comment)
|
functionality = EnumField(dump_only=True, description=m.FunctionalityRate.functionality.comment)
|
||||||
functionality_range = EnumField(dump_only=True, description=m.FunctionalityRate.functionality_range.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):
|
class FinalRate(Rate):
|
||||||
__doc__ = m.FinalRate.__doc__
|
__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,
|
workbench_computer = NestedOn(WorkbenchComputer, dump_only=True,
|
||||||
description=m.ResultRate.workbench_computer_id.comment)
|
description=m.ResultRate.workbench_computer_id.comment)
|
||||||
workbench_mobile = NestedOn(WorkbenchMobile, dump_only=True,
|
workbench_mobile = NestedOn(WorkbenchMobile, dump_only=True,
|
||||||
description=m.ResultRate.workbench_mobile_id.comment)
|
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')
|
|
||||||
|
|
||||||
|
appearance_range = EnumField(AppearanceRangev2,
|
||||||
# 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,
|
|
||||||
required=True,
|
required=True,
|
||||||
data_key='appearanceRange',
|
data_key='appearanceRangev2',
|
||||||
description=m.ManualRate.appearance_range.comment)
|
description=m.ManualRate.appearance_range.comment)
|
||||||
functionality_range = EnumField(FunctionalityRange,
|
functionality_range = EnumField(FunctionalityRangev2,
|
||||||
required=True,
|
required=True,
|
||||||
data_key='functionalityRange',
|
data_key='functionalityRangev2',
|
||||||
description=m.ManualRate.functionality_range.comment)
|
description=m.ManualRate.functionality_range.comment)
|
||||||
labelling = Boolean(description=m.ManualRate.labelling.comment)
|
labelling = Boolean(description=m.ManualRate.labelling.comment)
|
||||||
data_storage_range = EnumField(RatingRange, dump_only=True, data_key='dataStorageRange')
|
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')
|
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Price(EventWithOneDevice):
|
class Price(EventWithOneDevice):
|
||||||
__doc__ = m.Price.__doc__
|
__doc__ = m.Price.__doc__
|
||||||
currency = EnumField(Currency, required=True, description=m.Price.currency.comment)
|
currency = EnumField(Currency, required=True, description=m.Price.currency.comment)
|
||||||
|
@ -413,58 +424,6 @@ class Snapshot(EventWithOneDevice):
|
||||||
field_names=['elapsed'])
|
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):
|
class ToRepair(EventWithMultipleDevices):
|
||||||
__doc__ = m.ToRepair.__doc__
|
__doc__ = m.ToRepair.__doc__
|
||||||
|
|
||||||
|
|
Reference in a new issue