From 825c07c5f95332bb8c389d5b6699e2ac456c342c Mon Sep 17 00:00:00 2001 From: nad Date: Thu, 11 Apr 2019 18:29:51 +0200 Subject: [PATCH] first commit refactor score --- ereuse_devicehub/resources/enums.py | 24 +- ereuse_devicehub/resources/event/models.py | 379 ++---------------- ereuse_devicehub/resources/event/models.pyi | 358 +++++++---------- ereuse_devicehub/resources/event/rate/main.py | 65 +-- ereuse_devicehub/resources/event/rate/rate.py | 6 +- .../resources/event/rate/workbench/v1_0.py | 26 +- .../resources/event/rate/workbench/v2_0.py | 313 --------------- 7 files changed, 221 insertions(+), 950 deletions(-) delete mode 100644 ereuse_devicehub/resources/event/rate/workbench/v2_0.py diff --git a/ereuse_devicehub/resources/enums.py b/ereuse_devicehub/resources/enums.py index d3563fcb..0534351e 100644 --- a/ereuse_devicehub/resources/enums.py +++ b/ereuse_devicehub/resources/enums.py @@ -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' diff --git a/ereuse_devicehub/resources/event/models.py b/ereuse_devicehub/resources/event/models.py index bdd86af4..c18019f3 100644 --- a/ereuse_devicehub/resources/event/models.py +++ b/ereuse_devicehub/resources/event/models.py @@ -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 diff --git a/ereuse_devicehub/resources/event/models.pyi b/ereuse_devicehub/resources/event/models.pyi index c6340b8c..c481f04e 100644 --- a/ereuse_devicehub/resources/event/models.pyi +++ b/ereuse_devicehub/resources/event/models.pyi @@ -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 diff --git a/ereuse_devicehub/resources/event/rate/main.py b/ereuse_devicehub/resources/event/rate/main.py index 69213ba7..0de3fccb 100644 --- a/ereuse_devicehub/resources/event/rate/main.py +++ b/ereuse_devicehub/resources/event/rate/main.py @@ -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) diff --git a/ereuse_devicehub/resources/event/rate/rate.py b/ereuse_devicehub/resources/event/rate/rate.py index d6f4d0d0..eca005c4 100644 --- a/ereuse_devicehub/resources/event/rate/rate.py +++ b/ereuse_devicehub/resources/event/rate/rate.py @@ -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 diff --git a/ereuse_devicehub/resources/event/rate/workbench/v1_0.py b/ereuse_devicehub/resources/event/rate/workbench/v1_0.py index b303a97f..369437e0 100644 --- a/ereuse_devicehub/resources/event/rate/workbench/v1_0.py +++ b/ereuse_devicehub/resources/event/rate/workbench/v1_0.py @@ -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() diff --git a/ereuse_devicehub/resources/event/rate/workbench/v2_0.py b/ereuse_devicehub/resources/event/rate/workbench/v2_0.py deleted file mode 100644 index 5659c826..00000000 --- a/ereuse_devicehub/resources/event/rate/workbench/v2_0.py +++ /dev/null @@ -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