Review rate changes
This commit is contained in:
parent
e6d496872c
commit
16d00256df
|
@ -39,12 +39,6 @@ class DevicehubConfig(Config):
|
||||||
}
|
}
|
||||||
API_DOC_CLASS_DISCRIMINATOR = 'type'
|
API_DOC_CLASS_DISCRIMINATOR = 'type'
|
||||||
|
|
||||||
# TODO is necessary??
|
|
||||||
WORKBENCH_RATE_VERSION = StrictVersion('1.0')
|
|
||||||
PHOTOBOX_RATE_VERSION = StrictVersion('1.0')
|
|
||||||
"""
|
|
||||||
Official versions for WorkbenchRate and PhotoboxRate
|
|
||||||
"""
|
|
||||||
PRICE_SOFTWARE = PriceSoftware.Ereuse
|
PRICE_SOFTWARE = PriceSoftware.Ereuse
|
||||||
PRICE_VERSION = StrictVersion('1.0')
|
PRICE_VERSION = StrictVersion('1.0')
|
||||||
PRICE_CURRENCY = Currency.EUR
|
PRICE_CURRENCY = Currency.EUR
|
||||||
|
|
|
@ -164,7 +164,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "A",
|
"appearanceRange": "A",
|
||||||
"functionalityRange": "A",
|
"functionalityRange": "A",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"manufacturer": "ASUSTeK Computer INC.",
|
"manufacturer": "ASUSTeK Computer INC.",
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "A",
|
"appearanceRange": "A",
|
||||||
"functionalityRange": "A",
|
"functionalityRange": "A",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"manufacturer": "ASUSTeK Computer INC."
|
"manufacturer": "ASUSTeK Computer INC."
|
||||||
|
|
|
@ -11,7 +11,7 @@ device:
|
||||||
resolutionHeight: 1080
|
resolutionHeight: 1080
|
||||||
size: 21.5
|
size: 21.5
|
||||||
events:
|
events:
|
||||||
- type: TestVisual
|
- type: VisualTest
|
||||||
appearanceRange: A
|
appearanceRange: A
|
||||||
functionalityRange: C
|
functionalityRange: C
|
||||||
labelling: False
|
labelling: False
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "A",
|
"appearanceRange": "A",
|
||||||
"functionalityRange": "A",
|
"functionalityRange": "A",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"type": "Desktop",
|
"type": "Desktop",
|
||||||
|
|
|
@ -8,7 +8,7 @@ device:
|
||||||
manufacturer: BAZ
|
manufacturer: BAZ
|
||||||
layout: ES
|
layout: ES
|
||||||
events:
|
events:
|
||||||
- type: TestVisual
|
- type: VisualTest
|
||||||
appearanceRange: A
|
appearanceRange: A
|
||||||
functionalityRange: A
|
functionalityRange: A
|
||||||
labelling: False
|
labelling: False
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "B",
|
"appearanceRange": "B",
|
||||||
"functionalityRange": "C",
|
"functionalityRange": "C",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"type": "Desktop",
|
"type": "Desktop",
|
||||||
|
|
|
@ -122,7 +122,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "A",
|
"appearanceRange": "A",
|
||||||
"functionalityRange": "A",
|
"functionalityRange": "A",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"manufacturer": "Hewlett-Packard",
|
"manufacturer": "Hewlett-Packard",
|
||||||
|
|
|
@ -154,7 +154,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "B",
|
"appearanceRange": "B",
|
||||||
"functionalityRange": "A",
|
"functionalityRange": "A",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -157,7 +157,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "B",
|
"appearanceRange": "B",
|
||||||
"functionalityRange": "D",
|
"functionalityRange": "D",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"serialNumber": "CZC0408YJG",
|
"serialNumber": "CZC0408YJG",
|
||||||
|
|
|
@ -8,7 +8,7 @@ device:
|
||||||
serialNumber: ABCDEF
|
serialNumber: ABCDEF
|
||||||
imei: 35686800-004141-20
|
imei: 35686800-004141-20
|
||||||
events:
|
events:
|
||||||
- type: TestVisual
|
- type: VisualTest
|
||||||
appearanceRange: A
|
appearanceRange: A
|
||||||
functionalityRange: B
|
functionalityRange: B
|
||||||
labelling: False
|
labelling: False
|
||||||
|
|
|
@ -18,90 +18,90 @@ device:
|
||||||
model: d1ml
|
model: d1ml
|
||||||
manufacturer: d1mr
|
manufacturer: d1mr
|
||||||
tags:
|
tags:
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: tag1
|
id: tag1
|
||||||
events:
|
events:
|
||||||
- type: TestVisual
|
- type: VisualTest
|
||||||
appearanceRange: A
|
appearanceRange: A
|
||||||
functionalityRange: B
|
functionalityRange: B
|
||||||
- type: BenchmarkRamSysbench
|
- type: BenchmarkRamSysbench
|
||||||
rate: 2444
|
rate: 2444
|
||||||
elapsed: 1
|
elapsed: 1
|
||||||
components:
|
components:
|
||||||
- type: GraphicCard
|
- type: GraphicCard
|
||||||
serialNumber: gc1-1s
|
serialNumber: gc1-1s
|
||||||
model: gc1-1ml
|
model: gc1-1ml
|
||||||
manufacturer: gc1-1mr
|
manufacturer: gc1-1mr
|
||||||
- type: RamModule
|
- type: RamModule
|
||||||
serialNumber: rm1-1s
|
serialNumber: rm1-1s
|
||||||
model: rm1-1ml
|
model: rm1-1ml
|
||||||
manufacturer: rm1-1mr
|
manufacturer: rm1-1mr
|
||||||
size: 1024
|
size: 1024
|
||||||
- type: RamModule
|
- type: RamModule
|
||||||
serialNumber: rm2-1s
|
serialNumber: rm2-1s
|
||||||
model: rm2-1ml
|
model: rm2-1ml
|
||||||
manufacturer: rm2-1mr
|
manufacturer: rm2-1mr
|
||||||
size: 1024
|
size: 1024
|
||||||
- type: Processor
|
- type: Processor
|
||||||
model: p1-1ml
|
model: p1-1ml
|
||||||
manufacturer: p1-1mr
|
manufacturer: p1-1mr
|
||||||
events:
|
events:
|
||||||
- type: BenchmarkProcessor
|
- type: BenchmarkProcessor
|
||||||
rate: 2410
|
rate: 2410
|
||||||
elapsed: 44
|
elapsed: 44
|
||||||
- type: BenchmarkProcessorSysbench
|
- type: BenchmarkProcessorSysbench
|
||||||
rate: 4400
|
rate: 4400
|
||||||
elapsed: 44
|
elapsed: 44
|
||||||
- type: SolidStateDrive
|
- type: SolidStateDrive
|
||||||
serialNumber: ssd1-1s
|
serialNumber: ssd1-1s
|
||||||
model: ssd1-1ml
|
model: ssd1-1ml
|
||||||
manufacturer: ssd1-1mr
|
manufacturer: ssd1-1mr
|
||||||
size: 1100
|
size: 1100
|
||||||
events:
|
events:
|
||||||
- type: BenchmarkDataStorage
|
- type: BenchmarkDataStorage
|
||||||
readSpeed: 20
|
readSpeed: 20
|
||||||
writeSpeed: 15
|
writeSpeed: 15
|
||||||
elapsed: 21
|
elapsed: 21
|
||||||
- type: TestDataStorage
|
- type: TestDataStorage
|
||||||
elapsed: 233
|
elapsed: 233
|
||||||
severity: Info
|
severity: Info
|
||||||
status: Completed without error
|
status: Completed without error
|
||||||
length: Short
|
length: Short
|
||||||
lifetime: 99
|
lifetime: 99
|
||||||
assessment: True
|
assessment: True
|
||||||
powerCycleCount: 11
|
powerCycleCount: 11
|
||||||
reallocatedSectorCount: 2
|
reallocatedSectorCount: 2
|
||||||
reportedUncorrectableErrors: 1
|
reportedUncorrectableErrors: 1
|
||||||
commandTimeout: 11
|
commandTimeout: 11
|
||||||
currentPendingSectorCount: 1
|
currentPendingSectorCount: 1
|
||||||
offlineUncorrectable: 33
|
offlineUncorrectable: 33
|
||||||
remainingLifetimePercentage: 1
|
remainingLifetimePercentage: 1
|
||||||
- type: HardDrive
|
- type: HardDrive
|
||||||
serialNumber: hdd1-1s
|
serialNumber: hdd1-1s
|
||||||
model: hdd1-1ml
|
model: hdd1-1ml
|
||||||
manufacturer: hdd1-1mr
|
manufacturer: hdd1-1mr
|
||||||
events:
|
events:
|
||||||
- type: BenchmarkDataStorage
|
- type: BenchmarkDataStorage
|
||||||
readSpeed: 10
|
readSpeed: 10
|
||||||
writeSpeed: 5
|
writeSpeed: 5
|
||||||
elapsed: 20
|
elapsed: 20
|
||||||
- type: Motherboard
|
- type: Motherboard
|
||||||
serialNumber: mb1-1s
|
serialNumber: mb1-1s
|
||||||
model: mb1-1ml
|
model: mb1-1ml
|
||||||
manufacturer: mb1-1mr
|
manufacturer: mb1-1mr
|
||||||
- type: NetworkAdapter
|
- type: NetworkAdapter
|
||||||
serialNumber: na1-s
|
serialNumber: na1-s
|
||||||
model: na1-1ml
|
model: na1-1ml
|
||||||
manufacturer: na1-1mr
|
manufacturer: na1-1mr
|
||||||
speed: 1000
|
speed: 1000
|
||||||
wireless: False
|
wireless: False
|
||||||
- type: NetworkAdapter
|
- type: NetworkAdapter
|
||||||
serialNumber: na2-s
|
serialNumber: na2-s
|
||||||
model: na2-1ml
|
model: na2-1ml
|
||||||
manufacturer: na2-1mr
|
manufacturer: na2-1mr
|
||||||
wireless: True
|
wireless: True
|
||||||
speed: 58
|
speed: 58
|
||||||
- type: RamModule
|
- type: RamModule
|
||||||
serialNumber: rm3-1s
|
serialNumber: rm3-1s
|
||||||
model: rm3-1ml
|
model: rm3-1ml
|
||||||
manufacturer: rm3-1mr
|
manufacturer: rm3-1mr
|
||||||
|
|
|
@ -422,8 +422,7 @@ class Computer(Device):
|
||||||
|
|
||||||
1. The max Ethernet speed of the computer, 0 if ethernet
|
1. The max Ethernet speed of the computer, 0 if ethernet
|
||||||
adaptor exists but its speed is unknown, None if no eth
|
adaptor exists but its speed is unknown, None if no eth
|
||||||
adaptor exists.# TODO add all grade tables (chassis defects, camera defects, buttons test, connectivity, ..)
|
adaptor exists.
|
||||||
|
|
||||||
2. The max WiFi speed of the computer, 0 if computer has
|
2. The max WiFi speed of the computer, 0 if computer has
|
||||||
WiFi but its speed is unknown, None if no WiFi adaptor
|
WiFi but its speed is unknown, None if no WiFi adaptor
|
||||||
exists.
|
exists.
|
||||||
|
|
|
@ -42,7 +42,7 @@ class Device(Thing):
|
||||||
production_date = ... # type: Column
|
production_date = ... # type: Column
|
||||||
brand = ... # type: Column
|
brand = ... # type: Column
|
||||||
generation = ... # type: Column
|
generation = ... # type: Column
|
||||||
variant = ... # type: Column
|
variant = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
@ -65,7 +65,7 @@ class Device(Thing):
|
||||||
self.production_date = ... # type: Optional[datetime]
|
self.production_date = ... # type: Optional[datetime]
|
||||||
self.brand = ... # type: Optional[str]
|
self.brand = ... # type: Optional[str]
|
||||||
self.generation = ... # type: Optional[int]
|
self.generation = ... # type: Optional[int]
|
||||||
self.variant = ... # type: Optional[str]
|
self.variant = ... # type: Optional[str]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def events(self) -> List[e.Event]:
|
def events(self) -> List[e.Event]:
|
||||||
|
@ -202,15 +202,15 @@ class TelevisionSet(Monitor):
|
||||||
class Mobile(Device):
|
class Mobile(Device):
|
||||||
imei = ... # type: Column
|
imei = ... # type: Column
|
||||||
meid = ... # type: Column
|
meid = ... # type: Column
|
||||||
ram_size = ... # type: Column
|
ram_size = ... # type: Column
|
||||||
data_storage_size = ... # type: Column
|
data_storage_size = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.imei = ... # type: Optional[int]
|
self.imei = ... # type: Optional[int]
|
||||||
self.meid = ... # type: Optional[str]
|
self.meid = ... # type: Optional[str]
|
||||||
self.ram_size = ... # type: Optional[int]
|
self.ram_size = ... # type: Optional[int]
|
||||||
self.data_storage_size = ... # type: Optional[int]
|
self.data_storage_size = ... # type: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
class Smartphone(Mobile):
|
class Smartphone(Mobile):
|
||||||
|
|
|
@ -183,7 +183,6 @@ class Mobile(Device):
|
||||||
data_key='dataStorageSize',
|
data_key='dataStorageSize',
|
||||||
description=m.Mobile.data_storage_size)
|
description=m.Mobile.data_storage_size)
|
||||||
|
|
||||||
|
|
||||||
@pre_load
|
@pre_load
|
||||||
def convert_check_imei(self, data):
|
def convert_check_imei(self, data):
|
||||||
if data.get('imei', None):
|
if data.get('imei', None):
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
{% macro component_type(components, type) %}
|
{% macro component_type(components, type) %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for c in components if c.t == type %}
|
{% for c in components if c.t == type %}
|
||||||
<li>
|
<li>
|
||||||
{{ c.__format__('t') }}
|
{{ c.__format__('t') }}
|
||||||
<p>
|
<p>
|
||||||
<small class="text-muted">{{ c.__format__('s') }}</small>
|
<small class="text-muted">{{ c.__format__('s') }}</small>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro rate(range) %}
|
{% macro rate(range) %}
|
||||||
<span class="label label-primary">
|
<span class="label label-primary">
|
||||||
{{ range }}
|
{{ range }}
|
||||||
</span>
|
</span>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
|
@ -31,9 +31,9 @@ class OfType(f.Str):
|
||||||
|
|
||||||
|
|
||||||
class RateQ(query.Query):
|
class RateQ(query.Query):
|
||||||
rating = query.Between(events.Rate.rating, f.Float())
|
rating = query.Between(events.Rate._rating, f.Float())
|
||||||
appearance = query.Between(events.Rate.appearance, f.Float())
|
appearance = query.Between(events.Rate._appearance, f.Float())
|
||||||
functionality = query.Between(events.Rate.functionality, f.Float())
|
functionality = query.Between(events.Rate._functionality, f.Float())
|
||||||
|
|
||||||
|
|
||||||
class TagQ(query.Query):
|
class TagQ(query.Query):
|
||||||
|
|
|
@ -3,7 +3,8 @@ from collections import OrderedDict
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device import models as d
|
from ereuse_devicehub.resources.device import models as d
|
||||||
from ereuse_devicehub.resources.event.models import TestDataStorage, BenchmarkDataStorage
|
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, RateComputer, \
|
||||||
|
TestDataStorage
|
||||||
|
|
||||||
|
|
||||||
class DeviceRow(OrderedDict):
|
class DeviceRow(OrderedDict):
|
||||||
|
@ -43,6 +44,7 @@ class DeviceRow(OrderedDict):
|
||||||
if rate:
|
if rate:
|
||||||
self['Rate'] = rate.rating
|
self['Rate'] = rate.rating
|
||||||
self['Range'] = rate.rating_range
|
self['Range'] = rate.rating_range
|
||||||
|
assert isinstance(rate, RateComputer)
|
||||||
self['Processor Rate'] = rate.processor
|
self['Processor Rate'] = rate.processor
|
||||||
self['Processor Range'] = rate.processor_range
|
self['Processor Range'] = rate.processor_range
|
||||||
self['RAM Rate'] = rate.ram
|
self['RAM Rate'] = rate.ram
|
||||||
|
@ -92,15 +94,18 @@ class DeviceRow(OrderedDict):
|
||||||
self['{} {} Size (MB)'.format(type, i)] = component.size
|
self['{} {} Size (MB)'.format(type, i)] = component.size
|
||||||
self['{} {} Privacy'.format(type, i)] = component.privacy
|
self['{} {} Privacy'.format(type, i)] = component.privacy
|
||||||
try:
|
try:
|
||||||
self['{} {} Lifetime'.format(type, i)] = component.last_event_of(TestDataStorage).lifetime
|
self['{} {} Lifetime'.format(type, i)] = component.last_event_of(
|
||||||
|
TestDataStorage).lifetime
|
||||||
except:
|
except:
|
||||||
self['{} {} Lifetime'.format(type, i)] = ''
|
self['{} {} Lifetime'.format(type, i)] = ''
|
||||||
try:
|
try:
|
||||||
self['{} {} Reading speed'.format(type, i)] = component.last_event_of(BenchmarkDataStorage).read_speed
|
self['{} {} Reading speed'.format(type, i)] = component.last_event_of(
|
||||||
|
BenchmarkDataStorage).read_speed
|
||||||
except:
|
except:
|
||||||
self['{} {} Reading speed'.format(type, i)] = ''
|
self['{} {} Reading speed'.format(type, i)] = ''
|
||||||
try:
|
try:
|
||||||
self['{} {} Writing speed'.format(type, i)] = component.last_event_of(BenchmarkDataStorage).write_speed
|
self['{} {} Writing speed'.format(type, i)] = component.last_event_of(
|
||||||
|
BenchmarkDataStorage).write_speed
|
||||||
except:
|
except:
|
||||||
self['{} {} Writing speed'.format(type, i)] = ''
|
self['{} {} Writing speed'.format(type, i)] = ''
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,6 @@ class DevicesDocumentView(DeviceView):
|
||||||
query = self.query(args)
|
query = self.query(args)
|
||||||
return self.generate_post_csv(query)
|
return self.generate_post_csv(query)
|
||||||
|
|
||||||
# TODO fix only put one row for device.t == computer (rewrite multiples_devices.csv)
|
|
||||||
def generate_post_csv(self, query):
|
def generate_post_csv(self, query):
|
||||||
"""
|
"""
|
||||||
Get device query and put information in csv format
|
Get device query and put information in csv format
|
||||||
|
@ -122,9 +121,9 @@ class DevicesDocumentView(DeviceView):
|
||||||
for device in query:
|
for device in query:
|
||||||
d = DeviceRow(device)
|
d = DeviceRow(device)
|
||||||
if first:
|
if first:
|
||||||
cw.writerow(name for name in d.keys())
|
cw.writerow(d.keys())
|
||||||
first = False
|
first = False
|
||||||
cw.writerow(v for v in d.values())
|
cw.writerow(d.values())
|
||||||
output = make_response(data.getvalue())
|
output = make_response(data.getvalue())
|
||||||
output.headers['Content-Disposition'] = 'attachment; filename=export.csv'
|
output.headers['Content-Disposition'] = 'attachment; filename=export.csv'
|
||||||
output.headers['Content-type'] = 'text/csv'
|
output.headers['Content-type'] = 'text/csv'
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from distutils.version import StrictVersion
|
|
||||||
from enum import Enum, IntEnum, unique
|
from enum import Enum, IntEnum, unique
|
||||||
from typing import Set, Union
|
from typing import Set, Union
|
||||||
|
|
||||||
|
@ -18,8 +17,8 @@ class SnapshotSoftware(Enum):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
RATE_POSITIVE = 0, 10
|
R_POSITIVE = 0, 10
|
||||||
RATE_NEGATIVE = -3, 5
|
R_NEGATIVE = -3, 5
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
|
@ -65,45 +64,30 @@ class PriceSoftware(Enum):
|
||||||
Ereuse = 'Ereuse'
|
Ereuse = 'Ereuse'
|
||||||
|
|
||||||
|
|
||||||
@unique
|
|
||||||
class AggregateRatingVersions(Enum):
|
|
||||||
v1 = StrictVersion('1.0')
|
|
||||||
"""
|
|
||||||
This version is set to aggregate :class:`ereuse_devicehub.resources.
|
|
||||||
event.models.RateComputer` version X and :class:`ereuse_devicehub.
|
|
||||||
resources.event.models.PhotoboxRate` version Y.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class AppearanceRange(Enum):
|
class AppearanceRange(Enum):
|
||||||
"""
|
"""Grades the imperfections that aesthetically affect the device, but not its usage."""
|
||||||
This grade will be defined based on the aesthetics/cosmetic aspects, like visual damage or blemishes principally
|
|
||||||
focused on chassis, physical buttons and screens.
|
|
||||||
"""
|
|
||||||
Z = 'Z. The device is new'
|
Z = 'Z. The device is new'
|
||||||
A = 'A. Is like new; without visual damage'
|
A = 'A. Is like new; without visual damage'
|
||||||
B = 'B. Is in really good condition; small visual damage in difficult places to spot'
|
B = 'B. Is in really good condition; small visual damage in difficult places to spot'
|
||||||
C = 'C. Is in good condition; small visual damage in parts that are easy to spot, minor cosmetic blemishes on chassis)'
|
C = 'C. Is in good condition; small visual damage in parts that are easy to spot, minor cosmetic blemishes on chassis'
|
||||||
D = 'D. Is acceptable; visual damage in visible parts, major cosmetic blemishes on chassis, missing cosmetic parts'
|
D = 'D. Is acceptable; visual damage in visible parts, major cosmetic blemishes on chassis, missing cosmetic parts'
|
||||||
E = 'E. Is unacceptable; severity visual damage, missing essential parts'
|
E = 'E. Is unacceptable; considerable visual damage, missing essential parts'
|
||||||
NONE = 'NA. Grade doesn’t exists'
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
APPEARANCE_RANGE = 0.5, -0.3
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class FunctionalityRange(Enum):
|
class FunctionalityRange(Enum):
|
||||||
"""Based on usage condition of a device and its functionality aspects, like screen defect or camera defects"""
|
"""Grades the defects of a device that affect its usage."""
|
||||||
A = 'A. All the buttons works perfectly, no screen/camera defects and chassis without usage issues'
|
A = 'A. All the buttons works perfectly, no screen/camera defects and chassis without usage issues'
|
||||||
B = 'B. There is a button difficult to press or unstable it, a screen/camera defect or chassis problem'
|
B = 'B. There is a button difficult to press or unstable it, a screen/camera defect or chassis problem'
|
||||||
C = 'C. Chassis defects or multiple buttons don\'t work; broken or unusable it, some screen/camera defect'
|
C = 'C. Chassis defects or multiple buttons don\'t work; broken or unusable it, some screen/camera defect'
|
||||||
D = 'D. Chassis severity usage problems. All buttons, screen or camera don\'t work; broken or unusable it'
|
D = 'D. Chassis severity usage problems. All buttons, screen or camera don\'t work; broken or unusable it'
|
||||||
NONE = 'NA. Grade doesn’t exists'
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
FUNCTIONALITY_RANGE = 0.4, -0.3
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
|
@ -113,9 +97,11 @@ class BatteryHealthRange(Enum):
|
||||||
B = 'B. Battery health is good'
|
B = 'B. Battery health is good'
|
||||||
C = 'C. Battery health is overheat / over voltage status but can stand the minimum duration'
|
C = 'C. Battery health is overheat / over voltage status but can stand the minimum duration'
|
||||||
D = 'D. Battery health is bad; can’t stand the minimum duration time'
|
D = 'D. Battery health is bad; can’t stand the minimum duration time'
|
||||||
E = 'E. Battery health is very bad; and status is dead; unusable or miss it '
|
E = 'E. Battery health is very bad; and status is dead; unusable or miss it'
|
||||||
NONE = 'NA. Grade doesn’t exists'
|
NONE = 'NA. Grade doesn’t exists'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
|
@ -127,6 +113,9 @@ class BiosAccessRange(Enum):
|
||||||
D = 'D. Like B or C, but you had to unlock the BIOS (i.e. by removing the battery)'
|
D = 'D. Like B or C, but you had to unlock the BIOS (i.e. by removing the battery)'
|
||||||
E = 'E. The device could not be booted through the network.'
|
E = 'E. The device could not be booted through the network.'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class Orientation(Enum):
|
class Orientation(Enum):
|
||||||
|
|
|
@ -109,11 +109,6 @@ class TestConnectivityDef(TestDef):
|
||||||
SCHEMA = schemas.TestConnectivity
|
SCHEMA = schemas.TestConnectivity
|
||||||
|
|
||||||
|
|
||||||
class TestBatteryDef(TestDef):
|
|
||||||
VIEW = None
|
|
||||||
SCHEMA = schemas.TestBattery
|
|
||||||
|
|
||||||
|
|
||||||
class TestCameraDef(TestDef):
|
class TestCameraDef(TestDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.TestCamera
|
SCHEMA = schemas.TestCamera
|
||||||
|
@ -134,9 +129,9 @@ class TestBiosDef(TestDef):
|
||||||
SCHEMA = schemas.TestBios
|
SCHEMA = schemas.TestBios
|
||||||
|
|
||||||
|
|
||||||
class TestVisualDef(TestDef):
|
class VisualTestDef(TestDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.TestVisual
|
SCHEMA = schemas.VisualTest
|
||||||
|
|
||||||
|
|
||||||
class RateDef(EventDef):
|
class RateDef(EventDef):
|
||||||
|
|
|
@ -14,7 +14,7 @@ from collections import Iterable
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_UP
|
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_UP
|
||||||
from typing import Optional, Set, Union, Tuple
|
from typing import Optional, Set, Union
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import inflection
|
import inflection
|
||||||
|
@ -40,10 +40,10 @@ from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.agent.models import Agent
|
from ereuse_devicehub.resources.agent.models import Agent
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Desktop, \
|
from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Desktop, \
|
||||||
Device, Laptop, Server
|
Device, Laptop, Server
|
||||||
from ereuse_devicehub.resources.enums import BiosAccessRange, ErasureStandards, \
|
from ereuse_devicehub.resources.enums import AppearanceRange, BatteryHealth, BiosAccessRange, \
|
||||||
PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \
|
ErasureStandards, FunctionalityRange, PhysicalErasureMethod, PriceSoftware, \
|
||||||
RatingRange, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \
|
R_NEGATIVE, R_POSITIVE, RatingRange, ReceiverRole, Severity, SnapshotExpectedEvents, \
|
||||||
TestDataStorageLength, FunctionalityRange, AppearanceRange, BatteryHealthRange
|
SnapshotSoftware, TestDataStorageLength
|
||||||
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
|
|
||||||
|
@ -669,7 +669,6 @@ class TestMixin:
|
||||||
return Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
|
return Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MeasureBattery(TestMixin, Test):
|
class MeasureBattery(TestMixin, Test):
|
||||||
"""A sample of the status of the battery.
|
"""A sample of the status of the battery.
|
||||||
|
|
||||||
|
@ -708,7 +707,7 @@ class TestDataStorage(TestMixin, Test):
|
||||||
assessment = Column(Boolean)
|
assessment = Column(Boolean)
|
||||||
reallocated_sector_count = Column(SmallInteger)
|
reallocated_sector_count = Column(SmallInteger)
|
||||||
power_cycle_count = Column(SmallInteger)
|
power_cycle_count = Column(SmallInteger)
|
||||||
reported_uncorrectable_errors = Column(SmallInteger)
|
_reported_uncorrectable_errors = Column('reported_uncorrectable_errors', Integer)
|
||||||
command_timeout = Column(Integer)
|
command_timeout = Column(Integer)
|
||||||
current_pending_sector_count = Column(SmallInteger)
|
current_pending_sector_count = Column(SmallInteger)
|
||||||
offline_uncorrectable = Column(SmallInteger)
|
offline_uncorrectable = Column(SmallInteger)
|
||||||
|
@ -737,6 +736,16 @@ class TestDataStorage(TestMixin, Test):
|
||||||
t += self.description
|
t += self.description
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reported_uncorrectable_errors(self):
|
||||||
|
return self._reported_uncorrectable_errors
|
||||||
|
|
||||||
|
@reported_uncorrectable_errors.setter
|
||||||
|
def reported_uncorrectable_errors(self, value):
|
||||||
|
# We assume that a huge number is not meaningful
|
||||||
|
# So we keep it like this
|
||||||
|
self._reported_uncorrectable_errors = min(value, db.PSQL_INT_MAX)
|
||||||
|
|
||||||
|
|
||||||
class StressTest(TestMixin, Test):
|
class StressTest(TestMixin, Test):
|
||||||
"""The act of stressing (putting to the maximum capacity)
|
"""The act of stressing (putting to the maximum capacity)
|
||||||
|
@ -760,95 +769,84 @@ class TestAudio(TestMixin, Test):
|
||||||
"""
|
"""
|
||||||
Test to check all this aspects related with audio functions, Manual Tests??
|
Test to check all this aspects related with audio functions, Manual Tests??
|
||||||
"""
|
"""
|
||||||
loudspeaker = Column(Boolean)
|
_speaker = Column('speaker', Boolean)
|
||||||
loudspeaker.comment = 'Test to determine if the speaker is working properly and what sound quality it has.'
|
_speaker.comment = """Whether the speaker works as expected."""
|
||||||
microphone = Column(Boolean)
|
_microphone = Column('microphone', Boolean)
|
||||||
microphone.comment = 'This evaluate if microphone works correctly'
|
_microphone.comment = """Whether the microphone works as expected."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def speaker(self):
|
||||||
|
return self._speaker
|
||||||
|
|
||||||
|
@speaker.setter
|
||||||
|
def speaker(self, x):
|
||||||
|
self._speaker = x
|
||||||
|
self._check()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def microphone(self):
|
||||||
|
return self._microphone
|
||||||
|
|
||||||
|
@microphone.setter
|
||||||
|
def microphone(self, x):
|
||||||
|
self._microphone = x
|
||||||
|
self._check()
|
||||||
|
|
||||||
|
def _check(self):
|
||||||
|
"""Sets ``severity`` to ``error`` if any of the variables fail."""
|
||||||
|
if not self._speaker or not self._microphone:
|
||||||
|
self.severity = Severity.Error
|
||||||
|
|
||||||
|
|
||||||
class TestConnectivity(TestMixin, Test):
|
class TestConnectivity(TestMixin, Test):
|
||||||
"""
|
"""Tests that the device can connect both physically and
|
||||||
Test to check all this aspects related with functionality connections in devices
|
wirelessly.
|
||||||
"""
|
|
||||||
# TODO Add Severity and return unique score
|
|
||||||
cellular_network = Column(Boolean)
|
|
||||||
cellular_network.comment = 'Evaluate if cellular network works properly'
|
|
||||||
wifi = Column(Boolean)
|
|
||||||
wifi.comment = 'Evaluate if wifi connection works correctly'
|
|
||||||
bluetooth = Column(Boolean)
|
|
||||||
bluetooth.comment = 'Evaluate if bluetooth works'
|
|
||||||
usb_port = Column(Boolean)
|
|
||||||
usb_port.comment = 'Evaluate if usb port was detected and charger plug works'
|
|
||||||
locked = Column(Boolean)
|
|
||||||
locked.comment = 'Test to check if devices is locked'
|
|
||||||
|
|
||||||
|
A failing test means that at least one connection of the device
|
||||||
class TestBattery(TestMixin, Test):
|
is not working well. A comment should get into more detail.
|
||||||
"""
|
"""
|
||||||
Test battery health, status and length of charge. Minimum X minutes discharging the device
|
|
||||||
"""
|
|
||||||
# TODO how to determinate if test PASS depend on battery stat and/or health
|
|
||||||
battery_stat = Column(Boolean)
|
|
||||||
battery_stat.comment = """
|
|
||||||
Some batteries can report a self-check life status.
|
|
||||||
"""
|
|
||||||
battery_health = Column(DBEnum(BatteryHealthRange))
|
|
||||||
battery_health.comment = BatteryHealthRange.__doc__
|
|
||||||
|
|
||||||
|
|
||||||
class TestCamera(TestMixin, Test):
|
class TestCamera(TestMixin, Test):
|
||||||
|
"""Tests the working conditions of the camera of the device,
|
||||||
|
specially when taking pictures or recording video.
|
||||||
"""
|
"""
|
||||||
Test to determinate functionality and defects on camera when take pictures or record video
|
|
||||||
# TODO define when test FAIL
|
|
||||||
"""
|
|
||||||
camera = Column(Boolean)
|
|
||||||
camera.comment = ""
|
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboard(TestMixin, Test):
|
class TestKeyboard(TestMixin, Test):
|
||||||
"""
|
"""Whether the keyboard works correctly."""
|
||||||
Test to determinate if keyboard layout are and works correctly
|
|
||||||
# TODO define when test FAIL
|
|
||||||
"""
|
|
||||||
keyboard = Column(Boolean)
|
|
||||||
keyboard.comment = ""
|
|
||||||
|
|
||||||
|
|
||||||
class TestTrackpad(TestMixin, Test):
|
class TestTrackpad(TestMixin, Test):
|
||||||
"""
|
"""Whether the trackpad works correctly."""
|
||||||
Test trackpad works correctly
|
|
||||||
# TODO define when test FAIL
|
|
||||||
"""
|
|
||||||
trackpad = Column(Boolean)
|
|
||||||
trackpad.comment = ""
|
|
||||||
|
|
||||||
|
|
||||||
class TestBios(TestMixin, Test):
|
class TestBios(TestMixin, Test):
|
||||||
"""
|
"""Tests the working condition and grades the usability of the BIOS."""
|
||||||
Test that determinate motherboard no beeps, codes or errors when power on.
|
|
||||||
And a grade to reflect some possibles difficult to access or modify setting in the BIOS, like password protection..
|
|
||||||
"""
|
|
||||||
bios_power_on = Column(Boolean)
|
bios_power_on = Column(Boolean)
|
||||||
bios_power_on.comment = """
|
bios_power_on.comment = """Whether there are no beeps or error
|
||||||
Motherboards do a self check when powering up (R2 p.23), test PASS if no beeps, codes, or errors appears.
|
codes when booting up.
|
||||||
|
|
||||||
|
Reference: R2 standard page 23.
|
||||||
"""
|
"""
|
||||||
access_range = Column(DBEnum(BiosAccessRange))
|
access_range = Column(DBEnum(BiosAccessRange))
|
||||||
access_range.comment = 'Range of difficult to access BIOS'
|
access_range.comment = """Difficulty to modify the boot menu.
|
||||||
|
|
||||||
|
This is used as an usability measure for accessing and modifying
|
||||||
|
a bios, specially as something as important as modifying the boot
|
||||||
|
menu."""
|
||||||
|
|
||||||
|
|
||||||
class TestVisual(TestMixin, Test):
|
class VisualTest(TestMixin, Test):
|
||||||
"""
|
"""The act of visually inspecting the appearance and functionality
|
||||||
Manual rate test its are represented with grade and focuses mainly on
|
of the device.
|
||||||
the aesthetic or cosmetic defects of important parts of a device.
|
|
||||||
Like defects on chassis, display, ..
|
|
||||||
"""
|
"""
|
||||||
appearance_range = Column(DBEnum(AppearanceRange))
|
appearance_range = Column(DBEnum(AppearanceRange))
|
||||||
appearance_range.comment = AppearanceRange.__doc__
|
appearance_range.comment = AppearanceRange.__doc__
|
||||||
functionality_range = Column(DBEnum(FunctionalityRange))
|
functionality_range = Column(DBEnum(FunctionalityRange))
|
||||||
functionality_range.comment = FunctionalityRange.__doc__
|
functionality_range.comment = FunctionalityRange.__doc__
|
||||||
labelling = Column(Boolean)
|
labelling = Column(Boolean)
|
||||||
labelling.comment = """Whether there are tags to be removed.
|
labelling.comment = """Whether there are tags to be removed."""
|
||||||
"""
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return super().__str__() + '. Appearance {} and functionality {}'.format(
|
return super().__str__() + '. Appearance {} and functionality {}'.format(
|
||||||
|
@ -858,29 +856,54 @@ class TestVisual(TestMixin, Test):
|
||||||
|
|
||||||
|
|
||||||
class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||||
"""The act of computing a rate based on different categories:
|
"""The act of computing a rate based on different categories"""
|
||||||
1. Quality: the appearance, performance, and functionality
|
# todo jn: explain in each comment what the rate considers.
|
||||||
of a device.
|
N = 2
|
||||||
|
"""The number of significant digits for rates.
|
||||||
|
Values are rounded and stored to it.
|
||||||
There are two ways of rating a device:
|
|
||||||
|
|
||||||
1. When processing the device with Workbench and the Android App.
|
|
||||||
2. Anytime after with the Android App or website.X
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
rating = Column(Float(decimal_return_scale=2), check_range('rating', *RATE_POSITIVE))
|
_rating = Column('rating', Float(decimal_return_scale=N), check_range('rating', *R_POSITIVE))
|
||||||
rating.comment = """The rating for the content."""
|
_rating.comment = """The rating for the content."""
|
||||||
version = Column(StrictVersionType)
|
version = Column(StrictVersionType)
|
||||||
version.comment = """The version of the software."""
|
version.comment = """The version of the software."""
|
||||||
appearance = Column(Float(decimal_return_scale=2), check_range('appearance', *RATE_NEGATIVE))
|
_appearance = Column('appearance',
|
||||||
functionality = Column(Float(decimal_return_scale=2),
|
Float(decimal_return_scale=N),
|
||||||
check_range('functionality', *RATE_NEGATIVE))
|
check_range('appearance', *R_NEGATIVE))
|
||||||
|
_appearance.comment = """"""
|
||||||
|
_functionality = Column('functionality',
|
||||||
|
Float(decimal_return_scale=N),
|
||||||
|
check_range('functionality', *R_NEGATIVE))
|
||||||
|
_functionality.comment = """"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rating(self):
|
||||||
|
return self._rating
|
||||||
|
|
||||||
|
@rating.setter
|
||||||
|
def rating(self, x):
|
||||||
|
self._rating = round(max(x, 0), self.N)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def appearance(self):
|
||||||
|
return self._appearance
|
||||||
|
|
||||||
|
@appearance.setter
|
||||||
|
def appearance(self, x):
|
||||||
|
self._appearance = round(x, self.N)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def functionality(self):
|
||||||
|
return self._functionality
|
||||||
|
|
||||||
|
@functionality.setter
|
||||||
|
def functionality(self, x):
|
||||||
|
self._functionality = round(x, self.N)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rating_range(self) -> RatingRange:
|
def rating_range(self) -> RatingRange:
|
||||||
if self.rating:
|
""""""
|
||||||
return RatingRange.from_score(self.rating)
|
return RatingRange.from_score(self.rating) if self.rating else None
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
|
@ -901,54 +924,82 @@ class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def compute(cls, device) -> 'RateComputer':
|
def compute(cls, device) -> 'RateComputer':
|
||||||
"""
|
|
||||||
The act of compute general computer rate
|
|
||||||
"""
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class RateComputer(Rate):
|
class RateMixin:
|
||||||
"""
|
@declared_attr
|
||||||
Main class to group by device type: Computer
|
def id(cls):
|
||||||
Computer is broadly extended by ``Desktop``, ``Laptop``, and
|
return Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||||
``Server``.
|
|
||||||
"""
|
|
||||||
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 rate.')
|
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
class RateComputer(RateMixin, Rate):
|
||||||
super().__init__(**kwargs)
|
"""The act of rating a computer."""
|
||||||
|
_processor = Column('processor',
|
||||||
|
Float(decimal_return_scale=Rate.N),
|
||||||
|
check_range('processor', *R_POSITIVE))
|
||||||
|
_processor.comment = """The rate of the Processor."""
|
||||||
|
_ram = Column('ram', Float(decimal_return_scale=Rate.N), check_range('ram', *R_POSITIVE))
|
||||||
|
_ram.comment = """The rate of the RAM."""
|
||||||
|
_data_storage = Column('data_storage',
|
||||||
|
Float(decimal_return_scale=Rate.N),
|
||||||
|
check_range('data_storage', *R_POSITIVE))
|
||||||
|
_data_storage.comment = """'Data storage rate, like HHD, SSD.'"""
|
||||||
|
_graphic_card = Column('graphic_card',
|
||||||
|
Float(decimal_return_scale=Rate.N),
|
||||||
|
check_range('graphic_card', *R_POSITIVE))
|
||||||
|
_graphic_card.comment = 'Graphic card rate.'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def processor(self):
|
||||||
|
return self._processor
|
||||||
|
|
||||||
|
@processor.setter
|
||||||
|
def processor(self, x):
|
||||||
|
self._processor = round(x, self.N)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ram(self):
|
||||||
|
return self._ram
|
||||||
|
|
||||||
|
@ram.setter
|
||||||
|
def ram(self, x):
|
||||||
|
self._ram = round(x, self.N)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_storage(self):
|
||||||
|
return self._data_storage
|
||||||
|
|
||||||
|
@data_storage.setter
|
||||||
|
def data_storage(self, x):
|
||||||
|
self._data_storage = round(x, self.N)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def graphic_card(self):
|
||||||
|
return self._graphic_card
|
||||||
|
|
||||||
|
@graphic_card.setter
|
||||||
|
def graphic_card(self, x):
|
||||||
|
self._graphic_card = round(x, self.N)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data_storage_range(self):
|
def data_storage_range(self):
|
||||||
if self.data_storage:
|
return RatingRange.from_score(self.data_storage) if self.data_storage else None
|
||||||
return RatingRange.from_score(self.data_storage)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ram_range(self):
|
def ram_range(self):
|
||||||
if self.ram:
|
return RatingRange.from_score(self.ram) if self.ram else None
|
||||||
return RatingRange.from_score(self.ram)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def processor_range(self):
|
def processor_range(self):
|
||||||
if self.processor:
|
return RatingRange.from_score(self.processor) if self.processor else None
|
||||||
return RatingRange.from_score(self.processor)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def graphic_card_range(self):
|
def graphic_card_range(self):
|
||||||
if self.graphic_card:
|
return RatingRange.from_score(self.graphic_card) if self.graphic_card else None
|
||||||
return RatingRange.from_score(self.graphic_card)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def compute(cls, device) -> Tuple['RateComputer', 'Price']:
|
def compute(cls, device):
|
||||||
"""
|
"""
|
||||||
The act of compute general computer rate
|
The act of compute general computer rate
|
||||||
"""
|
"""
|
||||||
|
@ -1004,7 +1055,7 @@ class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_price(cls, value: Union[Decimal, float], rounding=ROUND) -> Decimal:
|
def to_price(cls, value: Union[Decimal, float], rounding=ROUND) -> Decimal:
|
||||||
"""Returns a Decimal value with the correct scale for Price.price."""
|
"""Returns a Decimal value with the correct scale for Price.price."""
|
||||||
if isinstance(value, float):
|
if isinstance(value, (float, int)):
|
||||||
value = Decimal(value)
|
value = Decimal(value)
|
||||||
# equation from marshmallow.fields.Decimal
|
# equation from marshmallow.fields.Decimal
|
||||||
return value.quantize(Decimal((0, (1,), -cls.SCALE)), rounding=rounding)
|
return value.quantize(Decimal((0, (1,), -cls.SCALE)), rounding=rounding)
|
||||||
|
@ -1088,7 +1139,7 @@ class EreusePrice(Price):
|
||||||
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
|
self.warranty2 = EreusePrice.Type(rate[self.WARRANTY2][role], price)
|
||||||
|
|
||||||
def __init__(self, rating: RateComputer, **kwargs) -> None:
|
def __init__(self, rating: RateComputer, **kwargs) -> None:
|
||||||
if rating.rating_range == RatingRange.VERY_LOW:
|
if not rating.rating_range or rating.rating_range == RatingRange.VERY_LOW:
|
||||||
raise InvalidRangeForPrice()
|
raise InvalidRangeForPrice()
|
||||||
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts
|
# We pass ROUND_UP strategy so price is always greater than what refurbisher... amounts
|
||||||
price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP)
|
price = self.to_price(rating.rating * self.MULTIPLIER[rating.device.__class__], ROUND_UP)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import ipaddress
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from typing import Dict, List, Optional, Set, Union
|
from typing import Dict, List, Optional, Set, Tuple, Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
|
@ -16,9 +16,9 @@ from teal.enums import Country
|
||||||
|
|
||||||
from ereuse_devicehub.resources.agent.models import Agent
|
from ereuse_devicehub.resources.agent.models import Agent
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer, Device
|
from ereuse_devicehub.resources.device.models import Component, Computer, Device
|
||||||
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ErasureStandards, \
|
from ereuse_devicehub.resources.enums import AppearanceRange, ErasureStandards, \
|
||||||
FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RatingSoftware, ReceiverRole, \
|
FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RatingRange, \
|
||||||
Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength, BiosAccessRange
|
ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength
|
||||||
from ereuse_devicehub.resources.models import Thing
|
from ereuse_devicehub.resources.models import Thing
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class Event(Thing):
|
||||||
severity = ... # type: Column
|
severity = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(created, updated)
|
super().__init__(**kwargs)
|
||||||
self.id = ... # type: UUID
|
self.id = ... # type: UUID
|
||||||
self.name = ... # type: str
|
self.name = ... # type: str
|
||||||
self.type = ... # type: str
|
self.type = ... # type: str
|
||||||
|
@ -75,7 +75,6 @@ class Event(Thing):
|
||||||
|
|
||||||
|
|
||||||
class EventWithOneDevice(Event):
|
class EventWithOneDevice(Event):
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.device = ... # type: Device
|
self.device = ... # type: Device
|
||||||
|
@ -178,8 +177,7 @@ class Install(EventWithOneDevice):
|
||||||
self.address = ... # type: Optional[int]
|
self.address = ... # type: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
class SnapshotRequest(Mod assert rate_computer.rating == 4.61
|
class SnapshotRequest(Model):
|
||||||
el):
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
self.id = ... # type: UUID
|
self.id = ... # type: UUID
|
||||||
self.request = ... # type: dict
|
self.request = ... # type: dict
|
||||||
|
@ -226,6 +224,7 @@ class BenchmarkGraphicCard(BenchmarkWithRate):
|
||||||
|
|
||||||
class Test(EventWithOneDevice):
|
class Test(EventWithOneDevice):
|
||||||
elapsed = ... # type: Column
|
elapsed = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.elapsed = ... # type: Optional[timedelta]
|
self.elapsed = ... # type: Optional[timedelta]
|
||||||
|
@ -233,6 +232,20 @@ class Test(EventWithOneDevice):
|
||||||
|
|
||||||
|
|
||||||
class TestDataStorage(Test):
|
class TestDataStorage(Test):
|
||||||
|
length = ... # type: Column
|
||||||
|
status = ... # type: Column
|
||||||
|
lifetime = ... # type: Column
|
||||||
|
first_error = ... # type: Column
|
||||||
|
passed_lifetime = ... # type: Column
|
||||||
|
assessment = ... # type: Column
|
||||||
|
reallocated_sector_count = ... # type: Column
|
||||||
|
power_cycle_count = ... # type: Column
|
||||||
|
reported_uncorrectable_errors = ... # type: Column
|
||||||
|
command_timeout = ... # type: Column
|
||||||
|
current_pending_sector_count = ... # type: Column
|
||||||
|
offline_uncorrectable = ... # type: Column
|
||||||
|
remaining_lifetime_percentage = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.id = ... # type: UUID
|
self.id = ... # type: UUID
|
||||||
|
@ -259,70 +272,92 @@ class TestAudio(Test):
|
||||||
"""
|
"""
|
||||||
Test to check all this aspects related with audio functions, Manual Tests??
|
Test to check all this aspects related with audio functions, Manual Tests??
|
||||||
"""
|
"""
|
||||||
loudspeaker = ... # type: Column
|
_speaker = ... # type: Column
|
||||||
microphone = ... # type: Column
|
_microphone = ... # type: Column
|
||||||
|
|
||||||
|
def __init__(self, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.speaker = ... # type: bool
|
||||||
|
self.microphone = ... # type: bool
|
||||||
|
|
||||||
|
|
||||||
class TestConnectivity(Test):
|
class TestConnectivity(Test):
|
||||||
cellular_network = ... # type: Column
|
pass
|
||||||
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):
|
class TestCamera(Test):
|
||||||
camera = ... # type: Column
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboard(Test):
|
class TestKeyboard(Test):
|
||||||
keyboard = ... # type: Column
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestTrackpad(Test):
|
class TestTrackpad(Test):
|
||||||
trackpad = ... # type: Column
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestBios(Test):
|
class TestBios(Test):
|
||||||
bios_power_on = ... # type: Column
|
bios_power_on = ... # type: Column
|
||||||
access_range = ... # type: BiosAccessRange
|
access_range = ... # type: Column
|
||||||
|
|
||||||
|
|
||||||
class TestVisual(ManualRate):
|
class VisualTest(Test):
|
||||||
appearance_range = ... # type: AppearanceRange
|
appearance_range = ... # type: AppearanceRange
|
||||||
functionality_range = ... # type: FunctionalityRange
|
functionality_range = ... # type: FunctionalityRange
|
||||||
labelling = ... # type: Column
|
labelling = ... # type: Column
|
||||||
|
|
||||||
|
|
||||||
class Rate(EventWithOneDevice):
|
class Rate(EventWithOneDevice):
|
||||||
rating = ... # type: Column
|
N = 2
|
||||||
appearance = ... # type: Column
|
_rating = ... # type: Column
|
||||||
functionality = ... # type: Column
|
_appearance = ... # type: Column
|
||||||
|
_functionality = ... # type: Column
|
||||||
version = ... # type: Column
|
version = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.rating = ... # type: float
|
self.rating = ... # type: float
|
||||||
self.software = ... # type: RatingSoftware
|
|
||||||
self.version = ... # type: StrictVersion
|
self.version = ... # type: StrictVersion
|
||||||
self.appearance = ... # type: float
|
self.appearance = ... # type: float
|
||||||
self.functionality = ... # type: float
|
self.functionality = ... # type: float
|
||||||
self.rating_range = ... # type: str
|
|
||||||
|
@property
|
||||||
|
def rating_range(self) -> RatingRange:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RateComputer(Rate):
|
class RateComputer(Rate):
|
||||||
id = ...
|
_processor = ... # type: Column
|
||||||
processor = ...
|
_ram = ... # type: Column
|
||||||
ram = ...
|
_data_storage = ... # type: Column
|
||||||
data_storage = ...
|
_graphic_card = ... # 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
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def compute(cls, device):
|
def compute(cls, device: Device) -> Tuple[RateComputer, EreusePrice]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_storage_range(self) -> Optional[RatingRange]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ram_range(self) -> Optional[RatingRange]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def processor_range(self) -> Optional[RatingRange]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def graphic_card_range(self) -> Optional[RatingRange]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from typing import Iterable
|
from typing import Dict, Iterable, Tuple
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Processor, RamModule
|
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Processor, \
|
||||||
|
RamModule
|
||||||
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
||||||
RateComputer, TestVisual, BenchmarkProcessorSysbench
|
BenchmarkProcessorSysbench, RateComputer, VisualTest
|
||||||
# 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
|
from ereuse_devicehub.resources.event.rate.rate import BaseRate
|
||||||
|
|
||||||
|
|
||||||
class RateAlgorithm(BaseRate):
|
class RateAlgorithm(BaseRate):
|
||||||
"""
|
"""The algorithm that generates the Rate v1.0.
|
||||||
Rate all components in Computer
|
|
||||||
|
Do not call directly this class, but use
|
||||||
|
:meth:`ereuse_devicehub.resources.event.models.RateComputer.compute`,
|
||||||
|
which then calls this.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Range(Enum):
|
class Range(Enum):
|
||||||
|
@ -43,48 +45,43 @@ class RateAlgorithm(BaseRate):
|
||||||
Processor.t: ('processor', ProcessorRate()),
|
Processor.t: ('processor', ProcessorRate()),
|
||||||
RamModule.t: ('ram', RamRate()),
|
RamModule.t: ('ram', RamRate()),
|
||||||
DataStorage.t: ('data_storage', DataStorageRate())
|
DataStorage.t: ('data_storage', DataStorageRate())
|
||||||
}
|
} # type: Dict[str, Tuple[str, BaseRate]]
|
||||||
|
|
||||||
def compute(self, device: Computer):
|
def compute(self, device: Computer) -> RateComputer:
|
||||||
|
"""Generates a new
|
||||||
|
:class:`ereuse_devicehub.resources.event.models.RateComputer`
|
||||||
|
for the passed-in device.
|
||||||
"""
|
"""
|
||||||
Compute RateComputer is a rate (score) ranging from 0 to 4.7
|
assert isinstance(device, Computer), 'Can only rate computers'
|
||||||
that represents estimating value of use of desktop and laptop computer components.
|
|
||||||
"""
|
try:
|
||||||
if not isinstance(device, Computer): # todo can be an assert?
|
test_visual = device.last_event_of(VisualTest)
|
||||||
raise CannotRate('Can only rate computers.')
|
except LookupError:
|
||||||
|
raise CannotRate('You need a visual test.')
|
||||||
|
|
||||||
rate = RateComputer()
|
rate = RateComputer()
|
||||||
|
|
||||||
rate.processor = rate.data_storage = rate.ram = 1 # Init
|
rate.processor = rate.data_storage = rate.ram = 1 # Init
|
||||||
|
|
||||||
# Group cpus, rams, storages and compute their rate
|
# Group cpus, rams, storage and compute their rate
|
||||||
# Treat the same way with HardDrive and SolidStateDrive like (DataStorage)
|
# Treat the same way with HardDrive and SolidStateDrive like (DataStorage)
|
||||||
clause = lambda x: DataStorage.t if isinstance(x, DataStorage) else x.t
|
clause = lambda x: DataStorage.t if isinstance(x, DataStorage) else x.t
|
||||||
c = (c for c in device.components if clause(c) in set(self.RATES.keys()))
|
c = (c for c in device.components if clause(c) in set(self.RATES.keys()))
|
||||||
for type, components in groupby(sorted(c, key=clause), key=clause):
|
for type, components in groupby(sorted(c, key=clause), key=clause):
|
||||||
if type == Processor.t: # ProcessorRate.compute expects only 1 processor
|
if type == Processor.t: # ProcessorRate.compute expects only 1 processor
|
||||||
components = next(components)
|
components = next(components)
|
||||||
field, rate_cls = self.RATES[type] # type: str, BaseRate
|
field, rate_cls = self.RATES[type]
|
||||||
result = rate_cls.compute(components, rate)
|
result = rate_cls.compute(components)
|
||||||
if result:
|
if result:
|
||||||
setattr(rate, field, result)
|
setattr(rate, field, result)
|
||||||
# TODO is necessary check if TestVisual exists?? cause StopIteration Error
|
|
||||||
try:
|
|
||||||
test_visual = next(e for e in device.events if isinstance(e, TestVisual))
|
|
||||||
except StopIteration:
|
|
||||||
raise CannotRate('You need a visual test.')
|
|
||||||
|
|
||||||
rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram)
|
rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram)
|
||||||
rate.appearance = self.Appearance.from_devicehub(test_visual.appearance_range).value
|
rate.appearance = self.Appearance.from_devicehub(test_visual.appearance_range).value
|
||||||
rate.functionality = self.Functionality.from_devicehub(test_visual.functionality_range).value
|
rate.functionality = self.Functionality.from_devicehub(
|
||||||
|
test_visual.functionality_range).value
|
||||||
|
|
||||||
rate.rating = round(max(rate_components + rate.functionality + rate.appearance, 0), 2)
|
rate.rating = rate_components + rate.functionality + rate.appearance
|
||||||
rate.appearance = round(rate.appearance, 2)
|
|
||||||
rate.functionality = round(rate.functionality, 2)
|
|
||||||
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)
|
device.events_one.add(rate)
|
||||||
|
assert 0 <= rate.rating <= 4.7, 'Rate ranges from 0 to 4.7'
|
||||||
return rate
|
return rate
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,18 +98,20 @@ class ProcessorRate(BaseRate):
|
||||||
# Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz, score = 23406.92 but results inan score of 17503.
|
# Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz, score = 23406.92 but results inan score of 17503.
|
||||||
DEFAULT_SCORE = 4000
|
DEFAULT_SCORE = 4000
|
||||||
|
|
||||||
def compute(self, processor: Processor, rate: RateComputer):
|
def compute(self, processor: Processor):
|
||||||
""" Compute processor rate
|
""" Compute processor rate
|
||||||
Obs: cores and speed are possible NULL value
|
Obs: cores and speed are possible NULL value
|
||||||
:return: result is a rate (score) of Processor characteristics
|
:return: result is a rate (score) of Processor characteristics
|
||||||
"""
|
"""
|
||||||
# todo for processor_device in processors; more than one processor
|
# todo jn? for processor_device in processors; more than one processor
|
||||||
cores = processor.cores or self.DEFAULT_CORES
|
cores = processor.cores or self.DEFAULT_CORES
|
||||||
speed = processor.speed or self.DEFAULT_SPEED
|
speed = processor.speed or self.DEFAULT_SPEED
|
||||||
# todo fix StopIteration if don't exists BenchmarkProcessor
|
# todo jn? fix StopIteration if don't exists BenchmarkProcessor
|
||||||
benchmark_cpu = next(e for e in processor.events if
|
benchmark_cpu = next(
|
||||||
isinstance(e, BenchmarkProcessor) and not isinstance(e, BenchmarkProcessorSysbench))
|
e for e in reversed(processor.events)
|
||||||
# todo fix if benchmark_cpu.rate == 0
|
if isinstance(e, BenchmarkProcessor) and not isinstance(e, BenchmarkProcessorSysbench)
|
||||||
|
)
|
||||||
|
# todo jn? fix if benchmark_cpu.rate == 0
|
||||||
benchmark_cpu = benchmark_cpu.rate or self.DEFAULT_SCORE
|
benchmark_cpu = benchmark_cpu.rate or self.DEFAULT_SCORE
|
||||||
|
|
||||||
# STEP: Fusion components
|
# STEP: Fusion components
|
||||||
|
@ -129,8 +128,6 @@ class ProcessorRate(BaseRate):
|
||||||
processor_rate = self.rate_lin(processor_norm)
|
processor_rate = self.rate_lin(processor_norm)
|
||||||
if processor_norm >= self.CLOG:
|
if processor_norm >= self.CLOG:
|
||||||
processor_rate = self.rate_log(processor_norm)
|
processor_rate = self.rate_log(processor_norm)
|
||||||
|
|
||||||
assert processor_rate, 'Could not rate processor.'
|
|
||||||
return processor_rate
|
return processor_rate
|
||||||
|
|
||||||
|
|
||||||
|
@ -146,7 +143,7 @@ class RamRate(BaseRate):
|
||||||
# ram.size.weight; ram.speed.weight;
|
# ram.size.weight; ram.speed.weight;
|
||||||
RAM_WEIGHTS = 0.7, 0.3
|
RAM_WEIGHTS = 0.7, 0.3
|
||||||
|
|
||||||
def compute(self, ram_devices: Iterable[RamModule], rate: RateComputer):
|
def compute(self, ram_devices: Iterable[RamModule]):
|
||||||
"""
|
"""
|
||||||
Obs: RamModule.speed is possible NULL value & size != NULL or NOT??
|
Obs: RamModule.speed is possible NULL value & size != NULL or NOT??
|
||||||
:return: result is a rate (score) of all RamModule components
|
:return: result is a rate (score) of all RamModule components
|
||||||
|
@ -203,7 +200,7 @@ class DataStorageRate(BaseRate):
|
||||||
# drive.size.weight; drive.readingSpeed.weight; drive.writingSpeed.weight;
|
# drive.size.weight; drive.readingSpeed.weight; drive.writingSpeed.weight;
|
||||||
DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25
|
DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25
|
||||||
|
|
||||||
def compute(self, data_storage_devices: Iterable[DataStorage], rate: RateComputer):
|
def compute(self, data_storage_devices: Iterable[DataStorage]):
|
||||||
"""
|
"""
|
||||||
Obs: size != NULL and 0 value & read_speed and write_speed != NULL
|
Obs: size != NULL and 0 value & read_speed and write_speed != NULL
|
||||||
:return: result is a rate (score) of all DataStorage devices
|
:return: result is a rate (score) of all DataStorage devices
|
||||||
|
@ -215,7 +212,7 @@ class DataStorageRate(BaseRate):
|
||||||
# STEP: Filtering, data cleaning and merging of component parts
|
# STEP: Filtering, data cleaning and merging of component parts
|
||||||
for storage in data_storage_devices:
|
for storage in data_storage_devices:
|
||||||
# We assume all hdd snapshots have BenchmarkDataStorage
|
# We assume all hdd snapshots have BenchmarkDataStorage
|
||||||
benchmark = next(e for e in storage.events if isinstance(e, BenchmarkDataStorage))
|
benchmark = storage.last_event_of(BenchmarkDataStorage)
|
||||||
# prevent NULL values
|
# prevent NULL values
|
||||||
_size = storage.size or 0
|
_size = storage.size or 0
|
||||||
size += _size
|
size += _size
|
||||||
|
|
|
@ -13,8 +13,8 @@ from ereuse_devicehub.resources import enums
|
||||||
from ereuse_devicehub.resources.agent import schemas as s_agent
|
from ereuse_devicehub.resources.agent import schemas as s_agent
|
||||||
from ereuse_devicehub.resources.device import schemas as s_device
|
from ereuse_devicehub.resources.device import schemas as s_device
|
||||||
from ereuse_devicehub.resources.enums import AppearanceRange, BiosAccessRange, FunctionalityRange, \
|
from ereuse_devicehub.resources.enums import AppearanceRange, BiosAccessRange, FunctionalityRange, \
|
||||||
PhysicalErasureMethod, PriceSoftware, RATE_POSITIVE, RatingRange, ReceiverRole, \
|
PhysicalErasureMethod, R_POSITIVE, RatingRange, ReceiverRole, \
|
||||||
Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength, BatteryHealthRange
|
Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength
|
||||||
from ereuse_devicehub.resources.event import models as m
|
from ereuse_devicehub.resources.event import models as m
|
||||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
||||||
from ereuse_devicehub.resources.schemas import Thing
|
from ereuse_devicehub.resources.schemas import Thing
|
||||||
|
@ -176,38 +176,24 @@ class StressTest(Test):
|
||||||
|
|
||||||
class TestAudio(Test):
|
class TestAudio(Test):
|
||||||
__doc__ = m.TestAudio.__doc__
|
__doc__ = m.TestAudio.__doc__
|
||||||
loudspeaker = Boolean()
|
speaker = Boolean(description=m.TestAudio._speaker.comment)
|
||||||
microphone = Boolean()
|
microphone = Boolean(description=m.TestAudio._microphone.comment)
|
||||||
|
|
||||||
|
|
||||||
class TestConnectivity(Test):
|
class TestConnectivity(Test):
|
||||||
__doc__ = m.TestConnectivity.__doc__
|
__doc__ = m.TestConnectivity.__doc__
|
||||||
cellular_network = Boolean()
|
|
||||||
wifi = Boolean()
|
|
||||||
bluetooth = Boolean()
|
|
||||||
usb_port = Boolean()
|
|
||||||
locked = Boolean()
|
|
||||||
|
|
||||||
|
|
||||||
class TestBattery(Test):
|
|
||||||
__doc__ = m.TestBattery.__doc__
|
|
||||||
battery_stat = Boolean()
|
|
||||||
battery_health = EnumField(BatteryHealthRange, data_key='batteryHealthRange')
|
|
||||||
|
|
||||||
|
|
||||||
class TestCamera(Test):
|
class TestCamera(Test):
|
||||||
__doc__ = m.TestCamera.__doc__
|
__doc__ = m.TestCamera.__doc__
|
||||||
camera = Boolean()
|
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboard(Test):
|
class TestKeyboard(Test):
|
||||||
__doc__ = m.TestKeyboard.__doc__
|
__doc__ = m.TestKeyboard.__doc__
|
||||||
keyboard = Boolean()
|
|
||||||
|
|
||||||
|
|
||||||
class TestTrackpad(Test):
|
class TestTrackpad(Test):
|
||||||
__doc__ = m.TestTrackpad.__doc__
|
__doc__ = m.TestTrackpad.__doc__
|
||||||
trackpad = Boolean()
|
|
||||||
|
|
||||||
|
|
||||||
class TestBios(Test):
|
class TestBios(Test):
|
||||||
|
@ -216,8 +202,8 @@ class TestBios(Test):
|
||||||
access_range = EnumField(BiosAccessRange, data_key='accessRange')
|
access_range = EnumField(BiosAccessRange, data_key='accessRange')
|
||||||
|
|
||||||
|
|
||||||
class TestVisual(Test):
|
class VisualTest(Test):
|
||||||
__doc__ = m.TestVisual.__doc__
|
__doc__ = m.VisualTest.__doc__
|
||||||
appearance_range = EnumField(AppearanceRange, data_key='appearanceRange')
|
appearance_range = EnumField(AppearanceRange, data_key='appearanceRange')
|
||||||
functionality_range = EnumField(FunctionalityRange, data_key='functionalityRange')
|
functionality_range = EnumField(FunctionalityRange, data_key='functionalityRange')
|
||||||
labelling = Boolean()
|
labelling = Boolean()
|
||||||
|
@ -225,14 +211,21 @@ class TestVisual(Test):
|
||||||
|
|
||||||
class Rate(EventWithOneDevice):
|
class Rate(EventWithOneDevice):
|
||||||
__doc__ = m.Rate.__doc__
|
__doc__ = m.Rate.__doc__
|
||||||
rating = Integer(validate=Range(*RATE_POSITIVE),
|
rating = Integer(validate=Range(*R_POSITIVE),
|
||||||
dump_only=True,
|
dump_only=True,
|
||||||
description=m.Rate.rating.comment)
|
description=m.Rate._rating.comment)
|
||||||
version = Version(dump_only=True,
|
version = Version(dump_only=True,
|
||||||
description=m.Rate.version.comment)
|
description=m.Rate.version.comment)
|
||||||
appearance = Integer(validate=Range(-3, 5), dump_only=True)
|
appearance = Integer(validate=Range(enums.R_NEGATIVE),
|
||||||
functionality = Integer(validate=Range(-3, 5), dump_only=True)
|
dump_only=True,
|
||||||
rating_range = EnumField(RatingRange, dump_only=True, data_key='ratingRange')
|
description=m.Rate._appearance.comment)
|
||||||
|
functionality = Integer(validate=Range(enums.R_NEGATIVE),
|
||||||
|
dump_only=True,
|
||||||
|
description=m.Rate._functionality.comment)
|
||||||
|
rating_range = EnumField(RatingRange,
|
||||||
|
dump_only=True,
|
||||||
|
data_key='ratingRange',
|
||||||
|
description=m.Rate.rating_range.__doc__)
|
||||||
|
|
||||||
|
|
||||||
class RateComputer(Rate):
|
class RateComputer(Rate):
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from contextlib import suppress
|
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from typing import List
|
from typing import List
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
@ -11,8 +10,7 @@ from teal.resource import View
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer
|
from ereuse_devicehub.resources.device.models import Component, Computer
|
||||||
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
||||||
from ereuse_devicehub.resources.event.models import Event, Snapshot, Rate, RateComputer, InvalidRangeForPrice, \
|
from ereuse_devicehub.resources.event.models import Event, RateComputer, Snapshot
|
||||||
EreusePrice
|
|
||||||
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import CannotRate
|
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import CannotRate
|
||||||
|
|
||||||
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
||||||
|
@ -94,7 +92,6 @@ class EventView(View):
|
||||||
if price:
|
if price:
|
||||||
snapshot.events.add(price)
|
snapshot.events.add(price)
|
||||||
|
|
||||||
|
|
||||||
db.session.add(snapshot)
|
db.session.add(snapshot)
|
||||||
db.session().final_flush()
|
db.session().final_flush()
|
||||||
ret = self.schema.jsonify(snapshot) # transform it back
|
ret = self.schema.jsonify(snapshot) # transform it back
|
||||||
|
|
|
@ -1,109 +1,115 @@
|
||||||
CREATE OR REPLACE FUNCTION add_edge(parent_id uuid, child_id uuid)
|
CREATE OR REPLACE FUNCTION add_edge(parent_id uuid, child_id uuid)
|
||||||
/* Adds an edge between ``parent`` and ``child``.
|
/* Adds an edge between ``parent`` and ``child``.
|
||||||
|
|
||||||
Designed to work with Directed Acyclic Graphs (DAG)
|
Designed to work with Directed Acyclic Graphs (DAG)
|
||||||
(or said in another way, trees with multiple parents without cycles).
|
(or said in another way, trees with multiple parents without cycles).
|
||||||
|
|
||||||
This method will raise an exception if:
|
This method will raise an exception if:
|
||||||
- Parent is the same as child.
|
- Parent is the same as child.
|
||||||
- Child contains the parent.
|
- Child contains the parent.
|
||||||
- Edge parent - child already exists.
|
- Edge parent - child already exists.
|
||||||
|
|
||||||
Influenced by:
|
Influenced by:
|
||||||
- https://www.codeproject.com/Articles/22824/A-Model-to-Represent-Directed-Acyclic-Graphs-DAG
|
- https://www.codeproject.com/Articles/22824/A-Model-to-Represent-Directed-Acyclic-Graphs-DAG
|
||||||
- http://patshaughnessy.net/2017/12/12/installing-the-postgres-ltree-extension
|
- http://patshaughnessy.net/2017/12/12/installing-the-postgres-ltree-extension
|
||||||
- https://en.wikipedia.org/wiki/Directed_acyclic_graph
|
- https://en.wikipedia.org/wiki/Directed_acyclic_graph
|
||||||
*/
|
*/
|
||||||
RETURNS void AS $$
|
RETURNS void AS
|
||||||
|
$$
|
||||||
DECLARE
|
DECLARE
|
||||||
parent text := replace(CAST(parent_id as text), '-', '_');
|
parent text := replace(CAST(parent_id as text), '-', '_');
|
||||||
child text := replace(CAST(child_id as text), '-', '_');
|
child text := replace(CAST(child_id as text), '-', '_');
|
||||||
BEGIN
|
BEGIN
|
||||||
if parent = child
|
if parent = child
|
||||||
then
|
then
|
||||||
raise exception 'Cannot create edge: the parent is the same as the child.';
|
raise exception 'Cannot create edge: the parent is the same as the child.';
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
if EXISTS (
|
if EXISTS(
|
||||||
SELECT 1 FROM path where path.path ~ CAST('*.' || child || '.*.' || parent || '.*' as lquery)
|
SELECT 1
|
||||||
)
|
FROM path
|
||||||
then
|
where path.path ~ CAST('*.' || child || '.*.' || parent || '.*' as lquery)
|
||||||
raise exception 'Cannot create edge: child already contains parent.';
|
)
|
||||||
end if;
|
then
|
||||||
|
raise exception 'Cannot create edge: child already contains parent.';
|
||||||
|
end if;
|
||||||
|
|
||||||
-- We have two subgraphs: the parent subgraph that goes from the parent to the root,
|
-- We have two subgraphs: the parent subgraph that goes from the parent to the root,
|
||||||
-- and the child subgraph, going from the child (which is the root of this subgraph)
|
-- and the child subgraph, going from the child (which is the root of this subgraph)
|
||||||
-- to all the leafs.
|
-- to all the leafs.
|
||||||
-- We do the cartesian product from all the paths of the parent subgraph that end in the parent
|
-- We do the cartesian product from all the paths of the parent subgraph that end in the parent
|
||||||
-- WITH all the paths that start from the child that end to its leafs.
|
-- WITH all the paths that start from the child that end to its leafs.
|
||||||
insert into path (lot_id, path) (
|
insert into path (lot_id, path) (
|
||||||
select distinct lot_id, fp.path || subpath(path.path, index(path.path, text2ltree(child)))
|
select distinct lot_id, fp.path || subpath(path.path, index(path.path, text2ltree(child)))
|
||||||
from path, (select path.path from path where path.path ~ CAST('*.' || parent AS lquery)) as fp
|
from path,
|
||||||
where path.path ~ CAST('*.' || child || '.*' AS lquery)
|
(select path.path from path where path.path ~ CAST('*.' || parent AS lquery)) as fp
|
||||||
);
|
where path.path ~ CAST('*.' || child || '.*' AS lquery)
|
||||||
-- Cleanup: old paths that start with the child (that where used above in the cartesian product)
|
);
|
||||||
-- have became a subset of the result of the cartesian product, thus being redundant.
|
-- Cleanup: old paths that start with the child (that where used above in the cartesian product)
|
||||||
delete from path where path.path ~ CAST(child || '.*' AS lquery);
|
-- have became a subset of the result of the cartesian product, thus being redundant.
|
||||||
|
delete from path where path.path ~ CAST(child || '.*' AS lquery);
|
||||||
END
|
END
|
||||||
$$
|
$$
|
||||||
LANGUAGE plpgsql;
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION delete_edge(parent_id uuid, child_id uuid)
|
CREATE OR REPLACE FUNCTION delete_edge(parent_id uuid, child_id uuid)
|
||||||
/* Deletes an edge between ``parent`` and ``child``.
|
/* Deletes an edge between ``parent`` and ``child``.
|
||||||
|
|
||||||
Designed to work with DAG (See ``add_edge`` function).
|
Designed to work with DAG (See ``add_edge`` function).
|
||||||
|
|
||||||
This method will raise an exception if the relationship does not
|
This method will raise an exception if the relationship does not
|
||||||
exist.
|
exist.
|
||||||
*/
|
*/
|
||||||
RETURNS void AS $$
|
RETURNS void AS
|
||||||
|
$$
|
||||||
DECLARE
|
DECLARE
|
||||||
parent text := replace(CAST(parent_id as text), '-', '_');
|
parent text := replace(CAST(parent_id as text), '-', '_');
|
||||||
child text := replace(CAST(child_id as text), '-', '_');
|
child text := replace(CAST(child_id as text), '-', '_');
|
||||||
number int;
|
number int;
|
||||||
BEGIN
|
BEGIN
|
||||||
-- to delete we remove from the path of the descendants of the child
|
-- to delete we remove from the path of the descendants of the child
|
||||||
-- (and the child) any ancestor coming from this edge.
|
-- (and the child) any ancestor coming from this edge.
|
||||||
-- When we added the edge we did a cartesian product. When removing
|
-- When we added the edge we did a cartesian product. When removing
|
||||||
-- this part of the path we will have duplicate paths.
|
-- this part of the path we will have duplicate paths.
|
||||||
|
|
||||||
-- don't check uniqueness for path key until we delete duplicates
|
-- don't check uniqueness for path key until we delete duplicates
|
||||||
SET CONSTRAINTS path_unique DEFERRED;
|
SET CONSTRAINTS path_unique DEFERRED;
|
||||||
|
|
||||||
-- remove everything above the child lot_id in the path
|
-- remove everything above the child lot_id in the path
|
||||||
-- this creates duplicates on path and lot_id
|
-- this creates duplicates on path and lot_id
|
||||||
update path
|
update path
|
||||||
set path = subpath(path, index(path, text2ltree(child)))
|
set path = subpath(path, index(path, text2ltree(child)))
|
||||||
where path ~ CAST('*.' || parent || '.' || child || '.*' AS lquery);
|
where path ~ CAST('*.' || parent || '.' || child || '.*' AS lquery);
|
||||||
|
|
||||||
-- remove duplicates
|
-- remove duplicates
|
||||||
-- we need an id field exclusively for this operation
|
-- we need an id field exclusively for this operation
|
||||||
-- from https://wiki.postgresql.org/wiki/Deleting_duplicates
|
-- from https://wiki.postgresql.org/wiki/Deleting_duplicates
|
||||||
DELETE
|
DELETE
|
||||||
FROM path
|
FROM path
|
||||||
WHERE id IN (SELECT id
|
WHERE id IN (SELECT id
|
||||||
FROM (SELECT id, ROW_NUMBER() OVER (partition BY lot_id, path) AS rnum FROM path) t
|
FROM (SELECT id, ROW_NUMBER() OVER (partition BY lot_id, path) AS rnum
|
||||||
WHERE t.rnum > 1);
|
FROM path) t
|
||||||
|
WHERE t.rnum > 1);
|
||||||
|
|
||||||
-- re-activate uniqueness check and perform check
|
-- re-activate uniqueness check and perform check
|
||||||
-- todo we should put this in a kind-of finally clause
|
-- todo we should put this in a kind-of finally clause
|
||||||
SET CONSTRAINTS path_unique IMMEDIATE;
|
SET CONSTRAINTS path_unique IMMEDIATE;
|
||||||
|
|
||||||
-- After the update the one of the paths of the child will be
|
-- After the update the one of the paths of the child will be
|
||||||
-- containing only the child.
|
-- containing only the child.
|
||||||
-- This can only be when the child has no parent at all.
|
-- This can only be when the child has no parent at all.
|
||||||
-- In case the child has more than one parent, remove this path
|
-- In case the child has more than one parent, remove this path
|
||||||
-- (note that we want it to remove it too from descendants of this
|
-- (note that we want it to remove it too from descendants of this
|
||||||
-- child, ex. 'child_id'.'desc1')
|
-- child, ex. 'child_id'.'desc1')
|
||||||
select COUNT(1) into number from path where lot_id = child_id;
|
select COUNT(1) into number from path where lot_id = child_id;
|
||||||
IF number > 1
|
IF number > 1
|
||||||
THEN
|
THEN
|
||||||
delete from path where path <@ text2ltree(child);
|
delete from path where path <@ text2ltree(child);
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
END
|
END
|
||||||
$$
|
$$
|
||||||
LANGUAGE plpgsql;
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from sqlalchemy.util import OrderedSet
|
|
||||||
from marshmallow.fields import Boolean
|
from marshmallow.fields import Boolean
|
||||||
|
from sqlalchemy.util import OrderedSet
|
||||||
from teal.marshmallow import SanitizedStr, URL
|
from teal.marshmallow import SanitizedStr, URL
|
||||||
|
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "A",
|
"appearanceRange": "A",
|
||||||
"functionalityRange": "A",
|
"functionalityRange": "A",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"manufacturer": "ASUSTeK Computer INC.",
|
"manufacturer": "ASUSTeK Computer INC.",
|
||||||
|
|
|
@ -10,7 +10,7 @@ device:
|
||||||
model: d1ml
|
model: d1ml
|
||||||
manufacturer: d1mr
|
manufacturer: d1mr
|
||||||
events:
|
events:
|
||||||
- type: TestVisual
|
- type: VisualTest
|
||||||
appearanceRange: A
|
appearanceRange: A
|
||||||
functionalityRange: B
|
functionalityRange: B
|
||||||
components:
|
components:
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "D",
|
"appearanceRange": "D",
|
||||||
"functionalityRange": "D",
|
"functionalityRange": "D",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"elapsed": 300,
|
"elapsed": 300,
|
||||||
|
|
|
@ -137,7 +137,7 @@
|
||||||
{
|
{
|
||||||
"appearanceRange": "A",
|
"appearanceRange": "A",
|
||||||
"functionalityRange": "A",
|
"functionalityRange": "A",
|
||||||
"type": "TestVisual"
|
"type": "VisualTest"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"elapsed": 300,
|
"elapsed": 300,
|
||||||
|
|
|
@ -42,4 +42,4 @@ def test_api_docs(client: Client):
|
||||||
'scheme': 'basic',
|
'scheme': 'basic',
|
||||||
'name': 'Authorization'
|
'name': 'Authorization'
|
||||||
}
|
}
|
||||||
assert len(docs['definitions']) == 102
|
assert len(docs['definitions']) == 101
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import datetime
|
import datetime
|
||||||
from datetime import timedelta
|
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -23,7 +22,7 @@ from ereuse_devicehub.resources.device.sync import MismatchBetweenTags, Mismatch
|
||||||
from ereuse_devicehub.resources.enums import ComputerChassis, DisplayTech, Severity, \
|
from ereuse_devicehub.resources.enums import ComputerChassis, DisplayTech, Severity, \
|
||||||
SnapshotSoftware
|
SnapshotSoftware
|
||||||
from ereuse_devicehub.resources.event import models as m
|
from ereuse_devicehub.resources.event import models as m
|
||||||
from ereuse_devicehub.resources.event.models import Remove, Test
|
from ereuse_devicehub.resources.event.models import Remove, TestConnectivity
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
from ereuse_devicehub.resources.user import User
|
from ereuse_devicehub.resources.user import User
|
||||||
from tests import conftest
|
from tests import conftest
|
||||||
|
@ -391,14 +390,14 @@ def test_get_device(app: Devicehub, user: UserClient):
|
||||||
])
|
])
|
||||||
db.session.add(pc)
|
db.session.add(pc)
|
||||||
# todo test is an abstract class. replace with another one
|
# todo test is an abstract class. replace with another one
|
||||||
db.session.add(Test(device=pc,
|
db.session.add(TestConnectivity(device=pc,
|
||||||
severity=Severity.Info,
|
severity=Severity.Info,
|
||||||
agent=Person(name='Timmy'),
|
agent=Person(name='Timmy'),
|
||||||
author=User(email='bar@bar.com')))
|
author=User(email='bar@bar.com')))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
pc, _ = user.get(res=d.Device, item=1)
|
pc, _ = user.get(res=d.Device, item=1)
|
||||||
assert len(pc['events']) == 1
|
assert len(pc['events']) == 1
|
||||||
assert pc['events'][0]['type'] == 'Test'
|
assert pc['events'][0]['type'] == 'TestConnectivity'
|
||||||
assert pc['events'][0]['device'] == 1
|
assert pc['events'][0]['device'] == 1
|
||||||
assert pc['events'][0]['severity'] == 'Info'
|
assert pc['events'][0]['severity'] == 'Info'
|
||||||
assert UUID(pc['events'][0]['author'])
|
assert UUID(pc['events'][0]['author'])
|
||||||
|
|
|
@ -362,7 +362,6 @@ def test_manual_rate_after_workbench_rate(user: UserClient):
|
||||||
s = file('real-hp.snapshot.11')
|
s = file('real-hp.snapshot.11')
|
||||||
snapshot, _ = user.post(s, res=models.Snapshot)
|
snapshot, _ = user.post(s, res=models.Snapshot)
|
||||||
device, _ = user.get(res=Device, item=snapshot['device']['id'])
|
device, _ = user.get(res=Device, item=snapshot['device']['id'])
|
||||||
# TODO
|
|
||||||
assert 'B' == device['rate']['appearanceRange']
|
assert 'B' == device['rate']['appearanceRange']
|
||||||
assert device['rate'] == 1
|
assert device['rate'] == 1
|
||||||
user.post({
|
user.post({
|
||||||
|
@ -378,3 +377,27 @@ def test_manual_rate_after_workbench_rate(user: UserClient):
|
||||||
@pytest.mark.xfail(reson='Develop an algorithm that can make rates only from manual rates')
|
@pytest.mark.xfail(reson='Develop an algorithm that can make rates only from manual rates')
|
||||||
def test_manual_rate_without_workbench_rate(user: UserClient):
|
def test_manual_rate_without_workbench_rate(user: UserClient):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reson='develop')
|
||||||
|
def test_measure_battery():
|
||||||
|
"""Tests the MeasureBattery."""
|
||||||
|
# todo jn
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reson='develop')
|
||||||
|
def test_test_camera():
|
||||||
|
"""Tests the TestCamera."""
|
||||||
|
# todo jn
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reson='develop')
|
||||||
|
def test_test_keyboard():
|
||||||
|
"""Tests the TestKeyboard."""
|
||||||
|
# todo jn
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reson='develop')
|
||||||
|
def test_test_trackpad():
|
||||||
|
"""Tests the TestTrackpad."""
|
||||||
|
# todo jn
|
||||||
|
|
|
@ -9,8 +9,8 @@ from ereuse_devicehub.resources.device.models import Computer, Desktop, HardDriv
|
||||||
RamModule
|
RamModule
|
||||||
from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, \
|
from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, \
|
||||||
FunctionalityRange
|
FunctionalityRange
|
||||||
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, \
|
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
||||||
BenchmarkProcessor, RateComputer, TestVisual, Snapshot
|
RateComputer, Snapshot, VisualTest
|
||||||
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import CannotRate
|
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import CannotRate
|
||||||
from tests import conftest
|
from tests import conftest
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
@ -28,16 +28,11 @@ def test_workbench_rate_db():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='AggreagteRate only takes data from WorkbenchRate as for now')
|
@pytest.mark.xfail(reason='ComputerRate V1 can only be triggered from Workbench snapshot software')
|
||||||
def test_rate_workbench_then_manual():
|
def test_rate_workbench_then_manual():
|
||||||
"""Checks that a new Rate is generated with a new rate
|
"""Checks that a new Rate is generated for a snapshot
|
||||||
value when a TestVisual is performed after performing a
|
that is not from Workbench.
|
||||||
RateComputer.
|
|
||||||
|
|
||||||
The new Rate needs to be computed by the values of
|
|
||||||
the appearance and funcitonality grade of TestVisual.
|
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
@ -60,11 +55,9 @@ def test_rate():
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add test visual with functionality and appearance range
|
# Add test visual with functionality and appearance range
|
||||||
visual_test = TestVisual()
|
VisualTest(appearance_range=AppearanceRange.A,
|
||||||
visual_test.appearance_range = AppearanceRange.A
|
functionality_range=FunctionalityRange.A,
|
||||||
visual_test.functionality_range = FunctionalityRange.A
|
device=pc)
|
||||||
|
|
||||||
pc.events_one.add(visual_test)
|
|
||||||
rate, price = RateComputer.compute(pc)
|
rate, price = RateComputer.compute(pc)
|
||||||
|
|
||||||
# events = pc.events
|
# events = pc.events
|
||||||
|
@ -120,3 +113,14 @@ def test_no_rate_if_device_is_not_computer(user: UserClient):
|
||||||
device = file('keyboard.snapshot')
|
device = file('keyboard.snapshot')
|
||||||
user.post(device, res=Snapshot)
|
user.post(device, res=Snapshot)
|
||||||
assert CannotRate
|
assert CannotRate
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reason='Test not developed')
|
||||||
|
def test_multiple_rates(user: UserClient):
|
||||||
|
"""Tests submitting two rates from Workbench,
|
||||||
|
ensuring that the tests / benchmarks...
|
||||||
|
from the first rate do not contaminate the second rate.
|
||||||
|
|
||||||
|
This ensures that rates only takes the last version of events
|
||||||
|
and components (in case device has new components, for example).
|
||||||
|
"""
|
||||||
|
|
|
@ -18,7 +18,7 @@ import pytest
|
||||||
from ereuse_devicehub.resources.device.models import Desktop, HardDrive, Processor, RamModule
|
from ereuse_devicehub.resources.device.models import Desktop, HardDrive, Processor, RamModule
|
||||||
from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, FunctionalityRange
|
from ereuse_devicehub.resources.enums import AppearanceRange, ComputerChassis, FunctionalityRange
|
||||||
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
||||||
RateComputer, TestVisual
|
VisualTest
|
||||||
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import DataStorageRate, ProcessorRate, \
|
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import DataStorageRate, ProcessorRate, \
|
||||||
RamRate, RateAlgorithm
|
RamRate, RateAlgorithm
|
||||||
|
|
||||||
|
@ -32,31 +32,31 @@ def test_rate_data_storage_rate():
|
||||||
hdd_1969 = HardDrive(size=476940)
|
hdd_1969 = HardDrive(size=476940)
|
||||||
hdd_1969.events_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8))
|
hdd_1969.events_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8))
|
||||||
|
|
||||||
data_storage_rate = DataStorageRate().compute([hdd_1969], RateComputer())
|
data_storage_rate = DataStorageRate().compute([hdd_1969])
|
||||||
|
|
||||||
assert math.isclose(data_storage_rate, 4.02, rel_tol=0.001), 'DataStorageRate returns incorrect value(rate)'
|
assert math.isclose(data_storage_rate, 4.02, rel_tol=0.001)
|
||||||
|
|
||||||
hdd_3054 = HardDrive(size=476940)
|
hdd_3054 = HardDrive(size=476940)
|
||||||
hdd_3054.events_one.add(BenchmarkDataStorage(read_speed=158, write_speed=34.7))
|
hdd_3054.events_one.add(BenchmarkDataStorage(read_speed=158, write_speed=34.7))
|
||||||
|
|
||||||
# calculate DataStorage Rate
|
# calculate DataStorage Rate
|
||||||
data_storage_rate = DataStorageRate().compute([hdd_3054], RateComputer())
|
data_storage_rate = DataStorageRate().compute([hdd_3054])
|
||||||
|
|
||||||
assert math.isclose(data_storage_rate, 4.07, rel_tol=0.001), 'DataStorageRate returns incorrect value(rate)'
|
assert math.isclose(data_storage_rate, 4.07, rel_tol=0.001)
|
||||||
|
|
||||||
hdd_81 = HardDrive(size=76319)
|
hdd_81 = HardDrive(size=76319)
|
||||||
hdd_81.events_one.add(BenchmarkDataStorage(read_speed=72.2, write_speed=24.3))
|
hdd_81.events_one.add(BenchmarkDataStorage(read_speed=72.2, write_speed=24.3))
|
||||||
|
|
||||||
data_storage_rate = DataStorageRate().compute([hdd_81], RateComputer())
|
data_storage_rate = DataStorageRate().compute([hdd_81])
|
||||||
|
|
||||||
assert math.isclose(data_storage_rate, 2.61, rel_tol=0.001), 'DataStorageRate returns incorrect value(rate)'
|
assert math.isclose(data_storage_rate, 2.61, rel_tol=0.001)
|
||||||
|
|
||||||
hdd_1556 = HardDrive(size=152587)
|
hdd_1556 = HardDrive(size=152587)
|
||||||
hdd_1556.events_one.add(BenchmarkDataStorage(read_speed=78.1, write_speed=24.4))
|
hdd_1556.events_one.add(BenchmarkDataStorage(read_speed=78.1, write_speed=24.4))
|
||||||
|
|
||||||
data_storage_rate = DataStorageRate().compute([hdd_1556], RateComputer())
|
data_storage_rate = DataStorageRate().compute([hdd_1556])
|
||||||
|
|
||||||
assert math.isclose(data_storage_rate, 3.70, rel_tol=0.001), 'DataStorageRate returns incorrect value(rate)'
|
assert math.isclose(data_storage_rate, 3.70, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
def test_rate_data_storage_size_is_null():
|
def test_rate_data_storage_size_is_null():
|
||||||
|
@ -69,7 +69,7 @@ def test_rate_data_storage_size_is_null():
|
||||||
hdd_null = HardDrive(size=None)
|
hdd_null = HardDrive(size=None)
|
||||||
hdd_null.events_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0))
|
hdd_null.events_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0))
|
||||||
|
|
||||||
data_storage_rate = DataStorageRate().compute([hdd_null], RateComputer())
|
data_storage_rate = DataStorageRate().compute([hdd_null])
|
||||||
assert data_storage_rate is None
|
assert data_storage_rate is None
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ def test_rate_no_data_storage():
|
||||||
"""
|
"""
|
||||||
hdd_null = HardDrive()
|
hdd_null = HardDrive()
|
||||||
hdd_null.events_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0))
|
hdd_null.events_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0))
|
||||||
data_storage_rate = DataStorageRate().compute([hdd_null], RateComputer())
|
data_storage_rate = DataStorageRate().compute([hdd_null])
|
||||||
assert data_storage_rate is None
|
assert data_storage_rate is None
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ def test_rate_ram_rate():
|
||||||
|
|
||||||
ram1 = RamModule(size=2048, speed=1333)
|
ram1 = RamModule(size=2048, speed=1333)
|
||||||
|
|
||||||
ram_rate = RamRate().compute([ram1], RateComputer())
|
ram_rate = RamRate().compute([ram1])
|
||||||
|
|
||||||
# todo rel_tol >= 0.002
|
# todo rel_tol >= 0.002
|
||||||
assert math.isclose(ram_rate, 2.02, rel_tol=0.002), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 2.02, rel_tol=0.002), 'RamRate returns incorrect value(rate)'
|
||||||
|
@ -109,7 +109,7 @@ def test_rate_ram_rate_2modules():
|
||||||
ram1 = RamModule(size=4096, speed=1600)
|
ram1 = RamModule(size=4096, speed=1600)
|
||||||
ram2 = RamModule(size=2048, speed=1067)
|
ram2 = RamModule(size=2048, speed=1067)
|
||||||
|
|
||||||
ram_rate = RamRate().compute([ram1, ram2], RateComputer())
|
ram_rate = RamRate().compute([ram1, ram2])
|
||||||
|
|
||||||
assert math.isclose(ram_rate, 3.79, rel_tol=0.001), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 3.79, rel_tol=0.001), 'RamRate returns incorrect value(rate)'
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ def test_rate_ram_rate_4modules():
|
||||||
ram3 = RamModule(size=512, speed=667)
|
ram3 = RamModule(size=512, speed=667)
|
||||||
ram4 = RamModule(size=512, speed=533)
|
ram4 = RamModule(size=512, speed=533)
|
||||||
|
|
||||||
ram_rate = RamRate().compute([ram1, ram2, ram3, ram4], RateComputer())
|
ram_rate = RamRate().compute([ram1, ram2, ram3, ram4])
|
||||||
|
|
||||||
# todo rel_tol >= 0.002
|
# todo rel_tol >= 0.002
|
||||||
assert math.isclose(ram_rate, 1.993, rel_tol=0.001), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 1.993, rel_tol=0.001), 'RamRate returns incorrect value(rate)'
|
||||||
|
@ -138,7 +138,7 @@ def test_rate_ram_module_size_is_0():
|
||||||
|
|
||||||
ram0 = RamModule(size=0, speed=888)
|
ram0 = RamModule(size=0, speed=888)
|
||||||
|
|
||||||
ram_rate = RamRate().compute([ram0], RateComputer())
|
ram_rate = RamRate().compute([ram0])
|
||||||
assert ram_rate is None
|
assert ram_rate is None
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,13 +150,13 @@ def test_rate_ram_speed_is_null():
|
||||||
|
|
||||||
ram0 = RamModule(size=2048, speed=None)
|
ram0 = RamModule(size=2048, speed=None)
|
||||||
|
|
||||||
ram_rate = RamRate().compute([ram0], RateComputer())
|
ram_rate = RamRate().compute([ram0])
|
||||||
|
|
||||||
assert math.isclose(ram_rate, 1.85, rel_tol=0.002), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 1.85, rel_tol=0.002), 'RamRate returns incorrect value(rate)'
|
||||||
|
|
||||||
ram0 = RamModule(size=1024, speed=None)
|
ram0 = RamModule(size=1024, speed=None)
|
||||||
|
|
||||||
ram_rate = RamRate().compute([ram0], RateComputer())
|
ram_rate = RamRate().compute([ram0])
|
||||||
|
|
||||||
# todo rel_tol >= 0.004
|
# todo rel_tol >= 0.004
|
||||||
assert math.isclose(ram_rate, 1.25, rel_tol=0.004), 'RamRate returns incorrect value(rate)'
|
assert math.isclose(ram_rate, 1.25, rel_tol=0.004), 'RamRate returns incorrect value(rate)'
|
||||||
|
@ -168,7 +168,7 @@ def test_rate_no_ram_module():
|
||||||
"""
|
"""
|
||||||
ram0 = RamModule()
|
ram0 = RamModule()
|
||||||
|
|
||||||
ram_rate = RamRate().compute([ram0], RateComputer())
|
ram_rate = RamRate().compute([ram0])
|
||||||
assert ram_rate is None
|
assert ram_rate is None
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,9 +184,9 @@ def test_rate_processor_rate():
|
||||||
# add score processor benchmark
|
# add score processor benchmark
|
||||||
cpu.events_one.add(BenchmarkProcessor(rate=3192.34))
|
cpu.events_one.add(BenchmarkProcessor(rate=3192.34))
|
||||||
|
|
||||||
processor_rate = ProcessorRate().compute(cpu, RateComputer())
|
processor_rate = ProcessorRate().compute(cpu)
|
||||||
|
|
||||||
assert math.isclose(processor_rate, 1, rel_tol=0.001), 'ProcessorRate returns incorrect value(rate)'
|
assert math.isclose(processor_rate, 1, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
def test_rate_processor_rate_2cores():
|
def test_rate_processor_rate_2cores():
|
||||||
|
@ -199,17 +199,17 @@ def test_rate_processor_rate_2cores():
|
||||||
# add score processor benchmark
|
# add score processor benchmark
|
||||||
cpu.events_one.add(BenchmarkProcessor(rate=27136.44))
|
cpu.events_one.add(BenchmarkProcessor(rate=27136.44))
|
||||||
|
|
||||||
processor_rate = ProcessorRate().compute(cpu, RateComputer())
|
processor_rate = ProcessorRate().compute(cpu)
|
||||||
|
|
||||||
assert math.isclose(processor_rate, 3.95, rel_tol=0.001), 'ProcessorRate returns incorrect value(rate)'
|
assert math.isclose(processor_rate, 3.95, rel_tol=0.001)
|
||||||
|
|
||||||
cpu = Processor(cores=2, speed=3.3)
|
cpu = Processor(cores=2, speed=3.3)
|
||||||
cpu.events_one.add(BenchmarkProcessor(rate=26339.48))
|
cpu.events_one.add(BenchmarkProcessor(rate=26339.48))
|
||||||
|
|
||||||
processor_rate = ProcessorRate().compute(cpu, RateComputer())
|
processor_rate = ProcessorRate().compute(cpu)
|
||||||
|
|
||||||
# todo rel_tol >= 0.002
|
# todo rel_tol >= 0.002
|
||||||
assert math.isclose(processor_rate, 3.93, rel_tol=0.002), 'ProcessorRate returns incorrect value(rate)'
|
assert math.isclose(processor_rate, 3.93, rel_tol=0.002)
|
||||||
|
|
||||||
|
|
||||||
def test_rate_processor_with_null_cores():
|
def test_rate_processor_with_null_cores():
|
||||||
|
@ -220,10 +220,10 @@ def test_rate_processor_with_null_cores():
|
||||||
# todo try without BenchmarkProcessor, StopIteration problem
|
# todo try without BenchmarkProcessor, StopIteration problem
|
||||||
cpu.events_one.add(BenchmarkProcessor())
|
cpu.events_one.add(BenchmarkProcessor())
|
||||||
|
|
||||||
processor_rate = ProcessorRate().compute(cpu, RateComputer())
|
processor_rate = ProcessorRate().compute(cpu)
|
||||||
|
|
||||||
# todo rel_tol >= 0.003
|
# todo rel_tol >= 0.003
|
||||||
assert math.isclose(processor_rate, 1.38, rel_tol=0.003), 'ProcessorRate returns incorrect value(rate)'
|
assert math.isclose(processor_rate, 1.38, rel_tol=0.003)
|
||||||
|
|
||||||
|
|
||||||
def test_rate_processor_with_null_speed():
|
def test_rate_processor_with_null_speed():
|
||||||
|
@ -233,9 +233,9 @@ def test_rate_processor_with_null_speed():
|
||||||
cpu = Processor(cores=1, speed=None)
|
cpu = Processor(cores=1, speed=None)
|
||||||
cpu.events_one.add(BenchmarkProcessor(rate=0))
|
cpu.events_one.add(BenchmarkProcessor(rate=0))
|
||||||
|
|
||||||
processor_rate = ProcessorRate().compute(cpu, RateComputer())
|
processor_rate = ProcessorRate().compute(cpu)
|
||||||
|
|
||||||
assert math.isclose(processor_rate, 1.06, rel_tol=0.001), 'ProcessorRate returns incorrect value(rate)'
|
assert math.isclose(processor_rate, 1.06, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
def test_rate_computer_rate():
|
def test_rate_computer_rate():
|
||||||
|
@ -324,11 +324,9 @@ def test_rate_computer_rate():
|
||||||
cpu
|
cpu
|
||||||
}
|
}
|
||||||
# Add test visual with functionality and appearance range
|
# Add test visual with functionality and appearance range
|
||||||
visual_test = TestVisual()
|
VisualTest(appearance_range=AppearanceRange.A,
|
||||||
visual_test.appearance_range = AppearanceRange.A
|
functionality_range=FunctionalityRange.A,
|
||||||
visual_test.functionality_range = FunctionalityRange.A
|
device=pc_test)
|
||||||
|
|
||||||
pc_test.events_one.add(visual_test)
|
|
||||||
|
|
||||||
# Compute all components rates and general rating
|
# Compute all components rates and general rating
|
||||||
rate_pc = RateAlgorithm().compute(pc_test)
|
rate_pc = RateAlgorithm().compute(pc_test)
|
||||||
|
@ -353,10 +351,9 @@ def test_rate_computer_rate():
|
||||||
cpu
|
cpu
|
||||||
}
|
}
|
||||||
# Add test visual with functionality and appearance range
|
# Add test visual with functionality and appearance range
|
||||||
visual_test = TestVisual()
|
VisualTest(appearance_range=AppearanceRange.B,
|
||||||
visual_test.appearance_range = AppearanceRange.B
|
functionality_range=FunctionalityRange.A,
|
||||||
visual_test.functionality_range = FunctionalityRange.A
|
device=pc_test)
|
||||||
pc_test.events_one.add(visual_test)
|
|
||||||
|
|
||||||
# Compute all components rates and general rating
|
# Compute all components rates and general rating
|
||||||
rate_pc = RateAlgorithm().compute(pc_test)
|
rate_pc = RateAlgorithm().compute(pc_test)
|
||||||
|
@ -384,12 +381,9 @@ def test_rate_computer_rate():
|
||||||
cpu
|
cpu
|
||||||
}
|
}
|
||||||
# Add test visual with functionality and appearance range
|
# Add test visual with functionality and appearance range
|
||||||
visual_test = TestVisual()
|
VisualTest(appearance_range=AppearanceRange.C,
|
||||||
visual_test.appearance_range = AppearanceRange.C
|
functionality_range=FunctionalityRange.A,
|
||||||
visual_test.functionality_range = FunctionalityRange.A
|
device=pc_test)
|
||||||
|
|
||||||
pc_test.events_one.add(visual_test)
|
|
||||||
|
|
||||||
# Compute all components rates and general rating
|
# Compute all components rates and general rating
|
||||||
rate_pc = RateAlgorithm().compute(pc_test)
|
rate_pc = RateAlgorithm().compute(pc_test)
|
||||||
|
|
||||||
|
@ -399,7 +393,7 @@ def test_rate_computer_rate():
|
||||||
|
|
||||||
assert math.isclose(rate_pc.processor, 1, rel_tol=0.001)
|
assert math.isclose(rate_pc.processor, 1, rel_tol=0.001)
|
||||||
|
|
||||||
assert math.isclose(rate_pc.rating, 1.58, rel_tol=0.001)
|
assert rate_pc.rating == 1.57
|
||||||
|
|
||||||
# Create a new Computer with components characteristics of pc with id = 798
|
# Create a new Computer with components characteristics of pc with id = 798
|
||||||
pc_test = Desktop(chassis=ComputerChassis.Tower)
|
pc_test = Desktop(chassis=ComputerChassis.Tower)
|
||||||
|
@ -413,11 +407,9 @@ def test_rate_computer_rate():
|
||||||
cpu
|
cpu
|
||||||
}
|
}
|
||||||
# Add test visual with functionality and appearance range
|
# Add test visual with functionality and appearance range
|
||||||
visual_test = TestVisual()
|
VisualTest(appearance_range=AppearanceRange.B,
|
||||||
visual_test.appearance_range = AppearanceRange.B
|
functionality_range=FunctionalityRange.A,
|
||||||
visual_test.functionality_range = FunctionalityRange.A
|
device=pc_test)
|
||||||
|
|
||||||
pc_test.events_one.add(visual_test)
|
|
||||||
|
|
||||||
# Compute all components rates and general rating
|
# Compute all components rates and general rating
|
||||||
rate_pc = RateAlgorithm().compute(pc_test)
|
rate_pc = RateAlgorithm().compute(pc_test)
|
||||||
|
|
|
@ -17,8 +17,8 @@ from ereuse_devicehub.resources.device.models import SolidStateDrive
|
||||||
from ereuse_devicehub.resources.device.sync import MismatchBetweenProperties, \
|
from ereuse_devicehub.resources.device.sync import MismatchBetweenProperties, \
|
||||||
MismatchBetweenTagsAndHid
|
MismatchBetweenTagsAndHid
|
||||||
from ereuse_devicehub.resources.enums import ComputerChassis, SnapshotSoftware
|
from ereuse_devicehub.resources.enums import ComputerChassis, SnapshotSoftware
|
||||||
from ereuse_devicehub.resources.event.models import BenchmarkProcessor, \
|
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
||||||
EraseSectors, Event, Snapshot, SnapshotRequest, RateComputer, TestVisual, BenchmarkDataStorage
|
EraseSectors, Event, RateComputer, Snapshot, SnapshotRequest, VisualTest
|
||||||
from ereuse_devicehub.resources.tag import Tag
|
from ereuse_devicehub.resources.tag import Tag
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
from tests.conftest import file
|
from tests.conftest import file
|
||||||
|
@ -70,7 +70,7 @@ def test_snapshot_post(user: UserClient):
|
||||||
snapshot = snapshot_and_check(user, file('basic.snapshot'),
|
snapshot = snapshot_and_check(user, file('basic.snapshot'),
|
||||||
event_types=(
|
event_types=(
|
||||||
BenchmarkProcessor.t,
|
BenchmarkProcessor.t,
|
||||||
TestVisual.t,
|
VisualTest.t,
|
||||||
RateComputer.t
|
RateComputer.t
|
||||||
),
|
),
|
||||||
perform_second_snapshot=False)
|
perform_second_snapshot=False)
|
||||||
|
@ -116,15 +116,17 @@ def test_snapshot_component_add_remove(user: UserClient):
|
||||||
# (represented with their S/N) should be:
|
# (represented with their S/N) should be:
|
||||||
# PC 1: p1c1s, p1c2s, p1c3s. PC 2: ø
|
# PC 1: p1c1s, p1c2s, p1c3s. PC 2: ø
|
||||||
s1 = file('1-device-with-components.snapshot')
|
s1 = file('1-device-with-components.snapshot')
|
||||||
# TODO if dont put len([event_types]) = 1 != len(event_types) = 18 is BenchmarkProcessor (str 18 chars); why??
|
snapshot1 = snapshot_and_check(user,
|
||||||
snapshot1 = snapshot_and_check(user, s1, event_types=('BenchmarkProcessor',), perform_second_snapshot=False)
|
s1,
|
||||||
|
event_types=(BenchmarkProcessor.t,),
|
||||||
|
perform_second_snapshot=False)
|
||||||
pc1_id = snapshot1['device']['id']
|
pc1_id = snapshot1['device']['id']
|
||||||
pc1, _ = user.get(res=m.Device, item=pc1_id)
|
pc1, _ = user.get(res=m.Device, item=pc1_id)
|
||||||
# Parent contains components
|
# Parent contains components
|
||||||
assert tuple(c['serialNumber'] for c in pc1['components']) == ('p1c1s', 'p1c2s', 'p1c3s')
|
assert tuple(c['serialNumber'] for c in pc1['components']) == ('p1c1s', 'p1c2s', 'p1c3s')
|
||||||
# Components contain parent
|
# Components contain parent
|
||||||
assert all(c['parent'] == pc1_id for c in pc1['components'])
|
assert all(c['parent'] == pc1_id for c in pc1['components'])
|
||||||
# pc has Snapshot as event
|
# pc has two events: Snapshot and the BenchmarkProcessor
|
||||||
# TODO change assert to len(pc1['events']) == 2 cause we add BenchmarkProcessor event
|
# TODO change assert to len(pc1['events']) == 2 cause we add BenchmarkProcessor event
|
||||||
assert len(pc1['events']) == 2
|
assert len(pc1['events']) == 2
|
||||||
# TODO pc1['events'][0]['type'] == BenchmarkProcessor.t
|
# TODO pc1['events'][0]['type'] == BenchmarkProcessor.t
|
||||||
|
@ -154,7 +156,8 @@ def test_snapshot_component_add_remove(user: UserClient):
|
||||||
assert tuple(e['type'] for e in pc2['events']) == ('Snapshot',)
|
assert tuple(e['type'] for e in pc2['events']) == ('Snapshot',)
|
||||||
# p1c2s has two Snapshots, a Remove and an Add
|
# p1c2s has two Snapshots, a Remove and an Add
|
||||||
p1c2s, _ = user.get(res=m.Device, item=pc2['components'][0]['id'])
|
p1c2s, _ = user.get(res=m.Device, item=pc2['components'][0]['id'])
|
||||||
assert tuple(e['type'] for e in p1c2s['events']) == ('BenchmarkProcessor', 'Snapshot', 'Snapshot', 'Remove')
|
assert tuple(e['type'] for e in p1c2s['events']) == (
|
||||||
|
'BenchmarkProcessor', 'Snapshot', 'Snapshot', 'Remove')
|
||||||
|
|
||||||
# We register the first device again, but removing motherboard
|
# We register the first device again, but removing motherboard
|
||||||
# and moving processor from the second device to the first.
|
# and moving processor from the second device to the first.
|
||||||
|
@ -243,11 +246,7 @@ def test_snapshot_tag_inner_tag(tag_id: str, user: UserClient, app: Devicehub):
|
||||||
b['device']['tags'] = [{'type': 'Tag', 'id': tag_id}]
|
b['device']['tags'] = [{'type': 'Tag', 'id': tag_id}]
|
||||||
|
|
||||||
snapshot_and_check(user, b,
|
snapshot_and_check(user, b,
|
||||||
event_types=(
|
event_types=(RateComputer.t, BenchmarkProcessor.t, VisualTest.t))
|
||||||
RateComputer.t,
|
|
||||||
BenchmarkProcessor.t,
|
|
||||||
TestVisual.t
|
|
||||||
), perform_second_snapshot=False)
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
tag = Tag.query.one() # type: Tag
|
tag = Tag.query.one() # type: Tag
|
||||||
assert tag.device_id == 1, 'Tag should be linked to the first device'
|
assert tag.device_id == 1, 'Tag should be linked to the first device'
|
||||||
|
@ -315,15 +314,17 @@ def test_erase_privacy_standards(user: UserClient):
|
||||||
EraseSectors.t,
|
EraseSectors.t,
|
||||||
BenchmarkDataStorage.t,
|
BenchmarkDataStorage.t,
|
||||||
BenchmarkProcessor.t
|
BenchmarkProcessor.t
|
||||||
), perform_second_snapshot=True)
|
))
|
||||||
erase = next(e for e in snapshot['events'] if e['type'] == EraseSectors.t)
|
erase = next(e for e in snapshot['events'] if e['type'] == EraseSectors.t)
|
||||||
assert '2018-06-01T07:12:06+00:00' == erase['endTime']
|
assert '2018-06-01T07:12:06+00:00' == erase['endTime']
|
||||||
storage = next(e for e in snapshot['components'] if e['type'] == SolidStateDrive.t)
|
storage = next(e for e in snapshot['components'] if e['type'] == SolidStateDrive.t)
|
||||||
storage, _ = user.get(res=m.Device, item=storage['id']) # Let's get storage events too
|
storage, _ = user.get(res=m.Device, item=storage['id']) # Let's get storage events too
|
||||||
# order: creation time ascending
|
# order: creation time ascending
|
||||||
erasure1, benchmark_data_storage1, _snapshot1, erasure2, benchmark_data_storage2, _snapshot2 = storage['events']
|
erasure1, benchmark_data_storage1, _snapshot1, erasure2, benchmark_data_storage2, _snapshot2 = \
|
||||||
|
storage['events']
|
||||||
assert erasure1['type'] == erasure2['type'] == 'EraseSectors'
|
assert erasure1['type'] == erasure2['type'] == 'EraseSectors'
|
||||||
assert benchmark_data_storage1['type'] == benchmark_data_storage2['type'] == 'BenchmarkDataStorage'
|
assert benchmark_data_storage1['type'] == benchmark_data_storage2[
|
||||||
|
'type'] == 'BenchmarkDataStorage'
|
||||||
assert _snapshot1['type'] == _snapshot2['type'] == 'Snapshot'
|
assert _snapshot1['type'] == _snapshot2['type'] == 'Snapshot'
|
||||||
get_snapshot, _ = user.get(res=Event, item=_snapshot2['id'])
|
get_snapshot, _ = user.get(res=Event, item=_snapshot2['id'])
|
||||||
assert get_snapshot['events'][0]['endTime'] == '2018-06-01T07:12:06+00:00'
|
assert get_snapshot['events'][0]['endTime'] == '2018-06-01T07:12:06+00:00'
|
||||||
|
@ -381,7 +382,7 @@ def test_snapshot_computer_monitor(user: UserClient):
|
||||||
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
||||||
def test_snapshot_mobile_smartphone_imei_manual_rate(user: UserClient):
|
def test_snapshot_mobile_smartphone_imei_manual_rate(user: UserClient):
|
||||||
s = file('smartphone.snapshot')
|
s = file('smartphone.snapshot')
|
||||||
snapshot = snapshot_and_check(user, s, event_types=('TestVisual',))
|
snapshot = snapshot_and_check(user, s, event_types=('VisualTest',))
|
||||||
mobile, _ = user.get(res=m.Device, item=snapshot['device']['id'])
|
mobile, _ = user.get(res=m.Device, item=snapshot['device']['id'])
|
||||||
assert mobile['imei'] == 3568680000414120
|
assert mobile['imei'] == 3568680000414120
|
||||||
# todo check that manual rate has been created
|
# todo check that manual rate has been created
|
||||||
|
@ -429,7 +430,7 @@ def assert_similar_components(components1: List[dict], components2: List[dict]):
|
||||||
|
|
||||||
def snapshot_and_check(user: UserClient,
|
def snapshot_and_check(user: UserClient,
|
||||||
input_snapshot: dict,
|
input_snapshot: dict,
|
||||||
event_types: Tuple[str] = tuple(),
|
event_types: Tuple[str, ...] = tuple(),
|
||||||
perform_second_snapshot=True) -> dict:
|
perform_second_snapshot=True) -> dict:
|
||||||
"""
|
"""
|
||||||
Performs a Snapshot and then checks if the result is ok:
|
Performs a Snapshot and then checks if the result is ok:
|
||||||
|
|
|
@ -36,7 +36,7 @@ def test_workbench_server_condensed(user: UserClient):
|
||||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||||
events = snapshot['events']
|
events = snapshot['events']
|
||||||
assert {(event['type'], event['device']) for event in events} == {
|
assert {(event['type'], event['device']) for event in events} == {
|
||||||
('RateComputer', 1), # Only (RateComputer, 1), delete (Rate, 1)
|
('RateComputer', 1),
|
||||||
('BenchmarkProcessorSysbench', 5),
|
('BenchmarkProcessorSysbench', 5),
|
||||||
('StressTest', 1),
|
('StressTest', 1),
|
||||||
('EraseSectors', 6),
|
('EraseSectors', 6),
|
||||||
|
@ -47,7 +47,7 @@ def test_workbench_server_condensed(user: UserClient):
|
||||||
('BenchmarkDataStorage', 6),
|
('BenchmarkDataStorage', 6),
|
||||||
('BenchmarkDataStorage', 7),
|
('BenchmarkDataStorage', 7),
|
||||||
('TestDataStorage', 6),
|
('TestDataStorage', 6),
|
||||||
('TestVisual', 1)
|
('VisualTest', 1)
|
||||||
}
|
}
|
||||||
assert snapshot['closed']
|
assert snapshot['closed']
|
||||||
assert snapshot['severity'] == 'Info'
|
assert snapshot['severity'] == 'Info'
|
||||||
|
@ -62,11 +62,9 @@ def test_workbench_server_condensed(user: UserClient):
|
||||||
assert device['rate']['closed']
|
assert device['rate']['closed']
|
||||||
assert device['rate']['severity'] == 'Info'
|
assert device['rate']['severity'] == 'Info'
|
||||||
assert device['rate']['rating'] == 0
|
assert device['rate']['rating'] == 0
|
||||||
assert device['rate']['type'] == 'RateComputer' # New in rate v2
|
assert device['rate']['type'] == 'RateComputer'
|
||||||
# new asserts get in TestVisual event info; why change in every execution device['events'][X]
|
assert device['events'][0]['appearanceRange'] == 'A'
|
||||||
# assert device['events'][0]['appearanceRange'] == 'A'
|
assert device['events'][0]['functionalityRange'] == 'B'
|
||||||
# assert device['events'][0]['functionalityRange'] == 'B'
|
|
||||||
# TODO add appearance and functionality Range in device[rate]
|
|
||||||
|
|
||||||
assert device['tags'][0]['id'] == 'tag1'
|
assert device['tags'][0]['id'] == 'tag1'
|
||||||
|
|
||||||
|
@ -154,8 +152,8 @@ def test_real_hp_11(user: UserClient):
|
||||||
'TestDataStorage',
|
'TestDataStorage',
|
||||||
'BenchmarkRamSysbench',
|
'BenchmarkRamSysbench',
|
||||||
'StressTest',
|
'StressTest',
|
||||||
'TestBios', # New in rate v2
|
'TestBios',
|
||||||
'TestVisual' # New in rate v2
|
'VisualTest'
|
||||||
}
|
}
|
||||||
assert len(list(e['type'] for e in snapshot['events'])) == 10
|
assert len(list(e['type'] for e in snapshot['events'])) == 10
|
||||||
assert pc['networkSpeeds'] == [1000, None], 'Device has no WiFi'
|
assert pc['networkSpeeds'] == [1000, None], 'Device has no WiFi'
|
||||||
|
@ -188,7 +186,6 @@ def test_snapshot_real_eee_1001pxd(user: UserClient):
|
||||||
assert pc['networkSpeeds'] == [100, 0], 'Although it has WiFi we do not know the speed'
|
assert pc['networkSpeeds'] == [100, 0], 'Although it has WiFi we do not know the speed'
|
||||||
assert pc['rate']
|
assert pc['rate']
|
||||||
rate = pc['rate']
|
rate = pc['rate']
|
||||||
# new asserts get in TestVisual event info; why change pc['events'][X]
|
|
||||||
# assert pc['events'][0]['appearanceRange'] == 'A'
|
# assert pc['events'][0]['appearanceRange'] == 'A'
|
||||||
# assert pc['events'][0]['functionalityRange'] == 'B'
|
# assert pc['events'][0]['functionalityRange'] == 'B'
|
||||||
# TODO add appearance and functionality Range in device[rate]
|
# TODO add appearance and functionality Range in device[rate]
|
||||||
|
|
Reference in New Issue