first commit refactor score

This commit is contained in:
nad 2019-04-11 18:29:51 +02:00
parent 17705e459e
commit 825c07c5f9
7 changed files with 221 additions and 950 deletions

View file

@ -18,23 +18,6 @@ class SnapshotSoftware(Enum):
return self.name return self.name
@unique
class RatingSoftware(Enum):
"""The software used to compute the Score."""
ECost = 'ECost'
"""
The eReuse.org rate algorithm that focuses maximizing refurbishment
of devices in general, specially penalizing very low and very high
devices in order to stimulate medium-range devices.
This model is cost-oriented.
"""
EMarket = 'EMarket'
def __str__(self):
return self.name
RATE_POSITIVE = 0, 10 RATE_POSITIVE = 0, 10
RATE_NEGATIVE = -3, 5 RATE_NEGATIVE = -3, 5
@ -126,7 +109,7 @@ class FunctionalityRange(Enum):
return self.name return self.name
FUNCTIONALITY_RANGE = -0.25, 0.5 FUNCTIONALITY_RANGE = -0.3, 0.4
@unique @unique
@ -142,8 +125,9 @@ class BatteryHealthRange(Enum):
def __str__(self): def __str__(self):
return self.name return self.name
@unique @unique
class Bios(Enum): class BiosAccessRange(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."""
A = 'A. If by pressing a key you could access a boot menu with the network boot' A = 'A. If by pressing a key you could access a boot menu with the network boot'
B = 'B. You had to get into the BIOS, and in less than 5 steps you could set the network boot' B = 'B. You had to get into the BIOS, and in less than 5 steps you could set the network boot'
@ -155,8 +139,6 @@ class Bios(Enum):
return self.name return self.name
# TODO add all grade tables (chassis defects, camera defects, buttons test, connectivity, ..)
@unique @unique
class Orientation(Enum): class Orientation(Enum):
Vertical = 'vertical' Vertical = 'vertical'

View file

@ -1,7 +1,6 @@
from collections import Iterable from collections import Iterable
from datetime import datetime, timedelta from datetime import datetime, timedelta
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_UP from decimal import Decimal, ROUND_HALF_EVEN, ROUND_UP
from distutils.version import StrictVersion
from typing import Optional, Set, Union from typing import Optional, Set, Union
from uuid import uuid4 from uuid import uuid4
@ -28,10 +27,10 @@ from ereuse_devicehub.db import db
from ereuse_devicehub.resources.agent.models import Agent from ereuse_devicehub.resources.agent.models import Agent
from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Desktop, \ from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Desktop, \
Device, Laptop, Server Device, Laptop, Server
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ErasureStandards, \ from ereuse_devicehub.resources.enums import BiosAccessRange, ErasureStandards, \
FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \ PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \
RatingRange, RatingSoftware, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \ RatingRange, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \
TestDataStorageLength, FUNCTIONALITY_RANGE, FunctionalityRange, AppearanceRange, BatteryHealthRange TestDataStorageLength, FunctionalityRange, AppearanceRange, BatteryHealthRange
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
@ -708,7 +707,7 @@ class TestAudio(Test):
""" """
Test to check all this aspects related with audio functions, Manual Tests?? Test to check all this aspects related with audio functions, Manual Tests??
""" """
loudspeaker = Column(BDEnum(LoudspeakerRange)) loudspeaker = Column(Boolean)
loudspeaker.comment = 'Test to determine if the speaker is working properly and what sound quality it has.' loudspeaker.comment = 'Test to determine if the speaker is working properly and what sound quality it has.'
microphone = Column(Boolean) microphone = Column(Boolean)
microphone.comment = 'This evaluate if microphone works correctly' microphone.comment = 'This evaluate if microphone works correctly'
@ -730,6 +729,7 @@ class TestConnectivity(Test):
locked = Column(Boolean) locked = Column(Boolean)
locked.comment = 'Test to check if devices is locked' locked.comment = 'Test to check if devices is locked'
class TestBattery(Test): class TestBattery(Test):
""" """
Test battery health, status and length of charge. Minimum X minutes discharging the device Test battery health, status and length of charge. Minimum X minutes discharging the device
@ -781,7 +781,7 @@ class TestBiosDifficulty:
Test to determinate a grade to reflect some possibles difficult to access or modify setting in the BIOS, like password protection.. Test to determinate a grade to reflect some possibles difficult to access or modify setting in the BIOS, like password protection..
""" """
bios_access_range = Column(BDEnum(BiosAccessRange)) bios_access_range = Column(BDEnum(BiosAccessRange))
bios_access_range.comment = 'Range of difficult to acces BIOS' bios_access_range.comment = 'Range of difficult to access BIOS'
class TestVisual(ManualRate): class TestVisual(ManualRate):
@ -795,6 +795,12 @@ class TestVisual(ManualRate):
functionality_range = Column(DBEnum(FunctionalityRange)) functionality_range = Column(DBEnum(FunctionalityRange))
functionality_range.comment = FunctionalityRange.__doc__ functionality_range.comment = FunctionalityRange.__doc__
def __str__(self) -> str:
return super().__str__() + '. Appearance {} and functionality {}'.format(
self.appearance_range,
self.functionality_range
)
class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice): class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of computing a rate based on different categories: """The act of computing a rate based on different categories:
@ -810,10 +816,11 @@ class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
rating = Column(Float(decimal_return_scale=2), check_range('rating', *RATE_POSITIVE)) rating = Column(Float(decimal_return_scale=2), check_range('rating', *RATE_POSITIVE))
rating.comment = """The rating for the content.""" rating.comment = """The rating for the content."""
software = Column(DBEnum(RatingSoftware))
software.comment = """The algorithm used to produce this rating."""
version = Column(StrictVersionType) version = Column(StrictVersionType)
version.comment = """The version of the software.""" version.comment = """The version of the software."""
appearance = Column(Float(decimal_return_scale=2), check_range('appearance', *RATE_NEGATIVE))
functionality = Column(Float(decimal_return_scale=2),
check_range('functionality', *RATE_NEGATIVE))
@property @property
def rating_range(self) -> RatingRange: def rating_range(self) -> RatingRange:
@ -835,7 +842,15 @@ class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
return args return args
def __str__(self) -> str: def __str__(self) -> str:
return '{} ({} v.{})'.format(self.rating_range, self.software, self.version) return '{} (v.{})'.format(self.rating_range, self.version)
@classmethod
def compute(cls, device) -> 'RateComputer':
"""
The act of compute general computer rate
"""
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import rate_algorithm
return rate_algorithm.compute(device)
class RateComputer(Rate): class RateComputer(Rate):
@ -852,332 +867,24 @@ class RateComputer(Rate):
comment='RAM memory rate.') comment='RAM memory rate.')
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE), data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
comment='Data storage rate, like HHD, SSD.') 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)) def __init__(self, **kwargs) -> None:
bios_range = Column(DBEnum(Bios)) super().__init__(**kwargs)
bios_range.comment = Bios.__doc__
appearance = Column(Float(decimal_return_scale=2), check_range('appearance', *RATE_NEGATIVE)) @property
functionality = Column(Float(decimal_return_scale=2), def data_storage_range(self):
check_range('functionality', *RATE_NEGATIVE)) if self.data_storage:
return RatingRange.from_score(self.data_storage)
def compute_rate(self): @property
""" def ram_range(self):
The act of compute general computer rate if self.ram:
""" return RatingRange.from_score(self.ram)
pass
def compute_features(self, device): @property
""" def processor_range(self):
The act of compute rate about features (quality) aspects of a device if self.processor:
""" return RatingRange.from_score(self.processor)
# norm = (x - xMin) / (xMax xMin)
# harmonic_mean = sum(x.weights)/sum(x.char_weight/x.char_norm)
CHARACTERISTIC_WEIGHTS = [
CPU_CHAR_WEIGHTS = 0.2, 0.4, 0.4 # cores, speed, benchmark
RAM_CHAR_WEIGHT = 0.3, 0.3, 0.4 # size, benchmark
DATA_STORAGE_CHAR_WEIGHT = 0.2, 0.4, 0.4 # size, speed, benchmark
GRAPHIC_CARD_CHAR_WEIGHT = 0.5, 0.5 # size, benchmark
...
]
self.processor = harmonic_mean(device.cpu.cores, device.cpu.speed, device.cpu.benchmarkCPUSysbench.rate,
PROCESSOR_CHAR_WEIGHTS)
self.ram = harmonic_mean(device.ram.size, device.ram.speed, device.ram.benchmarkRAMSysbench.rate,
RAM_CHAR_WEIGHT)
self.data_storage = harmonic_mean(device.data_storage.size, device.data_storage.speed,
device.data_storage.benchmarkDataStorage.rate, DATA_STORAGE_CHAR_WEIGHT)
self.graphic_card = harmonic_mean(device.graphic_card.size, device.graphic_card.benchmarkGraphicCard.rate,
GRAPHIC_CARD_CHAR_WEIGHT)
# self.network_adapter = device.network_adapter.rate
...
COMPONENTS_WEIGHTS..
return harmonic_mean(self.processor, self.ram, self.data_storage, self.graphic_card, ..., COMPONENTS_WEIGHTS)
def compute_functionality(self, device):
"""
The act of compute functionality characteristics of a device.
Two functionality variables, functionality rate (float) and functionality range (Enum)
"""
DATA_STORAGE_WEIGHT = 0.1
STRESS_WEIGHT = 0.2
BIOS_WEIGHT = 0.2
...
test_data_storage = device.last_event_of(TestDataStorage)
test_data_storage = int(test_data_storage) * DATA_STORAGE_WEIGHT
test_stress = device.last_event_of(StressTest)
test_stress = int(test_stress) * STRESS_WEIGHT
test_bios_power_on = device.last_event_of(TestBios).bios_power_on # type: bool
test_bios_power_on = int(test_bios_power_on) * BIOS_WEIGHT
...
functionality_tests = harmonic_mean(test_data_storage, test_stress, test_bios_power_on, ...)
functionality_range = device.last_event_of(TestVisual).functionality_range
self.functionality = functionality_range + functionality_tests
def compute_appearance(self, device):
"""
The act of compute appearance of a device.
"""
test_visual_appearance = device.last_event_of(TestVisual).appearance_range
self.appearance = test_visual_appearance
class RateDesktop(RateComputer):
"""
Rate class for device type: Desktop
"""
pass
class RateLaptop(RateComputer):
"""
Rate class for device type: Laptop
"""
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')
def compute_features(self, device):
"""
The act of compute rate about features (quality) aspects of a device
"""
# norm = (x - xMin) / (xMax xMin)
# harmonic_mean = sum(x.weights)/sum(x.char_weight/x.char_norm)
CHARACTERISTIC_WEIGHTS = [
PROCESSOR_CHAR_WEIGHTS = 0.2, 0.4, 0.4 # cores, speed, benchmark
RAM_CHAR_WEIGHT = 0.3, 0.3, 0.4 # size, benchmark
DATA_STORAGE_CHAR_WEIGHT = 0.2, 0.4, 0.4 # size, speed, benchmark
GRAPHIC_CARD_CHAR_WEIGHT = 0.5, 0.5 # size, benchmark
DISPLAY_CHAR_WEIGHT = 0.4, 0.6 # size, resolution
...
]
self.processor = harmonic_mean(device.cpu.cores, device.cpu.speed, device.cpu.benchmarkCPUSysbench.rate,
PROCESSOR_CHAR_WEIGHTS)
self.ram = harmonic_mean(device.ram.size, device.ram.speed, device.ram.benchmarkRAMSysbench.rate,
RAM_CHAR_WEIGHT)
self.data_storage = harmonic_mean(device.data_storage.size, device.data_storage.speed,
device.data_storage.benchmarkDataStorage.rate, DATA_STORAGE_CHAR_WEIGHT)
self.graphic_card = harmonic_mean(device.graphic_card.size, device.graphic_card.benchmarkGraphicCard.rate,
GRAPHIC_CARD_CHAR_WEIGHT)
self.display = harmonic_mean(device.display.size, device.display.resolution, DISPLAY_CHAR_WEIGHT)
...
COMPONENTS_WEIGHTS = [
PROCESSOR_WEIGHT: 0.1,
RAM_WEIGHT: 0.25,
DATA_STORAGE_WEIGHT: 0.05,
GRAPHIC_CARD_WEIGHT: 0.1,
DISPLAY_WEIGHT: 0.3,
BATTERY_WEIGHT: 0.25,
CAMERA_WEIGHT: 0.1,
... ...
]
return harmonic_mean(self.processor, self.ram, self.data_storage, self.graphic_card, self.display,
self.battery, self.camera, ..., COMPONENTS_WEIGHTS)
def compute_functionality(self, device):
"""
The act of compute functionality characteristics of a device.
Two functionality variables, functionality rate (float) and functionality range (Enum)
"""
DATA_STORAGE_WEIGHT = 0.1
STRESS_WEIGHT = 0.2
CONNECTIVITY_WEIGHT = 0.2
BIOS_WEIGHT = 0.25
BATTERY_WEIGHT = 0.05
AUDIO_WEIGHT = 0.05
CAMERA_WEIGHT = 0.05
...
test_data_storage = device.last_event_of(TestDataStorage)
test_data_storage = int(test_data_storage) * DATA_STORAGE_WEIGHT
test_stress = device.last_event_of(StressTest)
test_stress = int(test_stress) * STRESS_WEIGHT
test_connectivity = device.last_event_of(TestConnectivity)
test_connectivity = int(test_connectivity) * CONNECTIVITY_WEIGHT
test_bios_power_on = device.last_event_of(TestBios).bios_power_on # type: bool
test_bios_power_on = int(test_bios_power_on) * BIOS_WEIGHT
test_battery = device.last_event_of(TestBattery)
test_battery = int(test_battery) * BATTERY_WEIGHT
test_audio = int(device.last_event_of(TestAudio)) * AUDIO_WEIGHT
test_camera = device.last_event_of(TestCamera)
test_camera = int(test_camera) * CAMERA_WEIGHT
...
functionality_tests = harmonic_mean(test_data_storage, test_stress, test_connectivity, test_bios_power_on,
test_battery, test_audio, test_camera, ...)
functionality_range = device.last_event_of(TestVisual).functionality_range
self.functionality = functionality_range + functionality_tests
class RateServer(RateComputer):
"""
Rate class for device type: Desktop
"""
pass
class RateMobile(Rate):
"""
Main class to group by device type: Mobile
Computer is broadly extended by ``Smartphone``, ``Tablet``, and
``Cellphone``.
"""
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.')
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')
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')
def compute_rate(self):
"""
The act of compute general computer rate
"""
pass
def compute_features(self, device):
"""
The act of compute rate about features (quality) aspects of a device
"""
# norm = (x - xMin) / (xMax xMin)
# harmonic_mean = sum(x.weights)/sum(x.char_weight/x.char_norm)
CHARACTERISTIC_WEIGHTS = [
PROCESSOR_CHAR_WEIGHTS = 0.2, 0.4, 0.4 # cores, speed, benchmark
RAM_CHAR_WEIGHT = 0.3, 0.3, 0.4 # size, benchmark
DATA_STORAGE_CHAR_WEIGHT = 0.2, 0.4, 0.4 # size, speed, benchmark
GRAPHIC_CARD_CHAR_WEIGHT = 0.5, 0.5 # size, benchmark
DISPLAY_CHAR_WEIGHT = 0.4, 0.6 # size, resolution
...
]
self.processor = harmonic_mean(device.cpu.cores, device.cpu.speed, device.cpu.benchmarkCPUSysbench.rate,
PROCESSOR_CHAR_WEIGHTS)
self.ram = harmonic_mean(device.ram.size, device.ram.speed, device.ram.benchmarkRAMSysbench.rate,
RAM_CHAR_WEIGHT)
self.data_storage = harmonic_mean(device.data_storage.size, device.data_storage.speed,
device.data_storage.benchmarkDataStorage.rate, DATA_STORAGE_CHAR_WEIGHT)
self.graphic_card = harmonic_mean(device.graphic_card.size, device.graphic_card.benchmarkGraphicCard.rate,
GRAPHIC_CARD_CHAR_WEIGHT)
self.display = harmonic_mean(device.display.size, device.display.resolution, DISPLAY_CHAR_WEIGHT)
...
COMPONENTS_WEIGHTS = [
PROCESSOR_WEIGHT: 0.1,
RAM_WEIGHT: 0.25,
DATA_STORAGE_WEIGHT: 0.05,
GRAPHIC_CARD_WEIGHT: 0.1,
DISPLAY_WEIGHT: 0.3,
BATTERY_WEIGHT: 0.25,
CAMERA_WEIGHT: 0.1,
...
]
return harmonic_mean(self.processor, self.ram, self.data_storage, self.graphic_card, self.display,
self.battery, self.camera, ..., COMPONENTS_WEIGHTS)
def compute_functionality(self, device):
"""
The act of compute functionality characteristics of a device.
Two functionality variables, functionality rate (float) and functionality range (Enum)
"""
DATA_STORAGE_WEIGHT = 0.1
STRESS_WEIGHT = 0.2
CONNECTIVITY_WEIGHT = 0.2
BIOS_WEIGHT = 0.25
BATTERY_WEIGHT = 0.05
AUDIO_WEIGHT = 0.05
CAMERA_WEIGHT = 0.05
...
test_data_storage = device.last_event_of(TestDataStorage)
test_data_storage = int(test_data_storage) * DATA_STORAGE_WEIGHT
test_stress = device.last_event_of(StressTest)
test_stress = int(test_stress) * STRESS_WEIGHT
test_connectivity = device.last_event_of(TestConnectivity)
test_connectivity = int(test_connectivity) * CONNECTIVITY_WEIGHT
test_bios_power_on = device.last_event_of(TestBios).bios_power_on # type: bool
test_bios_power_on = int(test_bios_power_on) * BIOS_WEIGHT
test_battery = device.last_event_of(TestBattery)
test_battery = int(test_battery) * BATTERY_WEIGHT
test_audio = int(device.last_event_of(TestAudio)) * AUDIO_WEIGHT
test_camera = device.last_event_of(TestCamera)
test_camera = int(test_camera) * CAMERA_WEIGHT
...
functionality_tests = harmonic_mean(test_data_storage, test_stress, test_connectivity, test_bios_power_on,
test_battery, test_audio, test_camera, ...)
functionality_range = device.last_event_of(TestVisual).functionality_range
self.functionality = functionality_range + functionality_tests
def compute_appearance(self, device):
"""
The act of compute appearance of a device.
"""
test_visual_appearance = device.last_event_of(TestVisual).appearance_range
self.appearance = test_visual_appearance
class RateMonitor(Rate):
"""
Main class to group by device type: Monitor
Computer is broadly extended by ``ComputerMonitor``, ``TelevisionSet``, and
`` Projector``.
Important: Aspect ratio is an important variable in screen features
"""
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
# TODO determinate which variables must take into account to compute monitor score
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice): class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
@ -1205,15 +912,15 @@ class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
""" """
version = Column(StrictVersionType) version = Column(StrictVersionType)
version.comment = """The version of the software, or None.""" version.comment = """The version of the software, or None."""
rating_id = Column(UUID(as_uuid=True), ForeignKey(AggregateRate.id)) rating_id = Column(UUID(as_uuid=True), ForeignKey(RateComputer.id))
rating_id.comment = """The AggregateRate used to auto-compute rating_id.comment = """The AggregateRate used to auto-compute
this price, if it has not been set manually.""" this price, if it has not been set manually."""
rating = relationship(FinalRate, rating = relationship(Rate,
backref=backref('price', backref=backref('price',
lazy=True, lazy=True,
cascade=CASCADE_OWN, cascade=CASCADE_OWN,
uselist=False), uselist=False),
primaryjoin=AggregateRate.id == rating_id) primaryjoin=RateComputer.id == rating_id)
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args, **kwargs) -> None:
if 'price' in kwargs: if 'price' in kwargs:
@ -1307,7 +1014,7 @@ class EreusePrice(Price):
if self.WARRANTY2 in rate: if self.WARRANTY2 in rate:
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price) self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
def __init__(self, rating: AggregateRate, **kwargs) -> None: def __init__(self, rating: RateComputer, **kwargs) -> None:
if rating.rating_range == RatingRange.VERY_LOW: if rating.rating_range == RatingRange.VERY_LOW:
raise ValueError('Cannot compute price for Range.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 # We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts

View file

@ -123,6 +123,37 @@ class StepRandom(Step):
pass pass
class EraseBasic(EventWithOneDevice):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.start_time = ... # type: datetime
self.end_time = ... # type: datetime
self.steps = ... # type: List[Step]
self.zeros = ... # type: bool
self.success = ... # type: bool
@property
def standards(self) -> Set[ErasureStandards]:
pass
@property
def certificate(self) -> urlutils.URL:
pass
class EraseSectors(EraseBasic):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
class ErasePhysical(EraseBasic):
method = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.method = ... # type: PhysicalErasureMethod
class Snapshot(EventWithOneDevice): class Snapshot(EventWithOneDevice):
def __init__(self, **kwargs) -> None: def __init__(self, **kwargs) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)
@ -154,11 +185,124 @@ class SnapshotRequest(Model):
self.snapshot = ... # type: Snapshot self.snapshot = ... # type: Snapshot
class Benchmark(EventWithOneDevice):
pass
class BenchmarkDataStorage(Benchmark):
read_speed = ... # type: Column
write_speed = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.read_speed = ... # type: float
self.write_speed = ... # type: float
class BenchmarkWithRate(Benchmark):
rate = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.rate = ... # type: int
class BenchmarkProcessor(BenchmarkWithRate):
pass
class BenchmarkProcessorSysbench(BenchmarkProcessor):
pass
class BenchmarkRamSysbench(BenchmarkWithRate):
pass
class BenchmarkGraphicCard(BenchmarkWithRate):
pass
class Test(EventWithOneDevice):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.elapsed = ... # type: timedelta
self.success = ... # type: bool
class TestDataStorage(Test):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.id = ... # type: UUID
self.length = ... # type: TestDataStorageLength
self.status = ... # type: str
self.lifetime = ... # type: timedelta
self.first_error = ... # type: int
self.passed_lifetime = ... # type: timedelta
self.assessment = ... # type: int
self.reallocated_sector_count = ... # type: int
self.power_cycle_count = ... # type: int
self.reported_uncorrectable_errors = ... # type: int
self.command_timeout = ... # type: int
self.current_pending_sector_count = ... # type: int
self.offline_uncorrectable = ... # type: int
self.remaining_lifetime_percentage = ... # type: int
class StressTest(Test):
pass
class TestAudio(Test):
"""
Test to check all this aspects related with audio functions, Manual Tests??
"""
loudspeaker = ... # type: Column
microphone = ... # type: Column
class TestConnectivity(Test):
cellular_network = ... # type: Column
wifi = ... # type: Column
bluetooth = ... # type: Column
usb_port = ... # type: Column
locked = ... # type: Column
class TestBattery(Test):
battery_stat = ... # type: Column
battery_health = ... # type: Column
class TestCamera(Test):
camera = ... # type: Column
class TestKeyboard(Test):
keyboard = ... # type: Column
class TestTrackpad(Test):
trackpad = ... # type: Column
class TestBios(Test):
bios_power_on = ... # type: Column
class TestBiosDifficulty:
bios_access_range = ... # type: BiosAccessRange
class TestVisual(ManualRate):
appearance_range = ... # type: AppearanceRange
functionality_range = ... # type: FunctionalityRange
class Rate(EventWithOneDevice): class Rate(EventWithOneDevice):
rating = ... # type: Column rating = ... # type: Column
appearance = ... # type: Column appearance = ... # type: Column
functionality = ... # type: Column functionality = ... # type: Column
software = ... # type: Column
version = ... # type: Column version = ... # type: Column
def __init__(self, **kwargs) -> None: def __init__(self, **kwargs) -> None:
@ -171,118 +315,11 @@ class Rate(EventWithOneDevice):
self.rating_range = ... # type: str self.rating_range = ... # type: str
class IndividualRate(Rate): class RateComputer(Rate):
def __init__(self, **kwargs) -> None: id = ...
super().__init__(**kwargs) processor = ...
self.aggregated_ratings = ... # type: Set[AggregateRate] ram = ...
data_storage = ...
class AggregateRate(Rate):
manual_id = ... # type: Column
manual = ... # type: relationship
workbench = ... # type: relationship
workbench_id = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.manual_id = ... # type: UUID
self.manual = ... # type: ManualRate
self.workbench = ... # type: WorkbenchRate
self.workbench_id = ... # type: UUID
self.price = ... # type: Price
@property
def processor(self):
return self.workbench.processor
@property
def ram(self):
return self.workbench.ram
@property
def data_storage(self):
return self.workbench.data_storage
@property
def graphic_card(self):
return self.workbench.graphic_card
@property
def bios(self):
return self.workbench.bios
@property
def functionality_range(self):
return self.workbench.functionality_range
@property
def appearance_range(self):
return self.workbench.appearance_range
@property
def bios_range(self):
return self.workbench.bios_range
@property
def labelling(self):
return self.workbench.labelling
@classmethod
def from_workbench_rate(cls, rate: WorkbenchRate) -> AggregateRate:
pass
class ManualRate(IndividualRate):
labelling = ... # type: Column
appearance_range = ... # type: Column
functionality_range = ... # type: Column
aggregate_rate_manual = ... #type: relationship
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.labelling = ... # type: bool
self.appearance_range = ... # type: AppearanceRange
self.functionality_range = ... # type: FunctionalityRange
self.aggregate_rate_manual = ... #type: AggregateRate
def ratings(self) -> Set[Rate]:
pass
class WorkbenchRate(ManualRate):
processor = ... # type: Column
ram = ... # type: Column
data_storage = ... # type: Column
graphic_card = ... # type: Column
bios_range = ... # type: Column
bios = ... # type: Column
aggregate_rate_workbench = ... #type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.processor = ... # type: float
self.ram = ... # type: float
self.data_storage = ... # type: float
self.graphic_card = ... # type: float
self.bios_range = ... # type: Bios
self.bios = ... # type: float
self.aggregate_rate_workbench = ... #type: AggregateRate
@property
def data_storage_range(self):
pass
@property
def ram_range(self):
pass
@property
def processor_range(self):
pass
@property
def graphic_card_range(self):
pass
class Price(EventWithOneDevice): class Price(EventWithOneDevice):
@ -331,101 +368,6 @@ class EreusePrice(Price):
self.warranty2 = ... # type: float self.warranty2 = ... # type: float
class Test(EventWithOneDevice):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.elapsed = ... # type: timedelta
self.success = ... # type: bool
class TestDataStorage(Test):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.id = ... # type: UUID
self.length = ... # type: TestDataStorageLength
self.status = ... # type: str
self.lifetime = ... # type: timedelta
self.first_error = ... # type: int
self.passed_lifetime = ... # type: timedelta
self.assessment = ... # type: int
self.reallocated_sector_count = ... # type: int
self.power_cycle_count = ... # type: int
self.reported_uncorrectable_errors = ... # type: int
self.command_timeout = ... # type: int
self.current_pending_sector_count = ... # type: int
self.offline_uncorrectable = ... # type: int
self.remaining_lifetime_percentage = ... # type: int
class StressTest(Test):
pass
class EraseBasic(EventWithOneDevice):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.start_time = ... # type: datetime
self.end_time = ... # type: datetime
self.steps = ... # type: List[Step]
self.zeros = ... # type: bool
self.success = ... # type: bool
@property
def standards(self) -> Set[ErasureStandards]:
pass
@property
def certificate(self) -> urlutils.URL:
pass
class EraseSectors(EraseBasic):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
class ErasePhysical(EraseBasic):
method = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.method = ... # type: PhysicalErasureMethod
class Benchmark(EventWithOneDevice):
pass
class BenchmarkDataStorage(Benchmark):
read_speed = ... # type: Column
write_speed = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.read_speed = ... # type: float
self.write_speed = ... # type: float
class BenchmarkWithRate(Benchmark):
rate = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.rate = ... # type: int
class BenchmarkProcessor(BenchmarkWithRate):
pass
class BenchmarkProcessorSysbench(BenchmarkProcessor):
pass
class BenchmarkRamSysbench(BenchmarkWithRate):
pass
class ToRepair(EventWithMultipleDevices): class ToRepair(EventWithMultipleDevices):
pass pass

View file

@ -1,25 +1,15 @@
from contextlib import suppress
from distutils.version import StrictVersion
from typing import Set, Union
from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.device.models import Device
from ereuse_devicehub.resources.enums import RatingSoftware from ereuse_devicehub.resources.event.models import RateComputer
from ereuse_devicehub.resources.event.models import AggregateRate, EreusePrice, Rate, \
WorkbenchRate
from ereuse_devicehub.resources.event.rate.workbench import v1_0 from ereuse_devicehub.resources.event.rate.workbench import v1_0
RATE_TYPES = { RATE_TYPES = {
WorkbenchRate: { RateComputer: {
RatingSoftware.ECost: {
'1.0': v1_0.Rate() '1.0': v1_0.Rate()
},
RatingSoftware.EMarket: {
}
} }
} }
def rate(device: Device, rate: Rate): def rate(device: Device, version):
""" """
Rates the passed-in ``rate`` using values from the rate itself Rates the passed-in ``rate`` using values from the rate itself
and the ``device``. and the ``device``.
@ -29,50 +19,7 @@ def rate(device: Device, rate: Rate):
:param device: The device to use as a model. :param device: The device to use as a model.
:param rate: A half-filled rate. :param rate: A half-filled rate.
""" """
cls = rate.__class__
assert cls in RATE_TYPES, 'Rate type {} not supported.'.format(cls) assert cls in RATE_TYPES, 'Rate type {} not supported.'.format(cls)
assert rate.software in RATE_TYPES[cls], 'Rate soft {} not supported.'.format(rate.software) assert str(rate.version) in RATE_TYPES[cls], \
assert str(rate.version) in RATE_TYPES[cls][rate.software], \
'Rate version {} not supported.'.format(rate.version) 'Rate version {} not supported.'.format(rate.version)
RATE_TYPES[cls][rate.software][str(rate.version)].compute(device, rate) RATE_TYPES[cls][str(rate.version)].compute(device)
def main(rating_model: WorkbenchRate,
software: RatingSoftware,
version: StrictVersion) -> Set[Union[WorkbenchRate, AggregateRate, EreusePrice]]:
"""
Generates all the rates (per software and version) for a given
half-filled rate acting as a model, and finally it generates
an ``AggregateRating`` with the rate that matches the
``software`` and ``version``.
This method mutates ``rating_model`` by fulfilling it and
``rating_model.device`` by adding the new rates.
:return: A set of rates with the ``rate`` value computed, where
the first rate is the ``rating_model``.
"""
assert rating_model.device
events = set()
for soft, value in RATE_TYPES[rating_model.__class__].items():
for vers, func in value.items():
if not rating_model.rating: # Fill the rating before creating another rate
rating = rating_model
else: # original rating was filled already; use a new one
rating = WorkbenchRate(
labelling=rating_model.labelling,
appearance_range=rating_model.appearance_range,
functionality_range=rating_model.functionality_range,
device=rating_model.device,
)
rating.software = soft
rating.version = vers
rate(rating_model.device, rating)
events.add(rating)
if soft == software and vers == version:
aggregation = AggregateRate.from_workbench_rate(rating)
events.add(aggregation)
with suppress(ValueError):
# We will have exception if range == VERY_LOW
events.add(EreusePrice(aggregation))
return events

View file

@ -1,8 +1,8 @@
import math
from typing import Iterable from typing import Iterable
import math
from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.device.models import Device
from ereuse_devicehub.resources.event.models import WorkbenchRate
class BaseRate: class BaseRate:
@ -20,7 +20,7 @@ class BaseRate:
"""Ram has 30% of weight over total score, used in harmonic mean""" """Ram has 30% of weight over total score, used in harmonic mean"""
RAM_WEIGHT = 0.3 RAM_WEIGHT = 0.3
def compute(self, device: Device, rate: WorkbenchRate): def compute(self, device: Device):
raise NotImplementedError() raise NotImplementedError()
@staticmethod @staticmethod

View file

@ -5,13 +5,13 @@ from typing import Iterable
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Desktop, Laptop, \ from ereuse_devicehub.resources.device.models import Computer, DataStorage, Desktop, Laptop, \
Processor, RamModule, Server Processor, RamModule, Server
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \ from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
WorkbenchRate RateComputer, TestVisual
# todo if no return assign then rate_c = 1 is assigned # todo if no return assign then rate_c = 1 is assigned
# todo fix corner cases, like components characteristics == None # todo fix corner cases, like components characteristics == None
from ereuse_devicehub.resources.event.rate.rate import BaseRate from ereuse_devicehub.resources.event.rate.rate import BaseRate
class Rate(BaseRate): class RateAlgorithm(BaseRate):
""" """
Rate all components in Computer Rate all components in Computer
""" """
@ -46,15 +46,16 @@ class Rate(BaseRate):
DataStorage.t: ('data_storage', DataStorageRate()) DataStorage.t: ('data_storage', DataStorageRate())
} }
def compute(self, device: Computer, rate: WorkbenchRate): def compute(self, device: Computer):
""" """
Compute 'Workbench'Rate computer is a rate (score) ranging from 0 to 4.7 Compute RateComputer is a rate (score) ranging from 0 to 4.7
that represents estimating value of use of desktop and laptop computer components. that represents estimating value of use of desktop and laptop computer components.
This mutates "rate". This mutates "rate".
""" """
assert isinstance(device, (Desktop, Laptop, Server)) assert isinstance(device, (Desktop, Laptop, Server))
assert isinstance(rate, WorkbenchRate)
rate = RateComputer()
rate.processor = rate.data_storage = rate.ram = 1 # Init rate.processor = rate.data_storage = rate.ram = 1 # Init
@ -71,8 +72,8 @@ class Rate(BaseRate):
setattr(rate, field, result) setattr(rate, field, result)
rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram) rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram)
rate.appearance = self.Appearance.from_devicehub(rate.appearance_range).value rate.appearance = self.Appearance.from_devicehub(TestVisual.appearance_range).value
rate.functionality = self.Functionality.from_devicehub(rate.functionality_range).value rate.functionality = self.Functionality.from_devicehub(TestVisual.functionality_range).value
rate.rating = round(max(rate_components + rate.functionality + rate.appearance, 0), 2) rate.rating = round(max(rate_components + rate.functionality + rate.appearance, 0), 2)
rate.appearance = round(rate.appearance, 2) rate.appearance = round(rate.appearance, 2)
@ -80,6 +81,8 @@ class Rate(BaseRate):
rate.processor = round(rate.processor, 2) rate.processor = round(rate.processor, 2)
rate.ram = round(rate.ram, 2) rate.ram = round(rate.ram, 2)
rate.data_storage = round(rate.data_storage, 2) rate.data_storage = round(rate.data_storage, 2)
device.events_one.add(rate)
return rate
class ProcessorRate(BaseRate): class ProcessorRate(BaseRate):
@ -95,7 +98,7 @@ class ProcessorRate(BaseRate):
# Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz, score = 23406.92 but results inan score of 17503. # Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz, score = 23406.92 but results inan score of 17503.
DEFAULT_SCORE = 4000 DEFAULT_SCORE = 4000
def compute(self, processor: Processor, rate: WorkbenchRate): def compute(self, processor: Processor, rate: RateComputer):
""" Compute processor rate """ Compute processor rate
Obs: cores and speed are possible NULL value Obs: cores and speed are possible NULL value
:return: result is a rate (score) of Processor characteristics :return: result is a rate (score) of Processor characteristics
@ -139,7 +142,7 @@ class RamRate(BaseRate):
# ram.size.weight; ram.speed.weight; # ram.size.weight; ram.speed.weight;
RAM_WEIGHTS = 0.7, 0.3 RAM_WEIGHTS = 0.7, 0.3
def compute(self, ram_devices: Iterable[RamModule], rate: WorkbenchRate): def compute(self, ram_devices: Iterable[RamModule], rate: RateComputer):
""" """
Obs: RamModule.speed is possible NULL value & size != NULL or NOT?? Obs: RamModule.speed is possible NULL value & size != NULL or NOT??
:return: result is a rate (score) of all RamModule components :return: result is a rate (score) of all RamModule components
@ -196,7 +199,7 @@ class DataStorageRate(BaseRate):
# drive.size.weight; drive.readingSpeed.weight; drive.writingSpeed.weight; # drive.size.weight; drive.readingSpeed.weight; drive.writingSpeed.weight;
DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25 DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25
def compute(self, data_storage_devices: Iterable[DataStorage], rate: WorkbenchRate): def compute(self, data_storage_devices: Iterable[DataStorage], rate: RateComputer):
""" """
Obs: size != NULL and 0 value & read_speed and write_speed != NULL Obs: size != NULL and 0 value & read_speed and write_speed != NULL
:return: result is a rate (score) of all DataStorage devices :return: result is a rate (score) of all DataStorage devices
@ -252,3 +255,6 @@ class DataStorageRate(BaseRate):
# STEP: Fusion Characteristics # STEP: Fusion Characteristics
return self.harmonic_mean(self.DATA_STORAGE_WEIGHTS, return self.harmonic_mean(self.DATA_STORAGE_WEIGHTS,
rates=(size_rate, read_speed_rate, write_speed_rate)) rates=(size_rate, read_speed_rate, write_speed_rate))
rate_algorithm = RateAlgorithm()

View file

@ -1,313 +0,0 @@
from enum import Enum
from typing import Iterable
from ereuse_devicehub.resources.device.models import DataStorage, Processor, RamModule, Device
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, WorkbenchRate
from ereuse_devicehub.resources.event.rate.rate import BaseRate
class Rate(BaseRate):
"""
Rate all the categories of a device
RATE = Quality Rate + Functionality Rate + Appearance Rate
"""
class Range(Enum):
@classmethod
def from_devicehub(cls, r: Enum):
return getattr(cls, r.name) if r else cls.NONE
def compute(self, device: Device):
rate_quality = QualityRate.compute()
rate_functionality = FunctionalityRate.compute()
rate_appearance = self.Appearance.from_devicehub(rate.appearance_range).value
# Final result
return round(max(rate_quality + rate_functionality + rate_appearance, 0), 2)
class QualityRate(BaseRate):
"""
Rate Quality aspect
Display (screen)
Processor
RAM
Data Storage
Battery
Camera
"""
"""
List components wieghts
total_weights = 1
"""
DISPLAY_WEIGHT = 0.25
PROCESSOR_WEIGHT = 0.1
RAM_WEIGHT = 0.25
DATA_STORAGE_WEIGHT = 0.05
BATTERY_WEIGHT = 0.25
CAMERA_WEIGHT = 0.1
def __init__(self) -> None:
super().__init__()
# TODO Check if component exists before rate it.
self.RATES = {
# composition: type: (field, compute class)
Display.t: ('display', DisplayRate()),
Processor.t: ('processor', ProcessorRate()),
RamModule.t: ('ram', RamRate()),
DataStorage.t: ('data_storage', DataStorageRate()),
Battery.t: ('battery', BatteryRate()),
Camera.t: ('camera', CameraRate())
}
def compute(self, device: Device):
rate = self.RATES
# TODO Assign only the weight of existing components.
weights = (
self.DISPLAY_WEIGHT, self.PROCESSOR_WEIGHT, self.RAM_WEIGHT, self.DATA_STORAGE_WEIGHT, self.BATTERY_WEIGHT,
self.CAMERA_WEIGHT)
return self.harmonic_mean(weights, rate)
class FunctionalityRate(BaseRate):
"""
Rate Functionality aspects on mobile devices
"""
# Functionality Range v2
A = 0, 5
B = 0
C = -0, 25
D = -0, 5
NONE = -0, 3
# SUM(weights) = 1
SIM_WEIGHT = 0.2
USB_WEIGHT = 0.25
WIFI_WEIGHT = 0.05
BLUETOOTH_WEIGHT = 0.05
FINGERPRINT_WEIGHT = 0.05
LOUDSPEAKER_WEIGHT = 0.15
MICROPHONE_WEIGHT = 0.15
@classmethod
def compute(cls, device: Device):
"""
:param FunctionalityDevice: List[Boolean]
:return:
"""
test_bios_power_on = device.last_event_of(TestBios).bios_power_on # type: bool
test_bios_power_on = int(test_bios_power_on)
test_battery = device.last_event_of(TestBattery)
test_audio
test_data_storage
functionality = test_bios.bios_power_on * self.BIOS_WEIGHT + ...
return cls(funcionality=functionality)
# TODO Check if funcionality aspect is != NULL
sim = FunctionalityDevice.sim * self.SIM_WEIGHT
usb = FunctionalityDevice.usb * self.USB_WEIGHT
wifi = FunctionalityDevice.wifi * self.WIFI_WEIGHT
bt = FunctionalityDevice.bt * self.BLUETOOTH_WEIGHT
fingerprint = FunctionalityDevice.fingerprint * self.FINGERPRINT_WEIGHT
loudspeaker = FunctionalityDevice.loudspeaker * self.LOUDSPEAKER_WEIGHT
microphone = FunctionalityDevice.microphone * self.MICROPHONE_WEIGHT
functionality_rate = (sim + usb + wifi + bt + fingerprint + loudspeaker + microphone)
# TODO Add functionality range (buttons, chassis, display defects, camera defects)
return functionality_rate
class Appearance(Range):
"""
APPEARANCE GRADE [0.5,-0.5]
Enum(AppearanceRangev2)
"""
Z = 0.5
A = 0.4
B = 0.1
C = -0.1
D = -0.25
E = -0.5
NONE = -0.3
class DisplayRate(QualityRate):
"""
Calculate a DisplayRate
"""
SIZE_NORM = 3.5, 7.24
RESOLUTION_H_NORM = 440, 1080
RESOLUTION_W_NORM = 720, 2048
DISPLAY_WEIGHTS = 0.6, 0.2, 0.2
def compute(self, display: Display):
size = display.size or self.DEFAULT_SIZE
resolution_h = display.resolution_h or 0
resolution_w = display.resolution_w or 0
# STEP: Normalize values
size_norm = max(self.norm(size, *self.SIZE_NORM), 0)
resolution_h_norm = max(self.norm(resolution_h, *self.RESOLUTION_H_NORM), 0)
resolution_w_norm = max(self.norm(resolution_w, *self.RESOLUTION_W_NORM), 0)
# STEP: Fusion Characteristics
return self.harmonic_mean(self.DISPLAY_WEIGHTS, rates=(size_norm, resolution_h_norm, resolution_w_norm))
# COMPONENTS RATE V1 (PROCESSOR,RAM,HHD)
# TODO quality components rate qualityrate class??
class ProcessorRate(QualityRate):
"""
Calculate a ProcessorRate
"""
# processor.xMin, processor.xMax
PROCESSOR_NORM = 3196.17, 17503.81
CORES_NORM = 1, 6
DEFAULT_CORES = 1
DEFAULT_SPEED = 1.6
DEFAULT_SCORE = 4000
PROCESSOR_WEIGHTS = 0.5, 0.5
def compute(self, processor: Processor):
""" Compute processor rate
Obs: cores and speed are possible NULL value
:return: result is a rate (score) of Processor characteristics
"""
cores = processor.cores or self.DEFAULT_CORES
speed = processor.speed or self.DEFAULT_SPEED
# STEP: Normalize values
cores_norm = max(self.norm(cores, *self.PROCESSOR_NORM), 0)
cpu_speed_norm = max(self.norm(speed, *self.CORES_NORM), 0)
# STEP: Fusion Characteristics
return self.harmonic_mean(self.PROCESSOR_WEIGHTS, rates=(cores_norm, cpu_speed_norm))
class RamRate(QualityRate):
"""
Calculate a RamRate of all RamModule devices
"""
# ram.size.xMin; ram.size.xMax
SIZE_NORM = 256, 8192
RAM_SPEED_NORM = 133, 1333
# ram.speed.factor
RAM_SPEED_FACTOR = 3.7
# ram.size.weight; ram.speed.weight;
RAM_WEIGHTS = 0.7, 0.3
def compute(self, ram_devices: Iterable[RamModule]):
"""
Obs: RamModule.speed is possible NULL value & size != NULL or NOT??
:return: result is a rate (score) of all RamModule components
"""
size = 0.0
speed = 0.0
# STEP: Filtering, data cleaning and merging of component parts
for ram in ram_devices:
_size = ram.size or 0
size += _size
_speed = ram.speed or 0
speed += _speed
# STEP: Normalize values
size_norm = max(self.norm(size, *self.SIZE_NORM), 0)
ram_speed_norm = max(self.norm(speed, *self.RAM_SPEED_NORM), 0)
# STEP: Fusion Characteristics
return self.harmonic_mean(self.RAM_WEIGHTS, rates=(size_norm, ram_speed_norm))
class DataStorageRate(QualityRate):
"""
Calculate the rate of all DataStorage devices
"""
# drive.size.xMin; drive.size.xMax
SIZE_NORM = 4096, 265000
READ_SPEED_NORM = 2.7, 109.5
WRITE_SPEED_NORM = 2, 27.35
# drive.size.weight; drive.readingSpeed.weight; drive.writingSpeed.weight;
DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25
def compute(self, data_storage_devices: Iterable[DataStorage], rate: WorkbenchRate):
"""
Obs: size != NULL and 0 value & read_speed and write_speed != NULL
:return: result is a rate (score) of all DataStorage devices
"""
size = 0
read_speed = 0
write_speed = 0
# STEP: Filtering, data cleaning and merging of component parts
for storage in data_storage_devices:
# todo fix StopIteration if don't exists BenchmarkDataStorage
benchmark = next(e for e in storage.events if isinstance(e, BenchmarkDataStorage))
# prevent NULL values
_size = storage.size or 0
size += _size
read_speed += benchmark.read_speed * _size
write_speed += benchmark.write_speed * _size
# STEP: Fusion components
# Check almost one storage have size, try catch exception 0/0
if size:
read_speed /= size
write_speed /= size
# STEP: Normalize values
size_norm = max(self.norm(size, *self.SIZE_NORM), 0)
read_speed_norm = max(self.norm(read_speed, *self.READ_SPEED_NORM), 0)
write_speed_norm = max(self.norm(write_speed, *self.WRITE_SPEED_NORM), 0)
# STEP: Fusion Characteristics
return self.harmonic_mean(self.DATA_STORAGE_WEIGHTS,
rates=(size_norm, read_speed_norm, write_speed_norm))
class BatteryRate(QualityRate):
"""
Rate Battery component if device Type = {Mobile Devices}
"""
CAPACITY_NORM = 2200, 6000
DEFAULT_CAPACITY = 3000
def compute(self, display: Display):
capacity = battery.capacity or self.DEFAULT_CAPACITY
# STEP: Normalize values
capacity_norm = max(self.norm(capacity, *self.CAPACITY_NORM), 0)
return capacity_norm
class CameraRate(QualityRate):
"""
Rate camera component if exist on device
"""
RESOLUTION_NORM = 2200, 6000
DEFAULT_RESOLUTION = 16
def compute(self, display: Display):
resolution = camera.resolution or self.DEFAULT_RESOLUTION
# STEP: Normalize values
resolution_norm = max(self.norm(resolution, *self.RESOLUTION_NORM), 0)
return resolution_norm