first iteration models ratev2
This commit is contained in:
parent
ddb1877133
commit
0f508a1d59
|
@ -651,7 +651,7 @@ class ManualRate(IndividualRate):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class WorkbenchComputerRate(ManualRate):
|
class ComputerRate(ManualRate):
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id), primary_key=True)
|
id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id), primary_key=True)
|
||||||
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE))
|
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE))
|
||||||
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE))
|
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE))
|
||||||
|
@ -697,172 +697,6 @@ class WorkbenchComputerRate(ManualRate):
|
||||||
return RatingRange.from_score(self.graphic_card)
|
return RatingRange.from_score(self.graphic_card)
|
||||||
|
|
||||||
|
|
||||||
""" QUALITY RATE CODE START HERE """
|
|
||||||
|
|
||||||
|
|
||||||
class QualityRate(Rate):
|
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
|
||||||
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE),
|
|
||||||
comment='Is a test explain cpu component.')
|
|
||||||
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE),
|
|
||||||
comment='RAM memory rate.')
|
|
||||||
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
|
|
||||||
comment='Data storage rate, like HHD, SSD.')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ram_range(self):
|
|
||||||
return self.workbench.ram_range
|
|
||||||
|
|
||||||
@property
|
|
||||||
def processor_range(self):
|
|
||||||
return self.workbench.processor_range
|
|
||||||
|
|
||||||
@property
|
|
||||||
def display_range(self):
|
|
||||||
return self.workbench.data_storage_range
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data_storage_range(self):
|
|
||||||
return self.workbench.data_storage_range
|
|
||||||
|
|
||||||
@property
|
|
||||||
def battery_range(self):
|
|
||||||
return self.workbench.ram_range
|
|
||||||
|
|
||||||
@property
|
|
||||||
def camera_range(self):
|
|
||||||
return self.workbench_mobile.camera_range
|
|
||||||
|
|
||||||
@property
|
|
||||||
def graphic_card_range(self):
|
|
||||||
return self.workbench_mobil.graphic_card_range
|
|
||||||
|
|
||||||
|
|
||||||
class QualityRateComputer(QualityRate):
|
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(QualityRate.id), primary_key=True)
|
|
||||||
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE),
|
|
||||||
comment='Is a test explain cpu component.')
|
|
||||||
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE),
|
|
||||||
comment='RAM memory rate.')
|
|
||||||
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
|
|
||||||
comment='Data storage rate, like HHD, SSD.')
|
|
||||||
|
|
||||||
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
|
|
||||||
comment='Graphic card score in performance, amount of memory and benchmark result')
|
|
||||||
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
|
|
||||||
comment='Network adapter rate, take it speed limit')
|
|
||||||
|
|
||||||
bios = Column(Float(decimal_return_scale=2), check_range('bios', *RATE_POSITIVE))
|
|
||||||
bios_range = Column(DBEnum(Bios))
|
|
||||||
bios_range.comment = Bios.__doc__
|
|
||||||
|
|
||||||
# todo ensure for WorkbenchRate version and software are not None when inserting them
|
|
||||||
|
|
||||||
def ratings(self):
|
|
||||||
"""
|
|
||||||
#Computes all the possible rates taking this rating as a model.
|
|
||||||
|
|
||||||
#Returns a set of ratings, including this one, which is mutated,
|
|
||||||
#and the final :class:`.AggregateRate`.
|
|
||||||
"""
|
|
||||||
from ereuse_devicehub.resources.event.rate.main import main
|
|
||||||
return main(self, **app.config.get_namespace('WORKBENCH_RATE_'))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def graphic_card_range(self):
|
|
||||||
if self.graphic_card:
|
|
||||||
return RatingRange.from_score(self.graphic_card)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def network_adapter_range(self):
|
|
||||||
return self.workbench_mobil.network_adapter_range
|
|
||||||
|
|
||||||
@property
|
|
||||||
def bios_range(self):
|
|
||||||
return self.workbench_mobil.bios_range
|
|
||||||
|
|
||||||
|
|
||||||
class QualityRateMobile(QualityRate):
|
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(QualityRate.id), primary_key=True)
|
|
||||||
display = Column(Float(decimal_return_scale=2), check_range('display', *RATE_POSITIVE))
|
|
||||||
display.comment = 'Display rate, screen resolution and size to calculate PPI and convert in score'
|
|
||||||
battery = Column(Float(decimal_return_scale=2), check_range('battery', *RATE_POSITIVE),
|
|
||||||
comment='Battery rate is related with capacity and its health')
|
|
||||||
camera = Column(Float(decimal_return_scale=2), check_range('camera', *RATE_POSITIVE),
|
|
||||||
comment='Camera rate take into account resolution')
|
|
||||||
|
|
||||||
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
|
|
||||||
comment='Graphic card score in performance, amount of memory and benchmark result')
|
|
||||||
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
|
|
||||||
comment='Network adapter rate, take it speed limit')
|
|
||||||
|
|
||||||
bios = Column(Float(decimal_return_scale=2), check_range('bios', *RATE_POSITIVE))
|
|
||||||
bios_range = Column(DBEnum(Bios))
|
|
||||||
bios_range.comment = Bios.__doc__
|
|
||||||
|
|
||||||
# todo ensure for WorkbenchRate version and software are not None when inserting them
|
|
||||||
|
|
||||||
def ratings(self):
|
|
||||||
"""
|
|
||||||
#Computes all the possible rates taking this rating as a model.
|
|
||||||
"""
|
|
||||||
from ereuse_devicehub.resources.event.rate.main import main
|
|
||||||
return main(self, **app.config.get_namespace('WORKBENCH_RATE_'))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def display_range(self):
|
|
||||||
if self.data_storage:
|
|
||||||
return RatingRange.from_score(self.data_storage)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def battery_range(self):
|
|
||||||
if self.ram:
|
|
||||||
return RatingRange.from_score(self.ram)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def camera_range(self):
|
|
||||||
if self.processor:
|
|
||||||
return RatingRange.from_score(self.processor)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def graphic_card_range(self):
|
|
||||||
if self.graphic_card:
|
|
||||||
return RatingRange.from_score(self.graphic_card)
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionalityRate(Rate):
|
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
|
||||||
functionality = Column(Float(decimal_return_scale=2), check_range('functionality', *FUNCTIONALITY_RANGE))
|
|
||||||
functionality.comment = 'Functionality rate of a device'
|
|
||||||
|
|
||||||
functionality_range = Column(DBEnum(FunctionalityRangev2))
|
|
||||||
functionality_range.comment = FunctionalityRangev2.__doc__
|
|
||||||
|
|
||||||
connectivity = Column(Float(decimal_return_scale=2),
|
|
||||||
comment='This punctuation covers a series of aspects related to connectivity.')
|
|
||||||
audio = Column(Float(decimal_return_scale=2), comment='Take into account loudspeaker and microphone')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def connectivity_rate(self):
|
|
||||||
yield
|
|
||||||
|
|
||||||
@property
|
|
||||||
def audio_rate(self):
|
|
||||||
yield
|
|
||||||
|
|
||||||
@property
|
|
||||||
def test_buttonse(self):
|
|
||||||
yield
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def test_camera_defects(self):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
class FinalRate(Rate):
|
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
|
||||||
|
|
||||||
|
|
||||||
class AggregateRate(Rate):
|
class AggregateRate(Rate):
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||||
manual_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
manual_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
||||||
|
@ -959,7 +793,186 @@ class AggregateRate(Rate):
|
||||||
return aggregate
|
return aggregate
|
||||||
|
|
||||||
|
|
||||||
####################################################################################
|
class EreusePrice(Price):
|
||||||
|
"""The act of setting a price by guessing it using the eReuse.org
|
||||||
|
algorithm.
|
||||||
|
|
||||||
|
This algorithm states that the price is the use value of the device
|
||||||
|
(represented by its last :class:`.Rate`) multiplied by a constants
|
||||||
|
value agreed by a circuit or platform.
|
||||||
|
"""
|
||||||
|
MULTIPLIER = {
|
||||||
|
Desktop: 20,
|
||||||
|
Laptop: 30
|
||||||
|
}
|
||||||
|
|
||||||
|
class Type:
|
||||||
|
def __init__(self, percentage: float, price: Decimal) -> None:
|
||||||
|
# see https://stackoverflow.com/a/29651462 for the - 0.005
|
||||||
|
self.amount = EreusePrice.to_price(price * Decimal(percentage))
|
||||||
|
self.percentage = EreusePrice.to_price(price * Decimal(percentage))
|
||||||
|
self.percentage = round(percentage - 0.005, 2)
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
REFURBISHER, PLATFORM, RETAILER = 0, 1, 2
|
||||||
|
STANDARD, WARRANTY2 = 'STD', 'WR2'
|
||||||
|
SCHEMA = {
|
||||||
|
Desktop: {
|
||||||
|
RatingRange.HIGH: {
|
||||||
|
STANDARD: (0.35125, 0.204375, 0.444375),
|
||||||
|
WARRANTY2: (0.47425, 0.275875, 0.599875)
|
||||||
|
},
|
||||||
|
RatingRange.MEDIUM: {
|
||||||
|
STANDARD: (0.385, 0.2558333333, 0.3591666667),
|
||||||
|
WARRANTY2: (0.539, 0.3581666667, 0.5028333333)
|
||||||
|
},
|
||||||
|
RatingRange.LOW: {
|
||||||
|
STANDARD: (0.5025, 0.30875, 0.18875),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Laptop: {
|
||||||
|
RatingRange.HIGH: {
|
||||||
|
STANDARD: (0.3469230769, 0.195, 0.4580769231),
|
||||||
|
WARRANTY2: (0.4522307692, 0.2632307692, 0.6345384615)
|
||||||
|
},
|
||||||
|
RatingRange.MEDIUM: {
|
||||||
|
STANDARD: (0.382, 0.1735, 0.4445),
|
||||||
|
WARRANTY2: (0.5108, 0.2429, 0.6463)
|
||||||
|
},
|
||||||
|
RatingRange.LOW: {
|
||||||
|
STANDARD: (0.4528571429, 0.2264285714, 0.3207142857),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SCHEMA[Server] = SCHEMA[Desktop]
|
||||||
|
|
||||||
|
def __init__(self, device, rating_range, role, price: Decimal) -> None:
|
||||||
|
cls = device.__class__ if device.__class__ != Server else Desktop
|
||||||
|
rate = self.SCHEMA[cls][rating_range]
|
||||||
|
self.standard = EreusePrice.Type(rate[self.STANDARD][role], price)
|
||||||
|
if self.WARRANTY2 in rate:
|
||||||
|
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
|
||||||
|
|
||||||
|
def __init__(self, rating: AggregateRate, **kwargs) -> None:
|
||||||
|
if rating.rating_range == RatingRange.VERY_LOW:
|
||||||
|
raise ValueError('Cannot compute price for Range.VERY_LOW')
|
||||||
|
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts
|
||||||
|
price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP)
|
||||||
|
super().__init__(rating=rating,
|
||||||
|
device=rating.device,
|
||||||
|
price=price,
|
||||||
|
software=kwargs.pop('software', app.config['PRICE_SOFTWARE']),
|
||||||
|
version=kwargs.pop('version', app.config['PRICE_VERSION']),
|
||||||
|
**kwargs)
|
||||||
|
self._compute()
|
||||||
|
|
||||||
|
@orm.reconstructor
|
||||||
|
def _compute(self):
|
||||||
|
"""
|
||||||
|
Calculates eReuse.org prices when initializing the
|
||||||
|
instance from the price and other properties.
|
||||||
|
"""
|
||||||
|
self.refurbisher = self._service(self.Service.REFURBISHER)
|
||||||
|
self.retailer = self._service(self.Service.RETAILER)
|
||||||
|
self.platform = self._service(self.Service.PLATFORM)
|
||||||
|
if hasattr(self.refurbisher, 'warranty2'):
|
||||||
|
self.warranty2 = round(self.refurbisher.warranty2.amount
|
||||||
|
+ self.retailer.warranty2.amount
|
||||||
|
+ self.platform.warranty2.amount, 2)
|
||||||
|
|
||||||
|
def _service(self, role):
|
||||||
|
return self.Service(self.device, self.rating.rating_range, role, self.price)
|
||||||
|
|
||||||
|
|
||||||
|
class EreusePrice(Price):
|
||||||
|
"""The act of setting a price by guessing it using the eReuse.org
|
||||||
|
algorithm.
|
||||||
|
|
||||||
|
This algorithm states that the price is the use value of the device
|
||||||
|
(represented by its last :class:`.Rate`) multiplied by a constants
|
||||||
|
value agreed by a circuit or platform.
|
||||||
|
"""
|
||||||
|
MULTIPLIER = {
|
||||||
|
Desktop: 20,
|
||||||
|
Laptop: 30
|
||||||
|
}
|
||||||
|
|
||||||
|
class Type:
|
||||||
|
def __init__(self, percentage: float, price: Decimal) -> None:
|
||||||
|
# see https://stackoverflow.com/a/29651462 for the - 0.005
|
||||||
|
self.amount = EreusePrice.to_price(price * Decimal(percentage))
|
||||||
|
self.percentage = EreusePrice.to_price(price * Decimal(percentage))
|
||||||
|
self.percentage = round(percentage - 0.005, 2)
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
REFURBISHER, PLATFORM, RETAILER = 0, 1, 2
|
||||||
|
STANDARD, WARRANTY2 = 'STD', 'WR2'
|
||||||
|
SCHEMA = {
|
||||||
|
Desktop: {
|
||||||
|
RatingRange.HIGH: {
|
||||||
|
STANDARD: (0.35125, 0.204375, 0.444375),
|
||||||
|
WARRANTY2: (0.47425, 0.275875, 0.599875)
|
||||||
|
},
|
||||||
|
RatingRange.MEDIUM: {
|
||||||
|
STANDARD: (0.385, 0.2558333333, 0.3591666667),
|
||||||
|
WARRANTY2: (0.539, 0.3581666667, 0.5028333333)
|
||||||
|
},
|
||||||
|
RatingRange.LOW: {
|
||||||
|
STANDARD: (0.5025, 0.30875, 0.18875),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Laptop: {
|
||||||
|
RatingRange.HIGH: {
|
||||||
|
STANDARD: (0.3469230769, 0.195, 0.4580769231),
|
||||||
|
WARRANTY2: (0.4522307692, 0.2632307692, 0.6345384615)
|
||||||
|
},
|
||||||
|
RatingRange.MEDIUM: {
|
||||||
|
STANDARD: (0.382, 0.1735, 0.4445),
|
||||||
|
WARRANTY2: (0.5108, 0.2429, 0.6463)
|
||||||
|
},
|
||||||
|
RatingRange.LOW: {
|
||||||
|
STANDARD: (0.4528571429, 0.2264285714, 0.3207142857),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SCHEMA[Server] = SCHEMA[Desktop]
|
||||||
|
|
||||||
|
def __init__(self, device, rating_range, role, price: Decimal) -> None:
|
||||||
|
cls = device.__class__ if device.__class__ != Server else Desktop
|
||||||
|
rate = self.SCHEMA[cls][rating_range]
|
||||||
|
self.standard = EreusePrice.Type(rate[self.STANDARD][role], price)
|
||||||
|
if self.WARRANTY2 in rate:
|
||||||
|
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
|
||||||
|
|
||||||
|
def __init__(self, rating: AggregateRate, **kwargs) -> None:
|
||||||
|
if rating.rating_range == RatingRange.VERY_LOW:
|
||||||
|
raise ValueError('Cannot compute price for Range.VERY_LOW')
|
||||||
|
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts
|
||||||
|
price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP)
|
||||||
|
super().__init__(rating=rating,
|
||||||
|
device=rating.device,
|
||||||
|
price=price,
|
||||||
|
software=kwargs.pop('software', app.config['PRICE_SOFTWARE']),
|
||||||
|
version=kwargs.pop('version', app.config['PRICE_VERSION']),
|
||||||
|
**kwargs)
|
||||||
|
self._compute()
|
||||||
|
|
||||||
|
@orm.reconstructor
|
||||||
|
def _compute(self):
|
||||||
|
"""
|
||||||
|
Calculates eReuse.org prices when initializing the
|
||||||
|
instance from the price and other properties.
|
||||||
|
"""
|
||||||
|
self.refurbisher = self._service(self.Service.REFURBISHER)
|
||||||
|
self.retailer = self._service(self.Service.RETAILER)
|
||||||
|
self.platform = self._service(self.Service.PLATFORM)
|
||||||
|
if hasattr(self.refurbisher, 'warranty2'):
|
||||||
|
self.warranty2 = round(self.refurbisher.warranty2.amount
|
||||||
|
+ self.retailer.warranty2.amount
|
||||||
|
+ self.platform.warranty2.amount, 2)
|
||||||
|
|
||||||
|
def _service(self, role):
|
||||||
|
return self.Service(self.device, self.rating.rating_range, role, self.price)
|
||||||
|
|
||||||
|
|
||||||
class ResultRate(Rate):
|
class ResultRate(Rate):
|
||||||
|
@ -1150,6 +1163,92 @@ class ResultRate(Rate):
|
||||||
return aggregate
|
return aggregate
|
||||||
|
|
||||||
|
|
||||||
|
class BenchmarkRate:
|
||||||
|
"""
|
||||||
|
Common class to group by benchmark rate classes
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BenchmarkQuality(BenchmarkRate):
|
||||||
|
"""
|
||||||
|
Computes quality benchmarks results to aggregate in result rate
|
||||||
|
"""
|
||||||
|
cpu_sysbench = Column(Float(decimal_return_scale=2))
|
||||||
|
cpu_sysbench.comment = 'Benchmark processor component with sysbench tool'
|
||||||
|
ram_sysbench = Column(Float(decimal_return_scale=2))
|
||||||
|
ram_sysbench.comment = 'Benchmark RAM component'
|
||||||
|
# gpu_sysbench = Column(Float(decimal_return_scale=2)) todo how to do?
|
||||||
|
data_storage_sysbench = Column(Float(decimal_return_scale=2))
|
||||||
|
data_storage_sysbench.comment = 'Benchmark data storage component with sysbench tool'
|
||||||
|
data_storage_smart = Column(Float(decimal_return_scale=2))
|
||||||
|
data_storage_smart.comment = 'Benchmark data storage component with SMART tool'
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionalityTest(Test):
|
||||||
|
"""
|
||||||
|
Class where are generic devices functionality aspects
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TestAudio(FunctionalityTest):
|
||||||
|
"""
|
||||||
|
Test to check all this aspects related with audio fucntions
|
||||||
|
"""
|
||||||
|
loudspeaker = Column(BDEnum(LoudspeakerRange))
|
||||||
|
loudspeaker.comment = 'Range to determine if the speaker is working properly and what sound quality it has.'
|
||||||
|
microphone = Column(Boolean)
|
||||||
|
microphone.comment = 'This evaluate if microphone works correctly'
|
||||||
|
|
||||||
|
|
||||||
|
class TestConnectivity(FunctionalityTest):
|
||||||
|
"""
|
||||||
|
Test to check all this aspects related with functionality connections in devices
|
||||||
|
"""
|
||||||
|
|
||||||
|
SIM = Column(Boolean)
|
||||||
|
SIM.comment = 'Evaluate if SIM works'
|
||||||
|
wifi = Column(Boolean)
|
||||||
|
wifi.comment = 'Evaluate if wifi connection works correctly'
|
||||||
|
bluetooth = Column(Boolean)
|
||||||
|
bluetooth.comment = 'Evaluate if bluetooth works'
|
||||||
|
usb = Column(DBEnum())
|
||||||
|
usb.comment = 'Evaluate if usb port was detected and charger plug works'
|
||||||
|
|
||||||
|
|
||||||
|
class TestBattery(FunctionalityTest):
|
||||||
|
"""
|
||||||
|
Test of length of charge. Source R2: Minimum X minutes discharging the device
|
||||||
|
"""
|
||||||
|
battery_duration = Column(Boolean())
|
||||||
|
battery_duration.comment = ''
|
||||||
|
|
||||||
|
|
||||||
|
class TestBios(FunctionalityTest):
|
||||||
|
"""
|
||||||
|
Test that determinate if is difficult to access BIOS, like need password, are protected..
|
||||||
|
"""
|
||||||
|
bios_range = Column(DBEnum())
|
||||||
|
bios_range.comment = 'Range of difficult to acces BIOS'
|
||||||
|
|
||||||
|
|
||||||
|
class TestApperance():
|
||||||
|
"""
|
||||||
|
Class with appearance characteristics
|
||||||
|
"""
|
||||||
|
chassis_defects_range = Column(BDEnum(ChassisRange))
|
||||||
|
chassis_defects_range.comment = 'Range to determinate cosmetic defects on chassis like scratches'
|
||||||
|
|
||||||
|
|
||||||
|
class TestVisual(TestAppearance):
|
||||||
|
"""
|
||||||
|
Check aesthetics or cosmetic aspects. Like defects on chassis, display, ..
|
||||||
|
"""
|
||||||
|
camera_defects_range = Column(BDEnum(CameraRange))
|
||||||
|
camera_defects_range = 'Range to determinate cosmetic defects on camera'
|
||||||
|
display_defects_range = Column(BDEnum(DisplayRange))
|
||||||
|
display_defects_range = 'Range to determinate cosmetic defects on display'
|
||||||
|
|
||||||
|
|
||||||
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||||
"""The act of setting a trading price for the device.
|
"""The act of setting a trading price for the device.
|
||||||
|
|
||||||
|
@ -1217,97 +1316,6 @@ class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||||
return '{0:0.2f} {1}'.format(self.price, self.currency)
|
return '{0:0.2f} {1}'.format(self.price, self.currency)
|
||||||
|
|
||||||
|
|
||||||
class EreusePrice(Price):
|
|
||||||
"""The act of setting a price by guessing it using the eReuse.org
|
|
||||||
algorithm.
|
|
||||||
|
|
||||||
This algorithm states that the price is the use value of the device
|
|
||||||
(represented by its last :class:`.Rate`) multiplied by a constants
|
|
||||||
value agreed by a circuit or platform.
|
|
||||||
"""
|
|
||||||
MULTIPLIER = {
|
|
||||||
Desktop: 20,
|
|
||||||
Laptop: 30
|
|
||||||
}
|
|
||||||
|
|
||||||
class Type:
|
|
||||||
def __init__(self, percentage: float, price: Decimal) -> None:
|
|
||||||
# see https://stackoverflow.com/a/29651462 for the - 0.005
|
|
||||||
self.amount = EreusePrice.to_price(price * Decimal(percentage))
|
|
||||||
self.percentage = EreusePrice.to_price(price * Decimal(percentage))
|
|
||||||
self.percentage = round(percentage - 0.005, 2)
|
|
||||||
|
|
||||||
class Service:
|
|
||||||
REFURBISHER, PLATFORM, RETAILER = 0, 1, 2
|
|
||||||
STANDARD, WARRANTY2 = 'STD', 'WR2'
|
|
||||||
SCHEMA = {
|
|
||||||
Desktop: {
|
|
||||||
RatingRange.HIGH: {
|
|
||||||
STANDARD: (0.35125, 0.204375, 0.444375),
|
|
||||||
WARRANTY2: (0.47425, 0.275875, 0.599875)
|
|
||||||
},
|
|
||||||
RatingRange.MEDIUM: {
|
|
||||||
STANDARD: (0.385, 0.2558333333, 0.3591666667),
|
|
||||||
WARRANTY2: (0.539, 0.3581666667, 0.5028333333)
|
|
||||||
},
|
|
||||||
RatingRange.LOW: {
|
|
||||||
STANDARD: (0.5025, 0.30875, 0.18875),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Laptop: {
|
|
||||||
RatingRange.HIGH: {
|
|
||||||
STANDARD: (0.3469230769, 0.195, 0.4580769231),
|
|
||||||
WARRANTY2: (0.4522307692, 0.2632307692, 0.6345384615)
|
|
||||||
},
|
|
||||||
RatingRange.MEDIUM: {
|
|
||||||
STANDARD: (0.382, 0.1735, 0.4445),
|
|
||||||
WARRANTY2: (0.5108, 0.2429, 0.6463)
|
|
||||||
},
|
|
||||||
RatingRange.LOW: {
|
|
||||||
STANDARD: (0.4528571429, 0.2264285714, 0.3207142857),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SCHEMA[Server] = SCHEMA[Desktop]
|
|
||||||
|
|
||||||
def __init__(self, device, rating_range, role, price: Decimal) -> None:
|
|
||||||
cls = device.__class__ if device.__class__ != Server else Desktop
|
|
||||||
rate = self.SCHEMA[cls][rating_range]
|
|
||||||
self.standard = EreusePrice.Type(rate[self.STANDARD][role], price)
|
|
||||||
if self.WARRANTY2 in rate:
|
|
||||||
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
|
|
||||||
|
|
||||||
def __init__(self, rating: AggregateRate, **kwargs) -> None:
|
|
||||||
if rating.rating_range == RatingRange.VERY_LOW:
|
|
||||||
raise ValueError('Cannot compute price for Range.VERY_LOW')
|
|
||||||
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts
|
|
||||||
price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP)
|
|
||||||
super().__init__(rating=rating,
|
|
||||||
device=rating.device,
|
|
||||||
price=price,
|
|
||||||
software=kwargs.pop('software', app.config['PRICE_SOFTWARE']),
|
|
||||||
version=kwargs.pop('version', app.config['PRICE_VERSION']),
|
|
||||||
**kwargs)
|
|
||||||
self._compute()
|
|
||||||
|
|
||||||
@orm.reconstructor
|
|
||||||
def _compute(self):
|
|
||||||
"""
|
|
||||||
Calculates eReuse.org prices when initializing the
|
|
||||||
instance from the price and other properties.
|
|
||||||
"""
|
|
||||||
self.refurbisher = self._service(self.Service.REFURBISHER)
|
|
||||||
self.retailer = self._service(self.Service.RETAILER)
|
|
||||||
self.platform = self._service(self.Service.PLATFORM)
|
|
||||||
if hasattr(self.refurbisher, 'warranty2'):
|
|
||||||
self.warranty2 = round(self.refurbisher.warranty2.amount
|
|
||||||
+ self.retailer.warranty2.amount
|
|
||||||
+ self.platform.warranty2.amount, 2)
|
|
||||||
|
|
||||||
def _service(self, role):
|
|
||||||
return self.Service(self.device, self.rating.rating_range, role, self.price)
|
|
||||||
|
|
||||||
|
|
||||||
class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||||
"""The act of testing the physical condition of a device and its
|
"""The act of testing the physical condition of a device and its
|
||||||
components.
|
components.
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Desktop, Laptop, \
|
from ereuse_devicehub.resources.device.models import DataStorage, Processor, RamModule, Device
|
||||||
Processor, RamModule, Server, Device
|
from ereuse_devicehub.resources.enums import RatingRange
|
||||||
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, WorkbenchRate
|
||||||
WorkbenchRate
|
|
||||||
from ereuse_devicehub.resources.event.rate.rate import BaseRate
|
from ereuse_devicehub.resources.event.rate.rate import BaseRate
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,6 +127,288 @@ class Appearance(Range):
|
||||||
NONE = -0.3
|
NONE = -0.3
|
||||||
|
|
||||||
|
|
||||||
|
class QualityRate(Rate):
|
||||||
|
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||||
|
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE),
|
||||||
|
comment='Is a test explain cpu component.')
|
||||||
|
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE),
|
||||||
|
comment='RAM memory rate.')
|
||||||
|
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
|
||||||
|
comment='Data storage rate, like HHD, SSD.')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ram_range(self):
|
||||||
|
return self.workbench.ram_range
|
||||||
|
|
||||||
|
@property
|
||||||
|
def processor_range(self):
|
||||||
|
return self.workbench.processor_range
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_range(self):
|
||||||
|
return self.workbench.data_storage_range
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_storage_range(self):
|
||||||
|
return self.workbench.data_storage_range
|
||||||
|
|
||||||
|
@property
|
||||||
|
def battery_range(self):
|
||||||
|
return self.workbench.ram_range
|
||||||
|
|
||||||
|
@property
|
||||||
|
def camera_range(self):
|
||||||
|
return self.workbench_mobile.camera_range
|
||||||
|
|
||||||
|
@property
|
||||||
|
def graphic_card_range(self):
|
||||||
|
return self.workbench_mobil.graphic_card_range
|
||||||
|
|
||||||
|
|
||||||
|
class QualityRateComputer(QualityRate):
|
||||||
|
id = Column(UUID(as_uuid=True), ForeignKey(QualityRate.id), primary_key=True)
|
||||||
|
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE),
|
||||||
|
comment='Is a test explain cpu component.')
|
||||||
|
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE),
|
||||||
|
comment='RAM memory rate.')
|
||||||
|
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
|
||||||
|
comment='Data storage rate, like HHD, SSD.')
|
||||||
|
|
||||||
|
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
|
||||||
|
comment='Graphic card score in performance, amount of memory and benchmark result')
|
||||||
|
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
|
||||||
|
comment='Network adapter rate, take it speed limit')
|
||||||
|
|
||||||
|
bios = Column(Float(decimal_return_scale=2), check_range('bios', *RATE_POSITIVE))
|
||||||
|
bios_range = Column(DBEnum(Bios))
|
||||||
|
bios_range.comment = Bios.__doc__
|
||||||
|
|
||||||
|
# todo ensure for WorkbenchRate version and software are not None when inserting them
|
||||||
|
|
||||||
|
def ratings(self):
|
||||||
|
"""
|
||||||
|
#Computes all the possible rates taking this rating as a model.
|
||||||
|
|
||||||
|
#Returns a set of ratings, including this one, which is mutated,
|
||||||
|
#and the final :class:`.AggregateRate`.
|
||||||
|
"""
|
||||||
|
from ereuse_devicehub.resources.event.rate.main import main
|
||||||
|
return main(self, **app.config.get_namespace('WORKBENCH_RATE_'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def graphic_card_range(self):
|
||||||
|
if self.graphic_card:
|
||||||
|
return RatingRange.from_score(self.graphic_card)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def network_adapter_range(self):
|
||||||
|
return self.workbench_mobil.network_adapter_range
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bios_range(self):
|
||||||
|
return self.workbench_mobil.bios_range
|
||||||
|
|
||||||
|
|
||||||
|
class QualityRateMobile(QualityRate):
|
||||||
|
id = Column(UUID(as_uuid=True), ForeignKey(QualityRate.id), primary_key=True)
|
||||||
|
display = Column(Float(decimal_return_scale=2), check_range('display', *RATE_POSITIVE))
|
||||||
|
display.comment = 'Display rate, screen resolution and size to calculate PPI and convert in score'
|
||||||
|
battery = Column(Float(decimal_return_scale=2), check_range('battery', *RATE_POSITIVE),
|
||||||
|
comment='Battery rate is related with capacity and its health')
|
||||||
|
camera = Column(Float(decimal_return_scale=2), check_range('camera', *RATE_POSITIVE),
|
||||||
|
comment='Camera rate take into account resolution')
|
||||||
|
|
||||||
|
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
|
||||||
|
comment='Graphic card score in performance, amount of memory and benchmark result')
|
||||||
|
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
|
||||||
|
comment='Network adapter rate, take it speed limit')
|
||||||
|
|
||||||
|
bios = Column(Float(decimal_return_scale=2), check_range('bios', *RATE_POSITIVE))
|
||||||
|
bios_range = Column(DBEnum(Bios))
|
||||||
|
bios_range.comment = Bios.__doc__
|
||||||
|
|
||||||
|
# todo ensure for WorkbenchRate version and software are not None when inserting them
|
||||||
|
|
||||||
|
def ratings(self):
|
||||||
|
"""
|
||||||
|
#Computes all the possible rates taking this rating as a model.
|
||||||
|
"""
|
||||||
|
from ereuse_devicehub.resources.event.rate.main import main
|
||||||
|
return main(self, **app.config.get_namespace('WORKBENCH_RATE_'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_range(self):
|
||||||
|
if self.data_storage:
|
||||||
|
return RatingRange.from_score(self.data_storage)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def battery_range(self):
|
||||||
|
if self.ram:
|
||||||
|
return RatingRange.from_score(self.ram)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def camera_range(self):
|
||||||
|
if self.processor:
|
||||||
|
return RatingRange.from_score(self.processor)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def graphic_card_range(self):
|
||||||
|
if self.graphic_card:
|
||||||
|
return RatingRange.from_score(self.graphic_card)
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionalityRate(Rate):
|
||||||
|
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||||
|
functionality = Column(Float(decimal_return_scale=2), check_range('functionality', *FUNCTIONALITY_RANGE))
|
||||||
|
functionality.comment = 'Functionality rate of a device'
|
||||||
|
|
||||||
|
functionality_range = Column(DBEnum(FunctionalityRangev2))
|
||||||
|
functionality_range.comment = FunctionalityRangev2.__doc__
|
||||||
|
|
||||||
|
connectivity = Column(Float(decimal_return_scale=2),
|
||||||
|
comment='This punctuation covers a series of aspects related to connectivity.')
|
||||||
|
audio = Column(Float(decimal_return_scale=2), comment='Take into account loudspeaker and microphone')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connectivity_rate(self):
|
||||||
|
yield
|
||||||
|
|
||||||
|
@property
|
||||||
|
def audio_rate(self):
|
||||||
|
yield
|
||||||
|
|
||||||
|
@property
|
||||||
|
def test_buttonse(self):
|
||||||
|
yield
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_camera_defects(self):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
class ResultRate(Rate):
|
||||||
|
"""The act of grading the appearance, quality (performance), and functionality
|
||||||
|
of a device.
|
||||||
|
|
||||||
|
There are five categories of ``Rate``:
|
||||||
|
1. ``Quality``. How good is the machine, in terms of performance.
|
||||||
|
2. ``Functionality``.
|
||||||
|
3. ``Appearance``.
|
||||||
|
4. ``Market value``.
|
||||||
|
5. ``Cost of repair``.
|
||||||
|
|
||||||
|
|
||||||
|
There are types of rating a device:
|
||||||
|
|
||||||
|
1. Rate Quality
|
||||||
|
2. Rate Functionality
|
||||||
|
3. Rate Final
|
||||||
|
|
||||||
|
|
||||||
|
List of source where can input information of rating a device:
|
||||||
|
|
||||||
|
1. When processing the device with Workbench Computer/Mobile.
|
||||||
|
2. Using the Android App (through Scan).
|
||||||
|
3.
|
||||||
|
4. Anytime after manually written in a form in the website.
|
||||||
|
"""
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||||
|
quality_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
||||||
|
quality_id.comment = """The Quality Rate used to generate this
|
||||||
|
aggregation, or None if none used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
func_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
||||||
|
func_id.comment = """The Functionality Rate used to generate this
|
||||||
|
aggregation, or None if none used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
final_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
||||||
|
final_id.comment = """The Final Rate used to generate this
|
||||||
|
aggregation, or None if none used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
""" MANUAL INPUT """
|
||||||
|
manual_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
||||||
|
manual_id.comment = """The ManualEvent used to generate this
|
||||||
|
aggregation, or None if none used.
|
||||||
|
|
||||||
|
An example of ManualEvent is using the web or the Android app
|
||||||
|
to rate a device.
|
||||||
|
"""
|
||||||
|
manual = relationship(ManualRate,
|
||||||
|
backref=backref('aggregate_rate_manual',
|
||||||
|
lazy=True,
|
||||||
|
order_by=lambda: ResultRate.created,
|
||||||
|
collection_class=OrderedSet),
|
||||||
|
primaryjoin=manual_id == ManualRate.id)
|
||||||
|
|
||||||
|
""" WORKBENCH COMPUTER """
|
||||||
|
workbench_computer_id = Column(UUID(as_uuid=True), ForeignKey(QualityRateComputer.id))
|
||||||
|
workbench_computer_id.comment = """The WorkbenchRate used to generate
|
||||||
|
this aggregation, or None if none used.
|
||||||
|
"""
|
||||||
|
workbench_computer = relationship(QualityRateComputer,
|
||||||
|
backref=backref('aggregate_rate_workbench',
|
||||||
|
lazy=True,
|
||||||
|
order_by=lambda: ResultRate.created,
|
||||||
|
collection_class=OrderedSet),
|
||||||
|
primaryjoin=workbench_computer_id == QualityRateComputer.id)
|
||||||
|
|
||||||
|
""" WORKBENCH MOBILE """
|
||||||
|
|
||||||
|
workbench_mobile_id = Column(UUID(as_uuid=True), ForeignKey(QualityRateMobile.id))
|
||||||
|
workbench_mobile_id.comment = """The WorkbenchRate used to generate
|
||||||
|
this aggregation, or None if none used.
|
||||||
|
"""
|
||||||
|
workbench_mobile = relationship(QualityRateMobile,
|
||||||
|
backref=backref('aggregate_rate_workbench',
|
||||||
|
lazy=True,
|
||||||
|
order_by=lambda: ResultRate.created,
|
||||||
|
collection_class=OrderedSet),
|
||||||
|
primaryjoin=workbench_mobile_id == QualityRateMobile.id)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
|
kwargs.setdefault('version', StrictVersion('1.0'))
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def quality_rate(cls, quality: QualityRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def functionality_rate(cls, func: FunctionalityRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def final_rate(cls, rate: Rate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Categories
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def quality_category(cls, quality: QualityRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def functionality_category(cls, quality: QualityRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def appearance_category(cls, quality: QualityRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def maket_value_category(cls, quality: QualityRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cost_of_repair_category(cls, quality: QualityRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DisplayRate(QualityRate):
|
class DisplayRate(QualityRate):
|
||||||
"""
|
"""
|
||||||
Calculate a DisplayRate
|
Calculate a DisplayRate
|
||||||
|
@ -156,7 +437,6 @@ class DisplayRate(QualityRate):
|
||||||
|
|
||||||
# TODO quality components rate qualityrate class??
|
# TODO quality components rate qualityrate class??
|
||||||
|
|
||||||
|
|
||||||
class ProcessorRate(QualityRate):
|
class ProcessorRate(QualityRate):
|
||||||
"""
|
"""
|
||||||
Calculate a ProcessorRate
|
Calculate a ProcessorRate
|
||||||
|
|
Reference in a new issue