Review rate changes

This commit is contained in:
Xavier Bustamante Talavera 2019-05-08 19:12:05 +02:00
parent e6d496872c
commit 16d00256df
40 changed files with 642 additions and 568 deletions

View File

@ -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

View File

@ -164,7 +164,7 @@
{ {
"appearanceRange": "A", "appearanceRange": "A",
"functionalityRange": "A", "functionalityRange": "A",
"type": "TestVisual" "type": "VisualTest"
} }
], ],
"manufacturer": "ASUSTeK Computer INC.", "manufacturer": "ASUSTeK Computer INC.",

View File

@ -16,7 +16,7 @@
{ {
"appearanceRange": "A", "appearanceRange": "A",
"functionalityRange": "A", "functionalityRange": "A",
"type": "TestVisual" "type": "VisualTest"
} }
], ],
"manufacturer": "ASUSTeK Computer INC." "manufacturer": "ASUSTeK Computer INC."

View File

@ -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

View File

@ -16,7 +16,7 @@
{ {
"appearanceRange": "A", "appearanceRange": "A",
"functionalityRange": "A", "functionalityRange": "A",
"type": "TestVisual" "type": "VisualTest"
} }
], ],
"type": "Desktop", "type": "Desktop",

View File

@ -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

View File

@ -18,7 +18,7 @@
{ {
"appearanceRange": "B", "appearanceRange": "B",
"functionalityRange": "C", "functionalityRange": "C",
"type": "TestVisual" "type": "VisualTest"
} }
], ],
"type": "Desktop", "type": "Desktop",

View File

@ -122,7 +122,7 @@
{ {
"appearanceRange": "A", "appearanceRange": "A",
"functionalityRange": "A", "functionalityRange": "A",
"type": "TestVisual" "type": "VisualTest"
} }
], ],
"manufacturer": "Hewlett-Packard", "manufacturer": "Hewlett-Packard",

View File

@ -154,7 +154,7 @@
{ {
"appearanceRange": "B", "appearanceRange": "B",
"functionalityRange": "A", "functionalityRange": "A",
"type": "TestVisual" "type": "VisualTest"
} }
] ]
}, },

View File

@ -157,7 +157,7 @@
{ {
"appearanceRange": "B", "appearanceRange": "B",
"functionalityRange": "D", "functionalityRange": "D",
"type": "TestVisual" "type": "VisualTest"
} }
], ],
"serialNumber": "CZC0408YJG", "serialNumber": "CZC0408YJG",

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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):

View File

@ -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):

View File

@ -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 %}

View File

@ -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):

View File

@ -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)] = ''

View File

@ -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'

View File

@ -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 doesnt 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 doesnt 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; cant stand the minimum duration time' D = 'D. Battery health is bad; cant 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 doesnt exists' NONE = 'NA. Grade doesnt 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):

View File

@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -118,7 +118,7 @@
{ {
"appearanceRange": "A", "appearanceRange": "A",
"functionalityRange": "A", "functionalityRange": "A",
"type": "TestVisual" "type": "VisualTest"
} }
], ],
"manufacturer": "ASUSTeK Computer INC.", "manufacturer": "ASUSTeK Computer INC.",

View File

@ -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:

View File

@ -92,7 +92,7 @@
{ {
"appearanceRange": "D", "appearanceRange": "D",
"functionalityRange": "D", "functionalityRange": "D",
"type": "TestVisual" "type": "VisualTest"
}, },
{ {
"elapsed": 300, "elapsed": 300,

View File

@ -137,7 +137,7 @@
{ {
"appearanceRange": "A", "appearanceRange": "A",
"functionalityRange": "A", "functionalityRange": "A",
"type": "TestVisual" "type": "VisualTest"
}, },
{ {
"elapsed": 300, "elapsed": 300,

View File

@ -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

View File

@ -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'])

View File

@ -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

View File

@ -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).
"""

View File

@ -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)

View File

@ -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:

View File

@ -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]