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
@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_NEGATIVE = -3, 5
@ -126,7 +109,7 @@ class FunctionalityRange(Enum):
return self.name
FUNCTIONALITY_RANGE = -0.25, 0.5
FUNCTIONALITY_RANGE = -0.3, 0.4
@unique
@ -142,8 +125,9 @@ class BatteryHealthRange(Enum):
def __str__(self):
return self.name
@unique
class Bios(Enum):
class BiosAccessRange(Enum):
"""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'
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
# TODO add all grade tables (chassis defects, camera defects, buttons test, connectivity, ..)
@unique
class Orientation(Enum):
Vertical = 'vertical'

View File

@ -1,7 +1,6 @@
from collections import Iterable
from datetime import datetime, timedelta
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_UP
from distutils.version import StrictVersion
from typing import Optional, Set, Union
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.device.models import Component, Computer, DataStorage, Desktop, \
Device, Laptop, Server
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ErasureStandards, \
FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \
RatingRange, RatingSoftware, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \
TestDataStorageLength, FUNCTIONALITY_RANGE, FunctionalityRange, AppearanceRange, BatteryHealthRange
from ereuse_devicehub.resources.enums import BiosAccessRange, ErasureStandards, \
PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \
RatingRange, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \
TestDataStorageLength, FunctionalityRange, AppearanceRange, BatteryHealthRange
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
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??
"""
loudspeaker = Column(BDEnum(LoudspeakerRange))
loudspeaker = Column(Boolean)
loudspeaker.comment = 'Test 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'
@ -730,6 +729,7 @@ class TestConnectivity(Test):
locked = Column(Boolean)
locked.comment = 'Test to check if devices is locked'
class TestBattery(Test):
"""
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..
"""
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):
@ -795,6 +795,12 @@ class TestVisual(ManualRate):
functionality_range = Column(DBEnum(FunctionalityRange))
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):
"""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.comment = """The rating for the content."""
software = Column(DBEnum(RatingSoftware))
software.comment = """The algorithm used to produce this rating."""
version = Column(StrictVersionType)
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
def rating_range(self) -> RatingRange:
@ -835,7 +842,15 @@ class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
return args
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):
@ -852,332 +867,24 @@ class RateComputer(Rate):
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__
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
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
def data_storage_range(self):
if self.data_storage:
return RatingRange.from_score(self.data_storage)
def compute_rate(self):
"""
The act of compute general computer rate
"""
pass
@property
def ram_range(self):
if self.ram:
return RatingRange.from_score(self.ram)
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 = [
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
@property
def processor_range(self):
if self.processor:
return RatingRange.from_score(self.processor)
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
@ -1205,15 +912,15 @@ class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""
version = Column(StrictVersionType)
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
this price, if it has not been set manually."""
rating = relationship(FinalRate,
rating = relationship(Rate,
backref=backref('price',
lazy=True,
cascade=CASCADE_OWN,
uselist=False),
primaryjoin=AggregateRate.id == rating_id)
primaryjoin=RateComputer.id == rating_id)
def __init__(self, *args, **kwargs) -> None:
if 'price' in kwargs:
@ -1307,7 +1014,7 @@ class EreusePrice(Price):
if self.WARRANTY2 in rate:
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:
raise ValueError('Cannot compute price for Range.VERY_LOW')
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts

View File

@ -123,6 +123,37 @@ class StepRandom(Step):
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):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
@ -154,11 +185,124 @@ class SnapshotRequest(Model):
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):
rating = ... # type: Column
appearance = ... # type: Column
functionality = ... # type: Column
software = ... # type: Column
version = ... # type: Column
def __init__(self, **kwargs) -> None:
@ -171,118 +315,11 @@ class Rate(EventWithOneDevice):
self.rating_range = ... # type: str
class IndividualRate(Rate):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.aggregated_ratings = ... # type: Set[AggregateRate]
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 RateComputer(Rate):
id = ...
processor = ...
ram = ...
data_storage = ...
class Price(EventWithOneDevice):
@ -331,101 +368,6 @@ class EreusePrice(Price):
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):
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.enums import RatingSoftware
from ereuse_devicehub.resources.event.models import AggregateRate, EreusePrice, Rate, \
WorkbenchRate
from ereuse_devicehub.resources.event.models import RateComputer
from ereuse_devicehub.resources.event.rate.workbench import v1_0
RATE_TYPES = {
WorkbenchRate: {
RatingSoftware.ECost: {
'1.0': v1_0.Rate()
},
RatingSoftware.EMarket: {
}
RateComputer: {
'1.0': v1_0.Rate()
}
}
def rate(device: Device, rate: Rate):
def rate(device: Device, version):
"""
Rates the passed-in ``rate`` using values from the rate itself
and the ``device``.
@ -29,50 +19,7 @@ def rate(device: Device, rate: Rate):
:param device: The device to use as a model.
:param rate: A half-filled rate.
"""
cls = rate.__class__
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][rate.software], \
assert str(rate.version) in RATE_TYPES[cls], \
'Rate version {} not supported.'.format(rate.version)
RATE_TYPES[cls][rate.software][str(rate.version)].compute(device, rate)
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
RATE_TYPES[cls][str(rate.version)].compute(device)

View File

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

View File

@ -5,13 +5,13 @@ from typing import Iterable
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Desktop, Laptop, \
Processor, RamModule, Server
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
WorkbenchRate
RateComputer, TestVisual
# todo if no return assign then rate_c = 1 is assigned
# todo fix corner cases, like components characteristics == None
from ereuse_devicehub.resources.event.rate.rate import BaseRate
class Rate(BaseRate):
class RateAlgorithm(BaseRate):
"""
Rate all components in Computer
"""
@ -46,15 +46,16 @@ class Rate(BaseRate):
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.
This mutates "rate".
"""
assert isinstance(device, (Desktop, Laptop, Server))
assert isinstance(rate, WorkbenchRate)
rate = RateComputer()
rate.processor = rate.data_storage = rate.ram = 1 # Init
@ -71,8 +72,8 @@ class Rate(BaseRate):
setattr(rate, field, result)
rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram)
rate.appearance = self.Appearance.from_devicehub(rate.appearance_range).value
rate.functionality = self.Functionality.from_devicehub(rate.functionality_range).value
rate.appearance = self.Appearance.from_devicehub(TestVisual.appearance_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.appearance = round(rate.appearance, 2)
@ -80,6 +81,8 @@ class Rate(BaseRate):
rate.processor = round(rate.processor, 2)
rate.ram = round(rate.ram, 2)
rate.data_storage = round(rate.data_storage, 2)
device.events_one.add(rate)
return rate
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.
DEFAULT_SCORE = 4000
def compute(self, processor: Processor, rate: WorkbenchRate):
def compute(self, processor: Processor, rate: RateComputer):
""" Compute processor rate
Obs: cores and speed are possible NULL value
:return: result is a rate (score) of Processor characteristics
@ -139,7 +142,7 @@ class RamRate(BaseRate):
# ram.size.weight; ram.speed.weight;
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??
: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;
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
:return: result is a rate (score) of all DataStorage devices
@ -252,3 +255,6 @@ class DataStorageRate(BaseRate):
# STEP: Fusion Characteristics
return self.harmonic_mean(self.DATA_STORAGE_WEIGHTS,
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