Add states and quick-access properties on Device; merge in ereuse-rate
This commit is contained in:
parent
319e041ebc
commit
3cc510831c
|
@ -12,6 +12,7 @@ state Attributes {
|
|||
state Usufructuarees
|
||||
state Reservees
|
||||
state "Physical\nPossessor"
|
||||
state "Waste\n\Product"
|
||||
}
|
||||
|
||||
state Physical {
|
||||
|
@ -35,8 +36,17 @@ state Trading {
|
|||
Reserved --> Cancelled : Cancel
|
||||
Sold --> Cancelled : Cancel
|
||||
Sold --> Payed : Pay
|
||||
Registered --> ToBeDisposed
|
||||
ToBeDisposed --> Disposed : DisposeProduct
|
||||
Registered --> ToBeDisposed : ToDisposeProduct
|
||||
ToBeDisposed --> ProductDisposed : DisposeProduct
|
||||
Registered --> Donated: Donate
|
||||
Registered --> Renting: Rent
|
||||
Donated --> Cancelled : Cancel
|
||||
Renting --> Cancelled : Cancel
|
||||
}
|
||||
|
||||
state DataStoragePrivacyCompliance {
|
||||
state Erased
|
||||
state Destroyed
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ device:
|
|||
resolutionHeight: 1080
|
||||
size: 21.5
|
||||
events:
|
||||
- type: AppRate
|
||||
- type: ManualRate
|
||||
appearanceRange: A
|
||||
functionalityRange: C
|
||||
labelling: False
|
||||
|
|
|
@ -8,7 +8,7 @@ device:
|
|||
serialNumber: ABCDEF
|
||||
imei: 35686800-004141-20
|
||||
events:
|
||||
- type: AppRate
|
||||
- type: ManualRate
|
||||
appearanceRange: A
|
||||
functionalityRange: B
|
||||
labelling: False
|
||||
|
|
|
@ -26,6 +26,7 @@ device:
|
|||
functionalityRange: B
|
||||
- type: BenchmarkRamSysbench
|
||||
rate: 2444
|
||||
elapsed: 1
|
||||
components:
|
||||
- type: GraphicCard
|
||||
serialNumber: gc1-1s
|
||||
|
@ -35,22 +36,27 @@ components:
|
|||
serialNumber: rm1-1s
|
||||
model: rm1-1ml
|
||||
manufacturer: rm1-1mr
|
||||
size: 1024
|
||||
- type: RamModule
|
||||
serialNumber: rm2-1s
|
||||
model: rm2-1ml
|
||||
manufacturer: rm2-1mr
|
||||
size: 1024
|
||||
- type: Processor
|
||||
model: p1-1s
|
||||
model: p1-1ml
|
||||
manufacturer: p1-1mr
|
||||
events:
|
||||
- type: BenchmarkProcessor
|
||||
rate: 2410
|
||||
elapsed: 44
|
||||
- type: BenchmarkProcessorSysbench
|
||||
rate: 4400
|
||||
elapsed: 44
|
||||
- type: SolidStateDrive
|
||||
serialNumber: ssd1-1s
|
||||
model: ssd1-1ml
|
||||
manufacturer: ssd1-1mr
|
||||
size: 1100
|
||||
events:
|
||||
- type: BenchmarkDataStorage
|
||||
readSpeed: 20
|
||||
|
@ -78,7 +84,24 @@ components:
|
|||
- type: BenchmarkDataStorage
|
||||
readSpeed: 10
|
||||
writeSpeed: 5
|
||||
elapsed: 20
|
||||
- type: Motherboard
|
||||
serialNumber: mb1-1s
|
||||
model: mb1-1ml
|
||||
manufacturer: mb1-1mr
|
||||
- type: NetworkAdapter
|
||||
serialNumber: na1-s
|
||||
model: na1-1ml
|
||||
manufacturer: na1-1mr
|
||||
speed: 1000
|
||||
wireless: False
|
||||
- type: NetworkAdapter
|
||||
serialNumber: na2-s
|
||||
model: na2-1ml
|
||||
manufacturer: na2-1mr
|
||||
wireless: True
|
||||
speed: 58
|
||||
- type: RamModule
|
||||
serialNumber: rm3-1s
|
||||
model: rm3-1ml
|
||||
manufacturer: rm3-1mr
|
||||
|
|
|
@ -21,8 +21,8 @@ from teal.marshmallow import ValidationError
|
|||
from teal.resource import url_for_resource
|
||||
|
||||
from ereuse_devicehub.db import db
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
|
||||
RamFormat, RamInterface
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \
|
||||
DataStoragePrivacyCompliance, DisplayTech, RamFormat, RamInterface
|
||||
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
||||
|
||||
|
||||
|
@ -44,19 +44,22 @@ class Device(Thing):
|
|||
model = Column(Unicode(), check_lower('model'))
|
||||
manufacturer = Column(Unicode(), check_lower('manufacturer'))
|
||||
serial_number = Column(Unicode(), check_lower('serial_number'))
|
||||
weight = Column(Float(decimal_return_scale=3), check_range('weight', 0.1, 3))
|
||||
weight = Column(Float(decimal_return_scale=3), check_range('weight', 0.1, 5))
|
||||
weight.comment = """
|
||||
The weight of the device in Kgm.
|
||||
"""
|
||||
width = Column(Float(decimal_return_scale=3), check_range('width', 0.1, 3))
|
||||
width = Column(Float(decimal_return_scale=3), check_range('width', 0.1, 5))
|
||||
width.comment = """
|
||||
The width of the device in meters.
|
||||
"""
|
||||
height = Column(Float(decimal_return_scale=3), check_range('height', 0.1, 3))
|
||||
height = Column(Float(decimal_return_scale=3), check_range('height', 0.1, 5))
|
||||
height.comment = """
|
||||
The height of the device in meters.
|
||||
"""
|
||||
depth = Column(Float(decimal_return_scale=3), check_range('depth', 0.1, 3))
|
||||
depth = Column(Float(decimal_return_scale=3), check_range('depth', 0.1, 5))
|
||||
depth.comment = """
|
||||
The depth of the device in meters.
|
||||
"""
|
||||
color = Column(ColorType)
|
||||
color.comment = """The predominant color of the device."""
|
||||
|
||||
|
@ -98,6 +101,54 @@ class Device(Thing):
|
|||
"""The URL where to GET this device."""
|
||||
return urlutils.URL(url_for_resource(Device, item_id=self.id))
|
||||
|
||||
@property
|
||||
def rate(self):
|
||||
"""Gets the last aggregate rate."""
|
||||
with suppress(LookupError, ValueError):
|
||||
from ereuse_devicehub.resources.event.models import AggregateRate
|
||||
return self.last_event_of(AggregateRate)
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
"""Gets the actual Price of the device or None
|
||||
if no price has ever been set."""
|
||||
with suppress(LookupError, ValueError):
|
||||
from ereuse_devicehub.resources.event.models import Price
|
||||
return self.last_event_of(Price)
|
||||
|
||||
@property
|
||||
def trading(self):
|
||||
"""The actual trading state or None if there is no trading info."""
|
||||
from ereuse_devicehub.resources.device import states
|
||||
with suppress(LookupError, ValueError):
|
||||
event = self.last_event_of(*states.Trading.events())
|
||||
return states.Trading(event.__class__)
|
||||
|
||||
@property
|
||||
def physical(self):
|
||||
"""The actual physical state, None if there is no state."""
|
||||
from ereuse_devicehub.resources.device import states
|
||||
with suppress(LookupError, ValueError):
|
||||
event = self.last_event_of(*states.Physical.events())
|
||||
return states.Physical(event.__class__)
|
||||
|
||||
@property
|
||||
def physical_possessor(self):
|
||||
"""The actual physical possessor or None.
|
||||
|
||||
The physical possessor is the Agent that has physically
|
||||
the device. It differs from legal owners, usufructuarees
|
||||
or reserves in that the physical possessor does not have
|
||||
a legal relation per se with the device, but it is the one
|
||||
that has it physically. As an example, a transporter could
|
||||
be a physical possessor of a device although it does not
|
||||
own it legally.
|
||||
"""
|
||||
from ereuse_devicehub.resources.event.models import Receive
|
||||
with suppress(LookupError):
|
||||
event = self.last_event_of(Receive)
|
||||
return event.agent
|
||||
|
||||
@declared_attr
|
||||
def __mapper_args__(cls):
|
||||
"""
|
||||
|
@ -112,6 +163,16 @@ class Device(Thing):
|
|||
args[POLYMORPHIC_ON] = cls.type
|
||||
return args
|
||||
|
||||
def last_event_of(self, *types):
|
||||
"""Gets the last event of the given types.
|
||||
|
||||
:raise LookupError: Device has not an event of the given type.
|
||||
"""
|
||||
try:
|
||||
return next(e for e in reversed(self.events) if isinstance(e, types))
|
||||
except StopIteration:
|
||||
raise LookupError('{!r} does not contain events of types {}.'.format(self, types))
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.id < other.id
|
||||
|
||||
|
@ -169,31 +230,38 @@ class Computer(Device):
|
|||
@property
|
||||
def ram_size(self) -> int:
|
||||
"""The total of RAM memory the computer has."""
|
||||
return sum(ram.size for ram in self.components if isinstance(ram, RamModule))
|
||||
return sum(ram.size or 0 for ram in self.components if isinstance(ram, RamModule))
|
||||
|
||||
@property
|
||||
def data_storage_size(self) -> int:
|
||||
"""The total of data storage the computer has."""
|
||||
return sum(ds.size for ds in self.components if isinstance(ds, DataStorage))
|
||||
return sum(ds.size or 0 for ds in self.components if isinstance(ds, DataStorage))
|
||||
|
||||
@property
|
||||
def processor_model(self) -> str:
|
||||
"""The model of one of the processors of the computer."""
|
||||
return next(p.model for p in self.components if isinstance(p, Processor))
|
||||
return next((p.model for p in self.components if isinstance(p, Processor)), None)
|
||||
|
||||
@property
|
||||
def graphic_card_model(self) -> str:
|
||||
"""The model of one of the graphic cards of the computer."""
|
||||
return next(p.model for p in self.components if isinstance(p, GraphicCard))
|
||||
return next((p.model for p in self.components if isinstance(p, GraphicCard)), None)
|
||||
|
||||
@property
|
||||
def network_speeds(self) -> List[int]:
|
||||
"""Returns two speeds: the first for the eth and the
|
||||
second for the wifi networks, or 0 respectively if not found.
|
||||
"""Returns two values representing the speeds of the network
|
||||
adapters of the device.
|
||||
|
||||
1. The max Ethernet speed of the computer, 0 if ethernet
|
||||
adaptor exists but its speed is unknown, None if no eth
|
||||
adaptor exists.
|
||||
2. The max WiFi speed of the computer, 0 if computer has
|
||||
WiFi but its speed is unknown, None if no WiFi adaptor
|
||||
exists.
|
||||
"""
|
||||
speeds = [0, 0]
|
||||
speeds = [None, None]
|
||||
for net in (c for c in self.components if isinstance(c, NetworkAdapter)):
|
||||
speeds[net.wireless] = max(net.speed or 0, speeds[net.wireless])
|
||||
speeds[net.wireless] = max(net.speed or 0, speeds[net.wireless] or 0)
|
||||
return speeds
|
||||
|
||||
def __format__(self, format_spec):
|
||||
|
@ -322,6 +390,15 @@ class DataStorage(JoinedComponentTableMixin, Component):
|
|||
"""
|
||||
interface = Column(DBEnum(DataStorageInterface))
|
||||
|
||||
@property
|
||||
def privacy(self):
|
||||
"""Returns the privacy compliance state of the data storage."""
|
||||
# todo add physical destruction event
|
||||
from ereuse_devicehub.resources.event.models import EraseBasic
|
||||
with suppress(LookupError):
|
||||
erase = self.last_event_of(EraseBasic)
|
||||
return DataStoragePrivacyCompliance.from_erase(erase)
|
||||
|
||||
def __format__(self, format_spec):
|
||||
v = super().__format__(format_spec)
|
||||
if 's' in format_spec:
|
||||
|
@ -353,7 +430,7 @@ class NetworkMixin:
|
|||
speed.comment = """
|
||||
The maximum speed this network adapter can handle, in mbps.
|
||||
"""
|
||||
wireless = Column(Boolean)
|
||||
wireless = Column(Boolean, nullable=False, default=False)
|
||||
wireless.comment = """
|
||||
Whether it is a wireless interface.
|
||||
"""
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Dict, List, Set
|
||||
from typing import Dict, List, Set, Type, Union
|
||||
|
||||
from boltons import urlutils
|
||||
from boltons.urlutils import URL
|
||||
|
@ -7,10 +7,11 @@ from sqlalchemy import Column, Integer
|
|||
from sqlalchemy.orm import relationship
|
||||
from teal.db import Model
|
||||
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
|
||||
RamFormat, RamInterface
|
||||
from ereuse_devicehub.resources.event.models import Event, EventWithMultipleDevices, \
|
||||
EventWithOneDevice
|
||||
from ereuse_devicehub.resources.agent.models import Agent
|
||||
from ereuse_devicehub.resources.device import states
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \
|
||||
DataStoragePrivacyCompliance, DisplayTech, RamFormat, RamInterface
|
||||
from ereuse_devicehub.resources.event import models as e
|
||||
from ereuse_devicehub.resources.image.models import ImageList
|
||||
from ereuse_devicehub.resources.lot.models import Lot
|
||||
from ereuse_devicehub.resources.models import Thing
|
||||
|
@ -44,10 +45,10 @@ class Device(Thing):
|
|||
self.height = ... # type: float
|
||||
self.depth = ... # type: float
|
||||
self.color = ... # type: Color
|
||||
self.events = ... # type: List[Event]
|
||||
self.events = ... # type: List[e.Event]
|
||||
self.physical_properties = ... # type: Dict[str, object or None]
|
||||
self.events_multiple = ... # type: Set[EventWithMultipleDevices]
|
||||
self.events_one = ... # type: Set[EventWithOneDevice]
|
||||
self.events_multiple = ... # type: Set[e.EventWithMultipleDevices]
|
||||
self.events_one = ... # type: Set[e.EventWithOneDevice]
|
||||
self.images = ... # type: ImageList
|
||||
self.tags = ... # type: Set[Tag]
|
||||
self.lots = ... # type: Set[Lot]
|
||||
|
@ -56,6 +57,30 @@ class Device(Thing):
|
|||
def url(self) -> urlutils.URL:
|
||||
pass
|
||||
|
||||
@property
|
||||
def rate(self) -> Union[e.AggregateRate, None]:
|
||||
pass
|
||||
|
||||
@property
|
||||
def price(self) -> Union[e.Price, None]:
|
||||
pass
|
||||
|
||||
@property
|
||||
def trading(self) -> Union[states.Trading, None]:
|
||||
pass
|
||||
|
||||
@property
|
||||
def physical(self) -> Union[states.Physical, None]:
|
||||
pass
|
||||
|
||||
@property
|
||||
def physical_possessor(self) -> Union[Agent, None]:
|
||||
pass
|
||||
|
||||
def last_event_of(self, *types: Type[e.Event]) -> e.Event:
|
||||
pass
|
||||
|
||||
|
||||
class DisplayMixin:
|
||||
technology = ... # type: Column
|
||||
size = ... # type: Column
|
||||
|
@ -77,7 +102,7 @@ class Computer(DisplayMixin, Device):
|
|||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.components = ... # type: Set[Component]
|
||||
self.events_parent = ... # type: Set[Event]
|
||||
self.events_parent = ... # type: Set[e.Event]
|
||||
self.chassis = ... # type: ComputerChassis
|
||||
|
||||
@property
|
||||
|
@ -104,6 +129,7 @@ class Computer(DisplayMixin, Device):
|
|||
def network_speeds(self) -> List[int]:
|
||||
pass
|
||||
|
||||
|
||||
class Desktop(Computer):
|
||||
pass
|
||||
|
||||
|
@ -155,7 +181,7 @@ class Component(Device):
|
|||
super().__init__(**kwargs)
|
||||
self.parent_id = ... # type: int
|
||||
self.parent = ... # type: Computer
|
||||
self.events_components = ... # type: Set[Event]
|
||||
self.events_components = ... # type: Set[e.Event]
|
||||
|
||||
def similar_one(self, parent: Computer, blacklist: Set[int]) -> 'Component':
|
||||
pass
|
||||
|
@ -178,6 +204,10 @@ class DataStorage(Component):
|
|||
self.size = ... # type: int
|
||||
self.interface = ... # type: DataStorageInterface
|
||||
|
||||
@property
|
||||
def privacy(self) -> DataStoragePrivacyCompliance:
|
||||
pass
|
||||
|
||||
|
||||
class HardDrive(DataStorage):
|
||||
pass
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from marshmallow import post_load, pre_load
|
||||
from marshmallow.fields import Boolean, Float, Integer, Str, String
|
||||
from marshmallow.fields import Boolean, Float, Integer, List, Str, String
|
||||
from marshmallow.validate import Length, OneOf, Range
|
||||
from sqlalchemy.util import OrderedSet
|
||||
from stdnum import imei, meid
|
||||
|
@ -7,9 +7,9 @@ from teal.marshmallow import EnumField, SanitizedStr, URL, ValidationError
|
|||
from teal.resource import Schema
|
||||
|
||||
from ereuse_devicehub.marshmallow import NestedOn
|
||||
from ereuse_devicehub.resources.device import models as m
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
|
||||
RamFormat, RamInterface
|
||||
from ereuse_devicehub.resources.device import models as m, states
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \
|
||||
DataStoragePrivacyCompliance, DisplayTech, RamFormat, RamInterface
|
||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
||||
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
||||
|
||||
|
@ -24,13 +24,19 @@ class Device(Thing):
|
|||
model = SanitizedStr(lower=True, validate=Length(max=STR_BIG_SIZE))
|
||||
manufacturer = SanitizedStr(lower=True, validate=Length(max=STR_SIZE))
|
||||
serial_number = SanitizedStr(lower=True, data_key='serialNumber')
|
||||
weight = Float(validate=Range(0.1, 3), unit=UnitCodes.kgm, description=m.Device.weight.comment)
|
||||
width = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description=m.Device.width.comment)
|
||||
height = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description=m.Device.height.comment)
|
||||
weight = Float(validate=Range(0.1, 5), unit=UnitCodes.kgm, description=m.Device.weight.comment)
|
||||
width = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment)
|
||||
height = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment)
|
||||
depth = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment)
|
||||
events = NestedOn('Event', many=True, dump_only=True, description=m.Device.events.__doc__)
|
||||
events_one = NestedOn('Event', many=True, load_only=True, collection_class=OrderedSet)
|
||||
url = URL(dump_only=True, description=m.Device.url.__doc__)
|
||||
lots = NestedOn('Lot', many=True, dump_only=True)
|
||||
rate = NestedOn('AggregateRate', dump_only=True)
|
||||
price = NestedOn('Price', dump_only=True)
|
||||
trading = EnumField(states.Trading, dump_only=True)
|
||||
physical = EnumField(states.Physical, dump_only=True)
|
||||
physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor')
|
||||
|
||||
@pre_load
|
||||
def from_events_to_events_one(self, data: dict):
|
||||
|
@ -60,6 +66,11 @@ class Device(Thing):
|
|||
class Computer(Device):
|
||||
components = NestedOn('Component', many=True, dump_only=True, collection_class=OrderedSet)
|
||||
chassis = EnumField(ComputerChassis, required=True)
|
||||
ram_size = Integer(dump_only=True, data_key='ramSize')
|
||||
data_storage_size = Integer(dump_only=True, data_key='dataStorageSize')
|
||||
processor_model = Str(dump_only=True, data_key='processorModel')
|
||||
graphic_card_model = Str(dump_only=True, data_key='graphicCardModel')
|
||||
network_speeds = List(Integer(dump_only=True), dump_only=True, data_key='networkSpeeds')
|
||||
|
||||
|
||||
class Desktop(Computer):
|
||||
|
@ -148,6 +159,7 @@ class DataStorage(Component):
|
|||
unit=UnitCodes.mbyte,
|
||||
description=m.DataStorage.size.comment)
|
||||
interface = EnumField(DataStorageInterface)
|
||||
privacy = EnumField(DataStoragePrivacyCompliance, dump_only=True)
|
||||
|
||||
|
||||
class HardDrive(DataStorage):
|
||||
|
|
30
ereuse_devicehub/resources/device/states.py
Normal file
30
ereuse_devicehub/resources/device/states.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
from enum import Enum
|
||||
|
||||
from ereuse_devicehub.resources.event import models as e
|
||||
|
||||
|
||||
class State(Enum):
|
||||
@classmethod
|
||||
def events(cls):
|
||||
"""Events participating in this state."""
|
||||
return (s.value for s in cls)
|
||||
|
||||
|
||||
class Trading(State):
|
||||
Reserved = e.Reserve
|
||||
Cancelled = e.CancelTrade
|
||||
Sold = e.Sell
|
||||
Donated = e.Donate
|
||||
Renting = e.Rent
|
||||
# todo add Pay = e.Pay
|
||||
ToBeDisposed = e.ToDisposeProduct
|
||||
ProductDisposed = e.DisposeProduct
|
||||
|
||||
|
||||
class Physical(State):
|
||||
ToBeRepaired = e.ToRepair
|
||||
Repaired = e.Repair
|
||||
Preparing = e.ToPrepare
|
||||
Prepared = e.Prepare
|
||||
ReadyToBeUsed = e.ReadyToUse
|
||||
InUse = e.Live
|
|
@ -235,3 +235,21 @@ class ReceiverRole(Enum):
|
|||
CollectionPoint = 'A collection point.'
|
||||
RecyclingPoint = 'A recycling point.'
|
||||
Transporter = 'An user that ships the devices to another one.'
|
||||
|
||||
|
||||
class DataStoragePrivacyCompliance(Enum):
|
||||
EraseBasic = 'EraseBasic'
|
||||
EraseBasicError = 'EraseBasicError'
|
||||
EraseSectors = 'EraseSectors'
|
||||
EraseSectorsError = 'EraseSectorsError'
|
||||
Destruction = 'Destruction'
|
||||
DestructionError = 'DestructionError'
|
||||
|
||||
@classmethod
|
||||
def from_erase(cls, erasure) -> 'DataStoragePrivacyCompliance':
|
||||
"""Returns the correct enum depending of the passed-in erasure."""
|
||||
from ereuse_devicehub.resources.event.models import EraseSectors
|
||||
if isinstance(erasure, EraseSectors):
|
||||
return cls.EraseSectors if not erasure.error else cls.EraseSectorsError
|
||||
else:
|
||||
return cls.EraseBasic if not erasure.error else cls.EraseBasicError
|
||||
|
|
|
@ -74,9 +74,9 @@ class PhotoboxSystemRateDef(RateDef):
|
|||
SCHEMA = schemas.PhotoboxSystemRate
|
||||
|
||||
|
||||
class AppRateDef(RateDef):
|
||||
class ManualRateDef(RateDef):
|
||||
VIEW = None
|
||||
SCHEMA = schemas.AppRate
|
||||
SCHEMA = schemas.ManualRate
|
||||
|
||||
|
||||
class PriceDef(EventDef):
|
||||
|
@ -98,7 +98,8 @@ class SnapshotDef(EventDef):
|
|||
VIEW = SnapshotView
|
||||
SCHEMA = schemas.Snapshot
|
||||
|
||||
def __init__(self, app, import_name=__name__.split('.')[0], static_folder=None, static_url_path=None,
|
||||
def __init__(self, app, import_name=__name__.split('.')[0], static_folder=None,
|
||||
static_url_path=None,
|
||||
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
|
||||
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
|
||||
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from collections import Iterable
|
||||
from datetime import datetime, timedelta
|
||||
from distutils.version import StrictVersion
|
||||
from typing import Set, Union
|
||||
from uuid import uuid4
|
||||
|
||||
|
@ -24,17 +25,12 @@ from ereuse_devicehub.db import db
|
|||
from ereuse_devicehub.resources.agent.models import Agent
|
||||
from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Desktop, \
|
||||
Device, Laptop, Server
|
||||
from ereuse_devicehub.resources.enums import AppearanceRange, BOX_RATE_3, BOX_RATE_5, Bios, \
|
||||
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, \
|
||||
FunctionalityRange, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, RatingRange, RatingSoftware, \
|
||||
ReceiverRole, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength
|
||||
from ereuse_devicehub.resources.image.models import Image
|
||||
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
||||
from ereuse_devicehub.resources.user.models import User
|
||||
|
||||
"""
|
||||
A quantity of money with a currency.
|
||||
"""
|
||||
|
||||
|
||||
class JoinedTableMixin:
|
||||
# noinspection PyMethodParameters
|
||||
|
@ -54,7 +50,7 @@ class Event(Thing):
|
|||
incidence.comment = """
|
||||
Should this event be reviewed due some anomaly?
|
||||
"""
|
||||
closed = Column(Boolean, default=False, nullable=False)
|
||||
closed = Column(Boolean, default=True, nullable=False)
|
||||
closed.comment = """
|
||||
Whether the author has finished the event.
|
||||
After this is set to True, no modifications are allowed.
|
||||
|
@ -389,30 +385,6 @@ class IndividualRate(Rate):
|
|||
pass
|
||||
|
||||
|
||||
class AggregateRate(Rate):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||
ratings = relationship(IndividualRate,
|
||||
backref=backref('aggregated_ratings',
|
||||
lazy=True,
|
||||
order_by=lambda: IndividualRate.created,
|
||||
collection_class=OrderedSet),
|
||||
secondary=lambda: RateAggregateRate.__table__,
|
||||
order_by=lambda: IndividualRate.created,
|
||||
collection_class=OrderedSet)
|
||||
"""The ratings this aggregateRate aggregates."""
|
||||
|
||||
|
||||
class RateAggregateRate(db.Model):
|
||||
"""
|
||||
Represents the ``many to many`` relationship between
|
||||
``Rate`` and ``AggregateRate``.
|
||||
"""
|
||||
rate_id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||
aggregate_rate_id = Column(UUID(as_uuid=True),
|
||||
ForeignKey(AggregateRate.id),
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class ManualRate(IndividualRate):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||
labelling = Column(Boolean)
|
||||
|
@ -432,51 +404,67 @@ class WorkbenchRate(ManualRate):
|
|||
|
||||
# todo ensure for WorkbenchRate version and software are not None when inserting them
|
||||
|
||||
def ratings(self) -> Set['WorkbenchRate']:
|
||||
def ratings(self):
|
||||
"""
|
||||
Computes all the possible rates taking this rating as a model.
|
||||
|
||||
Returns a set of ratings, including this one, which is mutated.
|
||||
"""
|
||||
from ereuse_rate.main import main
|
||||
from ereuse_devicehub.resources.event.rate.main import main
|
||||
return main(self, **app.config.get_namespace('WORKBENCH_RATE_'))
|
||||
|
||||
|
||||
class AppRate(ManualRate):
|
||||
pass
|
||||
|
||||
|
||||
class PhotoboxRate(IndividualRate):
|
||||
class AggregateRate(Rate):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||
image_id = Column(UUID(as_uuid=True), ForeignKey(Image.id), nullable=False)
|
||||
image = relationship(Image,
|
||||
uselist=False,
|
||||
cascade=CASCADE_OWN,
|
||||
single_parent=True,
|
||||
primaryjoin=Image.id == image_id)
|
||||
manual_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
||||
manual = relationship(ManualRate,
|
||||
backref=backref('aggregate_rate_manual',
|
||||
lazy=True,
|
||||
order_by=lambda: AggregateRate.created,
|
||||
collection_class=OrderedSet),
|
||||
primaryjoin=manual_id == ManualRate.id)
|
||||
workbench_id = Column(UUID(as_uuid=True), ForeignKey(WorkbenchRate.id))
|
||||
workbench = relationship(WorkbenchRate,
|
||||
backref=backref('aggregate_rate_workbench',
|
||||
lazy=True,
|
||||
order_by=lambda: AggregateRate.created,
|
||||
collection_class=OrderedSet),
|
||||
primaryjoin=workbench_id == WorkbenchRate.id)
|
||||
|
||||
# todo how to ensure phtoboxrate.device == image.image_list.device?
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
kwargs.setdefault('version', StrictVersion('1.0'))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def processor(self):
|
||||
return self.workbench.processor
|
||||
|
||||
class PhotoboxUserRate(PhotoboxRate):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(PhotoboxRate.id), primary_key=True)
|
||||
assembling = Column(SmallInteger, check_range('assembling', *BOX_RATE_5), nullable=False)
|
||||
parts = Column(SmallInteger, check_range('parts', *BOX_RATE_5), nullable=False)
|
||||
buttons = Column(SmallInteger, check_range('buttons', *BOX_RATE_5), nullable=False)
|
||||
dents = Column(SmallInteger, check_range('dents', *BOX_RATE_5), nullable=False)
|
||||
decolorization = Column(SmallInteger,
|
||||
check_range('decolorization', *BOX_RATE_5),
|
||||
nullable=False)
|
||||
scratches = Column(SmallInteger, check_range('scratches', *BOX_RATE_5), nullable=False)
|
||||
tag_alignment = Column(SmallInteger,
|
||||
check_range('tag_alignment', *BOX_RATE_3),
|
||||
nullable=False)
|
||||
tag_adhesive = Column(SmallInteger, check_range('tag_adhesive', *BOX_RATE_3), nullable=False)
|
||||
dirt = Column(SmallInteger, check_range('dirt', *BOX_RATE_3), nullable=False)
|
||||
@property
|
||||
def ram(self):
|
||||
return self.workbench.ram
|
||||
|
||||
@property
|
||||
def data_storage(self):
|
||||
return self.workbench.data_storage
|
||||
|
||||
class PhotoboxSystemRate(PhotoboxRate):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(PhotoboxRate.id), primary_key=True)
|
||||
@property
|
||||
def graphic_card(self):
|
||||
return self.workbench.graphic_card
|
||||
|
||||
@property
|
||||
def bios(self):
|
||||
return self.workbench.bios
|
||||
|
||||
@classmethod
|
||||
def from_workbench_rate(cls, rate: WorkbenchRate):
|
||||
aggregate = cls()
|
||||
aggregate.rating = rate.rating
|
||||
aggregate.software = rate.software
|
||||
aggregate.appearance = rate.appearance
|
||||
aggregate.functionality = rate.functionality
|
||||
aggregate.device = rate.device
|
||||
aggregate.workbench = rate
|
||||
return aggregate
|
||||
|
||||
|
||||
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||
|
|
|
@ -19,7 +19,6 @@ from ereuse_devicehub.resources.device.models import Component, Computer, Device
|
|||
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, FunctionalityRange, \
|
||||
PriceSoftware, RatingSoftware, ReceiverRole, SnapshotExpectedEvents, SnapshotSoftware, \
|
||||
TestDataStorageLength
|
||||
from ereuse_devicehub.resources.image.models import Image
|
||||
from ereuse_devicehub.resources.models import Thing
|
||||
from ereuse_devicehub.resources.user.models import User
|
||||
|
||||
|
@ -78,7 +77,7 @@ class EventWithOneDevice(Event):
|
|||
|
||||
|
||||
class EventWithMultipleDevices(Event):
|
||||
devices = ... # type: relationship
|
||||
devices = ... # type: relationship
|
||||
|
||||
def __init__(self, id=None, name=None, incidence=None, closed=None, error=None,
|
||||
description=None, start_time=None, end_time=None, snapshot=None, agent=None,
|
||||
|
@ -165,11 +164,43 @@ class IndividualRate(Rate):
|
|||
|
||||
|
||||
class AggregateRate(Rate):
|
||||
manual_id = ... # type: Column
|
||||
manual = ... # type: relationship
|
||||
workbench = ... # type: relationship
|
||||
workbench_id = ... # type: Column
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.ratings = ... # type: Set[IndividualRate]
|
||||
self.manual_id = ... # type: UUID
|
||||
self.manual = ... # type: ManualRate
|
||||
self.workbench = ... # type: WorkbenchRate
|
||||
self.workbench_id = ... # type: UUID
|
||||
self.price = ... # type: Price
|
||||
|
||||
@property
|
||||
def processor(self):
|
||||
return self.workbench.processor
|
||||
|
||||
@property
|
||||
def ram(self):
|
||||
return self.workbench.ram
|
||||
|
||||
@property
|
||||
def data_storage(self):
|
||||
return self.workbench.data_storage
|
||||
|
||||
@property
|
||||
def graphic_card(self):
|
||||
return self.workbench.graphic_card
|
||||
|
||||
@property
|
||||
def bios(self):
|
||||
return self.workbench.bios
|
||||
|
||||
@classmethod
|
||||
def from_workbench_rate(cls, rate: WorkbenchRate) -> AggregateRate:
|
||||
pass
|
||||
|
||||
|
||||
class ManualRate(IndividualRate):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
|
@ -177,6 +208,7 @@ class ManualRate(IndividualRate):
|
|||
self.labelling = ... # type: bool
|
||||
self.appearance_range = ... # type: AppearanceRange
|
||||
self.functionality_range = ... # type: FunctionalityRange
|
||||
self.aggregate_rate_manual = ... #type: AggregateRate
|
||||
|
||||
|
||||
class WorkbenchRate(ManualRate):
|
||||
|
@ -187,34 +219,10 @@ class WorkbenchRate(ManualRate):
|
|||
self.data_storage = ... # type: float
|
||||
self.graphic_card = ... # type: float
|
||||
self.bios = ... # type: Bios
|
||||
self.aggregate_rate_workbench = ... #type: AggregateRate
|
||||
|
||||
|
||||
class AppRate(ManualRate):
|
||||
pass
|
||||
|
||||
|
||||
class PhotoboxRate(IndividualRate):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.num = ... # type: int
|
||||
self.image = ... # type: Image
|
||||
|
||||
|
||||
class PhotoboxUserRate(PhotoboxRate):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.assembling = ... # type: int
|
||||
self.parts = ... # type: int
|
||||
self.buttons = ... # type: int
|
||||
self.dents = ... # type: int
|
||||
self.decolorization = ... # type: int
|
||||
self.scratches = ... # type: int
|
||||
self.tag_adhesive = ... # type: int
|
||||
self.dirt = ... # type: int
|
||||
|
||||
|
||||
class PhotoboxSystemRate(PhotoboxRate):
|
||||
pass
|
||||
def ratings(self) -> Set[Rate]:
|
||||
pass
|
||||
|
||||
|
||||
class Price(EventWithOneDevice):
|
||||
|
@ -237,8 +245,24 @@ class Price(EventWithOneDevice):
|
|||
class EreusePrice(Price):
|
||||
MULTIPLIER = ... # type: Dict
|
||||
|
||||
class Type:
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.amount = ... # type: float
|
||||
self.percentage = ... # type: float
|
||||
|
||||
class Service:
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.standard = ... # type: EreusePrice.Type
|
||||
self.warranty2 = ... # type: EreusePrice.Type
|
||||
|
||||
def __init__(self, rating: AggregateRate, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.retailer = ... # type: EreusePrice.Service
|
||||
self.platform = ... # type: EreusePrice.Service
|
||||
self.refurbisher = ... # type: EreusePrice.Service
|
||||
self.warranty2 = ... # type: float
|
||||
|
||||
|
||||
class Test(EventWithOneDevice):
|
||||
|
|
0
ereuse_devicehub/resources/event/rate/__init__.py
Normal file
0
ereuse_devicehub/resources/event/rate/__init__.py
Normal file
78
ereuse_devicehub/resources/event/rate/main.py
Normal file
78
ereuse_devicehub/resources/event/rate/main.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
from contextlib import suppress
|
||||
from distutils.version import StrictVersion
|
||||
from typing import Set, Union
|
||||
|
||||
from ereuse_devicehub.resources.device.models import Device
|
||||
from ereuse_devicehub.resources.enums import RatingSoftware
|
||||
from ereuse_devicehub.resources.event.models import AggregateRate, EreusePrice, Rate, \
|
||||
WorkbenchRate
|
||||
from ereuse_devicehub.resources.event.rate.workbench import v1_0
|
||||
|
||||
RATE_TYPES = {
|
||||
WorkbenchRate: {
|
||||
RatingSoftware.ECost: {
|
||||
'1.0': v1_0.Rate()
|
||||
},
|
||||
RatingSoftware.EMarket: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def rate(device: Device, rate: Rate):
|
||||
"""
|
||||
Rates the passed-in ``rate`` using values from the rate itself
|
||||
and the ``device``.
|
||||
|
||||
This method mutates ``rate``.
|
||||
|
||||
:param device: The device to use as a model.
|
||||
:param rate: A half-filled rate.
|
||||
"""
|
||||
cls = rate.__class__
|
||||
assert cls in RATE_TYPES, 'Rate type {} not supported.'.format(cls)
|
||||
assert rate.software in RATE_TYPES[cls], 'Rate soft {} not supported.'.format(rate.software)
|
||||
assert str(rate.version) in RATE_TYPES[cls][rate.software], \
|
||||
'Rate version {} not supported.'.format(rate.version)
|
||||
RATE_TYPES[cls][rate.software][str(rate.version)].compute(device, rate)
|
||||
|
||||
|
||||
def main(rating_model: WorkbenchRate,
|
||||
software: RatingSoftware,
|
||||
version: StrictVersion) -> Set[Union[WorkbenchRate, AggregateRate, EreusePrice]]:
|
||||
"""
|
||||
Generates all the rates (per software and version) for a given
|
||||
half-filled rate acting as a model, and finally it generates
|
||||
an ``AggregateRating`` with the rate that matches the
|
||||
``software`` and ``version``.
|
||||
|
||||
This method mutates ``rating_model`` by fulfilling it and
|
||||
``rating_model.device`` by adding the new rates.
|
||||
|
||||
:return: A set of rates with the ``rate`` value computed, where
|
||||
the first rate is the ``rating_model``.
|
||||
"""
|
||||
assert rating_model.device
|
||||
events = set()
|
||||
for soft, value in RATE_TYPES[rating_model.__class__].items():
|
||||
for vers, func in value.items():
|
||||
if not rating_model.rating: # Fill the rating before creating another rate
|
||||
rating = rating_model
|
||||
else: # original rating was filled already; use a new one
|
||||
rating = WorkbenchRate(
|
||||
labelling=rating_model.labelling,
|
||||
appearance_range=rating_model.appearance_range,
|
||||
functionality_range=rating_model.functionality_range,
|
||||
device=rating_model.device,
|
||||
)
|
||||
rating.software = soft
|
||||
rating.version = vers
|
||||
rate(rating_model.device, rating)
|
||||
events.add(rating)
|
||||
if soft == software and vers == version:
|
||||
aggregation = AggregateRate.from_workbench_rate(rating)
|
||||
events.add(aggregation)
|
||||
with suppress(ValueError):
|
||||
# We will have exception if range == VERY_LOW
|
||||
events.add(EreusePrice(aggregation))
|
||||
return events
|
54
ereuse_devicehub/resources/event/rate/rate.py
Normal file
54
ereuse_devicehub/resources/event/rate/rate.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import math
|
||||
from typing import Iterable
|
||||
|
||||
from ereuse_devicehub.resources.device.models import Device
|
||||
from ereuse_devicehub.resources.event.models import WorkbenchRate
|
||||
|
||||
|
||||
class BaseRate:
|
||||
"""growing exponential from this value"""
|
||||
CEXP = 0
|
||||
"""growing lineal starting on this value"""
|
||||
CLIN = 242
|
||||
"""growing logarithmic starting on this value"""
|
||||
CLOG = 0.5
|
||||
|
||||
"""Processor has 50% of weight over total score, used in harmonic mean"""
|
||||
PROCESSOR_WEIGHT = 0.5
|
||||
"""Storage has 20% of weight over total score, used in harmonic mean"""
|
||||
DATA_STORAGE_WEIGHT = 0.2
|
||||
"""Ram has 30% of weight over total score, used in harmonic mean"""
|
||||
RAM_WEIGHT = 0.3
|
||||
|
||||
def compute(self, device: Device, rate: WorkbenchRate):
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
def norm(x, x_min, x_max):
|
||||
return (x - x_min) / (x_max - x_min)
|
||||
|
||||
@staticmethod
|
||||
def rate_log(x):
|
||||
return math.log10(2 * x) + 3.57 # todo magic number!
|
||||
|
||||
@staticmethod
|
||||
def rate_lin(x):
|
||||
return 7 * x + 0.06 # todo magic number!
|
||||
|
||||
@staticmethod
|
||||
def rate_exp(x):
|
||||
return math.exp(x) / (2 - math.exp(x))
|
||||
|
||||
@staticmethod
|
||||
def harmonic_mean(weights: Iterable[float], rates: Iterable[float]):
|
||||
return sum(weights) / sum(char / rate for char, rate in zip(weights, rates))
|
||||
|
||||
def harmonic_mean_rates(self, rate_processor, rate_storage, rate_ram):
|
||||
"""
|
||||
Merging components
|
||||
"""
|
||||
total_weights = self.PROCESSOR_WEIGHT + self.DATA_STORAGE_WEIGHT + self.RAM_WEIGHT
|
||||
total_rate = self.PROCESSOR_WEIGHT / rate_processor \
|
||||
+ self.DATA_STORAGE_WEIGHT / rate_storage \
|
||||
+ self.RAM_WEIGHT / rate_ram
|
||||
return total_weights / total_rate
|
253
ereuse_devicehub/resources/event/rate/workbench/v1_0.py
Normal file
253
ereuse_devicehub/resources/event/rate/workbench/v1_0.py
Normal file
|
@ -0,0 +1,253 @@
|
|||
from enum import Enum
|
||||
from itertools import groupby
|
||||
from typing import Iterable
|
||||
|
||||
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Desktop, Laptop, \
|
||||
Processor, RamModule, Server
|
||||
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
||||
WorkbenchRate
|
||||
# todo if no return assign then rate_c = 1 is assigned
|
||||
# todo fix corner cases, like components characteristics == None
|
||||
from ereuse_devicehub.resources.event.rate.rate import BaseRate
|
||||
|
||||
|
||||
class Rate(BaseRate):
|
||||
"""
|
||||
Rate all components in Computer
|
||||
"""
|
||||
|
||||
class Range(Enum):
|
||||
@classmethod
|
||||
def from_devicehub(cls, r: Enum):
|
||||
return getattr(cls, r.name) if r else cls.NONE
|
||||
|
||||
class Appearance(Range):
|
||||
Z = 0.5
|
||||
A = 0.3
|
||||
B = 0
|
||||
C = -0.2
|
||||
D = -0.5
|
||||
E = -1.0
|
||||
NONE = -0.3
|
||||
|
||||
class Functionality(Range):
|
||||
A = 0.4
|
||||
B = -0.5
|
||||
C = -0.75
|
||||
D = -1
|
||||
NONE = -0.3
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.RATES = {
|
||||
# composition: type: (field, compute class)
|
||||
Processor.t: ('processor', ProcessorRate()),
|
||||
RamModule.t: ('ram', RamRate()),
|
||||
DataStorage.t: ('data_storage', DataStorageRate())
|
||||
}
|
||||
|
||||
def compute(self, device: Computer, rate: WorkbenchRate):
|
||||
"""
|
||||
Compute 'Workbench'Rate computer is a rate (score) ranging from 0 to 4.7
|
||||
that represents estimating value of use of desktop and laptop computer components.
|
||||
|
||||
This mutates "rate".
|
||||
"""
|
||||
assert isinstance(device, (Desktop, Laptop, Server))
|
||||
assert isinstance(rate, WorkbenchRate)
|
||||
|
||||
rate.processor = rate.data_storage = rate.ram = 1 # Init
|
||||
|
||||
# Group cpus, rams, storages and compute their rate
|
||||
# Treat the same way with HardDrive and SolidStateDrive like (DataStorage)
|
||||
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()))
|
||||
for type, components in groupby(sorted(c, key=clause), key=clause):
|
||||
if type == Processor.t: # ProcessorRate.compute expects only 1 processor
|
||||
components = next(components)
|
||||
field, rate_cls = self.RATES[type] # type: str, BaseRate
|
||||
result = rate_cls.compute(components, rate)
|
||||
if result:
|
||||
setattr(rate, field, result)
|
||||
|
||||
rate_components = self.harmonic_mean_rates(rate.processor, rate.data_storage, rate.ram)
|
||||
rate.appearance = self.Appearance.from_devicehub(rate.appearance_range).value
|
||||
rate.functionality = self.Functionality.from_devicehub(rate.functionality_range).value
|
||||
|
||||
rate.rating = round(max(rate_components + rate.functionality + rate.appearance, 0), 2)
|
||||
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)
|
||||
|
||||
|
||||
class ProcessorRate(BaseRate):
|
||||
"""
|
||||
Calculate a ProcessorRate of all Processor devices
|
||||
"""
|
||||
# processor.xMin, processor.xMax
|
||||
PROCESSOR_NORM = 3196.17, 17503.81
|
||||
|
||||
DEFAULT_CORES = 1
|
||||
DEFAULT_SPEED = 1.6
|
||||
# In case of i2, i3,.. result penalized.
|
||||
# Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz, score = 23406.92 but results inan score of 17503.
|
||||
DEFAULT_SCORE = 4000
|
||||
|
||||
def compute(self, processor: Processor, rate: WorkbenchRate):
|
||||
""" Compute processor rate
|
||||
Obs: cores and speed are possible NULL value
|
||||
:return: result is a rate (score) of Processor characteristics
|
||||
"""
|
||||
# todo for processor_device in processors; more than one processor
|
||||
cores = processor.cores or self.DEFAULT_CORES
|
||||
speed = processor.speed or self.DEFAULT_SPEED
|
||||
# todo fix StopIteration if don't exists BenchmarkProcessor
|
||||
benchmark_cpu = next(e for e in processor.events if isinstance(e, BenchmarkProcessor))
|
||||
benchmark_cpu = benchmark_cpu.rate or self.DEFAULT_SCORE
|
||||
|
||||
# STEP: Fusion components
|
||||
processor_rate = (benchmark_cpu + speed * 2000 * cores) / 2 # todo magic number!
|
||||
|
||||
# STEP: Normalize values
|
||||
processor_norm = max(self.norm(processor_rate, *self.PROCESSOR_NORM), 0)
|
||||
|
||||
# STEP: Compute rate/score from every component
|
||||
# Calculate processor_rate
|
||||
if processor_norm >= self.CEXP:
|
||||
processor_rate = self.rate_exp(processor_norm)
|
||||
if self.CLIN <= processor_norm < self.CLOG:
|
||||
processor_rate = self.rate_lin(processor_norm)
|
||||
if processor_norm >= self.CLOG:
|
||||
processor_rate = self.rate_log(processor_norm)
|
||||
|
||||
assert processor_rate, 'Could not rate processor.'
|
||||
return processor_rate
|
||||
|
||||
|
||||
class RamRate(BaseRate):
|
||||
"""
|
||||
Calculate a RamRate of all RamModule devices
|
||||
"""
|
||||
# ram.size.xMin; ram.size.xMax
|
||||
SIZE_NORM = 256, 8192
|
||||
RAM_SPEED_NORM = 133, 1333
|
||||
# ram.speed.factor
|
||||
RAM_SPEED_FACTOR = 3.7
|
||||
# ram.size.weight; ram.speed.weight;
|
||||
RAM_WEIGHTS = 0.7, 0.3
|
||||
|
||||
def compute(self, ram_devices: Iterable[RamModule], rate: WorkbenchRate):
|
||||
"""
|
||||
Obs: RamModule.speed is possible NULL value & size != NULL or NOT??
|
||||
:return: result is a rate (score) of all RamModule components
|
||||
"""
|
||||
size = 0.0
|
||||
speed = 0.0
|
||||
|
||||
# STEP: Filtering, data cleaning and merging of component parts
|
||||
for ram in ram_devices:
|
||||
_size = ram.size or 0
|
||||
size += _size
|
||||
if ram.speed:
|
||||
speed += (ram.speed or 0) * _size
|
||||
else:
|
||||
speed += (_size / self.RAM_SPEED_FACTOR) * _size
|
||||
|
||||
# STEP: Fusion components
|
||||
# To guarantee that there will be no 0/0
|
||||
if size:
|
||||
speed /= size
|
||||
|
||||
# STEP: Normalize values
|
||||
size_norm = max(self.norm(size, *self.SIZE_NORM), 0)
|
||||
ram_speed_norm = max(self.norm(speed, *self.RAM_SPEED_NORM), 0)
|
||||
|
||||
# STEP: Compute rate/score from every component
|
||||
# Calculate size_rate
|
||||
if self.CEXP <= size_norm < self.CLIN:
|
||||
size_rate = self.rate_exp(size_norm)
|
||||
if self.CLIN <= size_norm < self.CLOG:
|
||||
size_rate = self.rate_lin(size_norm)
|
||||
if size_norm >= self.CLOG:
|
||||
size_rate = self.rate_log(size_norm)
|
||||
# Calculate ram_speed_rate
|
||||
if self.CEXP <= ram_speed_norm < self.CLIN:
|
||||
ram_speed_rate = self.rate_exp(ram_speed_norm)
|
||||
if self.CLIN <= ram_speed_norm < self.CLOG:
|
||||
ram_speed_rate = self.rate_lin(ram_speed_norm)
|
||||
if ram_speed_norm >= self.CLOG:
|
||||
ram_speed_rate = self.rate_log(ram_speed_norm)
|
||||
|
||||
# STEP: Fusion Characteristics
|
||||
return self.harmonic_mean(self.RAM_WEIGHTS, rates=(size_rate, ram_speed_rate))
|
||||
|
||||
|
||||
class DataStorageRate(BaseRate):
|
||||
"""
|
||||
Calculate the rate of all DataStorage devices
|
||||
"""
|
||||
# drive.size.xMin; drive.size.xMax
|
||||
SIZE_NORM = 4, 265000
|
||||
READ_SPEED_NORM = 2.7, 109.5
|
||||
WRITE_SPEED_NORM = 2, 27.35
|
||||
# drive.size.weight; drive.readingSpeed.weight; drive.writingSpeed.weight;
|
||||
DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25
|
||||
|
||||
def compute(self, data_storage_devices: Iterable[DataStorage], rate: WorkbenchRate):
|
||||
"""
|
||||
Obs: size != NULL and 0 value & read_speed and write_speed != NULL
|
||||
:return: result is a rate (score) of all DataStorage devices
|
||||
"""
|
||||
size = 0
|
||||
read_speed = 0
|
||||
write_speed = 0
|
||||
|
||||
# STEP: Filtering, data cleaning and merging of component parts
|
||||
for storage in data_storage_devices:
|
||||
# todo fix StopIteration if don't exists BenchmarkDataStorage
|
||||
benchmark = next(e for e in storage.events if isinstance(e, BenchmarkDataStorage))
|
||||
# prevent NULL values
|
||||
_size = storage.size or 0
|
||||
size += _size
|
||||
read_speed += benchmark.read_speed * _size
|
||||
write_speed += benchmark.write_speed * _size
|
||||
|
||||
# STEP: Fusion components
|
||||
# Check almost one storage have size, try catch exception 0/0
|
||||
if size:
|
||||
read_speed /= size
|
||||
write_speed /= size
|
||||
|
||||
# STEP: Normalize values
|
||||
size_norm = max(self.norm(size, *self.SIZE_NORM), 0)
|
||||
read_speed_norm = max(self.norm(read_speed, *self.READ_SPEED_NORM), 0)
|
||||
write_speed_norm = max(self.norm(write_speed, *self.WRITE_SPEED_NORM), 0)
|
||||
|
||||
# STEP: Compute rate/score from every component
|
||||
# Calculate size_rate
|
||||
if size_norm >= self.CLOG:
|
||||
size_rate = self.rate_log(size_norm)
|
||||
elif self.CLIN <= size_norm < self.CLOG:
|
||||
size_rate = self.rate_lin(size_norm)
|
||||
elif self.CEXP <= size_norm < self.CLIN:
|
||||
size_rate = self.rate_exp(size_norm)
|
||||
# Calculate read_speed_rate
|
||||
if read_speed_norm >= self.CLOG:
|
||||
read_speed_rate = self.rate_log(read_speed_norm)
|
||||
elif self.CLIN <= read_speed_norm < self.CLOG:
|
||||
read_speed_rate = self.rate_lin(read_speed_norm)
|
||||
elif self.CEXP <= read_speed_norm < self.CLIN:
|
||||
read_speed_rate = self.rate_exp(read_speed_norm)
|
||||
# write_speed_rate
|
||||
if write_speed_norm >= self.CLOG:
|
||||
write_speed_rate = self.rate_log(write_speed_norm)
|
||||
elif self.CLIN <= write_speed_norm < self.CLOG:
|
||||
write_speed_rate = self.rate_lin(write_speed_norm)
|
||||
elif self.CEXP <= write_speed_norm < self.CLIN:
|
||||
write_speed_rate = self.rate_exp(write_speed_norm)
|
||||
|
||||
# STEP: Fusion Characteristics
|
||||
return self.harmonic_mean(self.DATA_STORAGE_WEIGHTS,
|
||||
rates=(size_rate, read_speed_rate, write_speed_rate))
|
|
@ -101,7 +101,7 @@ class StepRandom(Step):
|
|||
class Rate(EventWithOneDevice):
|
||||
rating = Integer(validate=Range(*RATE_POSITIVE),
|
||||
dump_only=True,
|
||||
data_key='ratingValue',
|
||||
data_key='rating',
|
||||
description='The rating for the content.')
|
||||
software = EnumField(RatingSoftware,
|
||||
dump_only=True,
|
||||
|
@ -118,10 +118,6 @@ class IndividualRate(Rate):
|
|||
pass
|
||||
|
||||
|
||||
class AggregateRate(Rate):
|
||||
ratings = NestedOn(IndividualRate, many=True)
|
||||
|
||||
|
||||
class PhotoboxRate(IndividualRate):
|
||||
num = Integer(dump_only=True)
|
||||
# todo Image
|
||||
|
@ -155,10 +151,6 @@ class ManualRate(IndividualRate):
|
|||
labelling = Boolean(description='Sets if there are labels stuck that should be removed.')
|
||||
|
||||
|
||||
class AppRate(ManualRate):
|
||||
pass
|
||||
|
||||
|
||||
class WorkbenchRate(ManualRate):
|
||||
processor = Float()
|
||||
ram = Float()
|
||||
|
@ -168,6 +160,16 @@ class WorkbenchRate(ManualRate):
|
|||
'boot from the network.')
|
||||
|
||||
|
||||
class AggregateRate(Rate):
|
||||
workbench = NestedOn(WorkbenchRate, dump_only=True)
|
||||
manual = NestedOn(ManualRate, dump_only=True)
|
||||
processor = Float(dump_only=True)
|
||||
ram = Float(dump_only=True)
|
||||
data_storage = Float(dump_only=True)
|
||||
graphic_card = Float(dump_only=True)
|
||||
bios = EnumField(Bios, dump_only=True)
|
||||
|
||||
|
||||
class Price(EventWithOneDevice):
|
||||
currency = EnumField(Currency, required=True)
|
||||
price = Decimal(places=4, rounding=decimal.ROUND_HALF_EVEN, required=True)
|
||||
|
@ -285,7 +287,7 @@ class StressTest(Test):
|
|||
|
||||
|
||||
class Benchmark(EventWithOneDevice):
|
||||
elapsed = TimeDelta(precision=TimeDelta.SECONDS)
|
||||
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
|
||||
|
||||
|
||||
class BenchmarkDataStorage(Benchmark):
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from contextlib import suppress
|
||||
from distutils.version import StrictVersion
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
|
@ -77,11 +76,9 @@ class SnapshotView(View):
|
|||
snapshot.events |= events
|
||||
|
||||
# Compute ratings
|
||||
with suppress(StopIteration):
|
||||
# todo are we sure we want to have snapshots without rates?
|
||||
snapshot.events |= next(
|
||||
e.ratings() for e in events_device if isinstance(e, WorkbenchRate)
|
||||
)
|
||||
for rate in (e for e in events_device if isinstance(e, WorkbenchRate)):
|
||||
rates = rate.ratings()
|
||||
snapshot.events |= rates
|
||||
|
||||
db.session.add(snapshot)
|
||||
db.session.commit()
|
||||
|
|
|
@ -15,7 +15,8 @@ class LotDef(Resource):
|
|||
AUTH = True
|
||||
ID_CONVERTER = Converters.uuid
|
||||
|
||||
def __init__(self, app, import_name=__name__.split('.')[0], static_folder=None, static_url_path=None,
|
||||
def __init__(self, app, import_name=__name__.split('.')[0], static_folder=None,
|
||||
static_url_path=None,
|
||||
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
|
||||
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
|
||||
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
|
||||
|
|
|
@ -5,7 +5,6 @@ click==6.7
|
|||
click-spinner==0.1.8
|
||||
colorama==0.3.9
|
||||
colour==0.1.5
|
||||
ereuse-rate==0.0.2
|
||||
ereuse-utils==0.4.0b9
|
||||
Flask==1.0.2
|
||||
Flask-Cors==3.0.6
|
||||
|
|
1
setup.py
1
setup.py
|
@ -37,7 +37,6 @@ setup(
|
|||
'teal>=0.2.0a24', # teal always first
|
||||
'click',
|
||||
'click-spinner',
|
||||
'ereuse-rate==0.0.2',
|
||||
'ereuse-utils[Naming]>=0.4b9',
|
||||
'hashids',
|
||||
'marshmallow_enum',
|
||||
|
|
|
@ -33,3 +33,4 @@ components:
|
|||
events:
|
||||
- type: BenchmarkProcessor
|
||||
rate: 2410
|
||||
elapsed: 11
|
||||
|
|
|
@ -91,7 +91,14 @@ def test_physical_properties():
|
|||
manufacturer='mr',
|
||||
width=2.0,
|
||||
color=Color())
|
||||
pc = Desktop(chassis=ComputerChassis.Tower)
|
||||
pc = Desktop(chassis=ComputerChassis.Tower,
|
||||
model='foo',
|
||||
manufacturer='bar',
|
||||
serial_number='foo-bar',
|
||||
weight=2.8,
|
||||
width=1.4,
|
||||
height=2.1,
|
||||
color=Color('LightSeaGreen'))
|
||||
pc.components.add(c)
|
||||
db.session.add(pc)
|
||||
db.session.commit()
|
||||
|
@ -110,6 +117,17 @@ def test_physical_properties():
|
|||
'color': Color(),
|
||||
'depth': None
|
||||
}
|
||||
assert pc.physical_properties == {
|
||||
'model': 'foo',
|
||||
'manufacturer': 'bar',
|
||||
'serial_number': 'foo-bar',
|
||||
'weight': 2.8,
|
||||
'width': 1.4,
|
||||
'height': 2.1,
|
||||
'depth': None,
|
||||
'color': Color('LightSeaGreen'),
|
||||
'chassis': ComputerChassis.Tower
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ipaddress
|
||||
from datetime import timedelta
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
from flask import current_app as app, g
|
||||
|
@ -8,6 +9,7 @@ from teal.enums import Currency, Subdivision
|
|||
|
||||
from ereuse_devicehub.client import UserClient
|
||||
from ereuse_devicehub.db import db
|
||||
from ereuse_devicehub.resources.device import states
|
||||
from ereuse_devicehub.resources.device.models import Desktop, Device, GraphicCard, HardDrive, \
|
||||
RamModule, SolidStateDrive
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, TestDataStorageLength
|
||||
|
@ -175,22 +177,24 @@ def test_update_parent():
|
|||
assert not benchmark.parent
|
||||
|
||||
|
||||
@pytest.mark.parametrize('event_model', [
|
||||
models.ToRepair,
|
||||
models.Repair,
|
||||
models.ToPrepare,
|
||||
models.ReadyToUse,
|
||||
models.ToPrepare,
|
||||
models.Prepare,
|
||||
@pytest.mark.parametrize('event_model_state', [
|
||||
(models.ToRepair, states.Physical.ToBeRepaired),
|
||||
(models.Repair, states.Physical.Repaired),
|
||||
(models.ToPrepare, states.Physical.Preparing),
|
||||
(models.ReadyToUse, states.Physical.ReadyToBeUsed),
|
||||
(models.ToPrepare, states.Physical.Preparing),
|
||||
(models.Prepare, states.Physical.Prepared)
|
||||
])
|
||||
def test_generic_event(event_model: models.Event, user: UserClient):
|
||||
def test_generic_event(event_model_state: Tuple[models.Event, states.Trading], user: UserClient):
|
||||
"""Tests POSTing all generic events."""
|
||||
event_model, state = event_model_state
|
||||
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
|
||||
event = {'type': event_model.t, 'devices': [snapshot['device']['id']]}
|
||||
event, _ = user.post(event, res=models.Event)
|
||||
assert event['devices'][0]['id'] == snapshot['device']['id']
|
||||
device, _ = user.get(res=Device, item=snapshot['device']['id'])
|
||||
assert device['events'][-1]['id'] == event['id']
|
||||
assert device['physical'] == state.name
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
|
@ -214,6 +218,8 @@ def test_live():
|
|||
assert live['ip'] == '79.147.10.10'
|
||||
assert live['subdivision'] == 'ES-CA'
|
||||
assert live['country'] == 'ES'
|
||||
device, _ = client.get(res=Device, item=live['device']['id'])
|
||||
assert device['physical'] == states.Physical.InUse.name
|
||||
|
||||
|
||||
@pytest.mark.xfail(reson='Functionality not developed.')
|
||||
|
@ -226,14 +232,15 @@ def test_reserve(user: UserClient):
|
|||
"""Performs a reservation and then cancels it."""
|
||||
|
||||
|
||||
@pytest.mark.parametrize('event_model', [
|
||||
models.Sell,
|
||||
models.Donate,
|
||||
models.Rent,
|
||||
models.DisposeProduct
|
||||
@pytest.mark.parametrize('event_model_state', [
|
||||
(models.Sell, states.Trading.Sold),
|
||||
(models.Donate, states.Trading.Donated),
|
||||
(models.Rent, states.Trading.Renting),
|
||||
(models.DisposeProduct, states.Trading.ProductDisposed)
|
||||
])
|
||||
def test_trade(event_model: models.Event, user: UserClient):
|
||||
"""Tests POSTing all generic events."""
|
||||
def test_trade(event_model_state: Tuple[models.Event, states.Trading], user: UserClient):
|
||||
"""Tests POSTing all Trade events."""
|
||||
event_model, state = event_model_state
|
||||
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
|
||||
event = {
|
||||
'type': event_model.t,
|
||||
|
@ -246,6 +253,7 @@ def test_trade(event_model: models.Event, user: UserClient):
|
|||
assert event['devices'][0]['id'] == snapshot['device']['id']
|
||||
device, _ = user.get(res=Device, item=snapshot['device']['id'])
|
||||
assert device['events'][-1]['id'] == event['id']
|
||||
assert device['trading'] == state.name
|
||||
|
||||
|
||||
@pytest.mark.xfail(reson='Develop migrate')
|
||||
|
@ -259,6 +267,7 @@ def test_price_custom():
|
|||
chassis=ComputerChassis.Docking)
|
||||
price = models.Price(price=25.25, currency=Currency.EUR)
|
||||
price.device = computer
|
||||
assert computer.price == price
|
||||
db.session.add(computer)
|
||||
db.session.commit()
|
||||
|
||||
|
@ -268,3 +277,6 @@ def test_price_custom():
|
|||
assert p['device']['id'] == price.device.id == computer.id
|
||||
assert p['price'] == 25.25
|
||||
assert p['currency'] == Currency.EUR.name == 'EUR'
|
||||
|
||||
c, _ = client.get(res=Device, item=computer.id)
|
||||
assert c['price']['id'] == p['id']
|
||||
|
|
|
@ -3,11 +3,13 @@ from distutils.version import StrictVersion
|
|||
import pytest
|
||||
|
||||
from ereuse_devicehub.db import db
|
||||
from ereuse_devicehub.resources.device.models import Computer, Desktop
|
||||
from ereuse_devicehub.resources.enums import Bios, ComputerChassis, ImageMimeTypes, Orientation, \
|
||||
RatingSoftware
|
||||
from ereuse_devicehub.resources.event.models import PhotoboxRate, WorkbenchRate
|
||||
from ereuse_devicehub.resources.image.models import Image, ImageList
|
||||
from ereuse_devicehub.resources.device.models import Computer, Desktop, HardDrive, Processor, \
|
||||
RamModule
|
||||
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ComputerChassis, \
|
||||
FunctionalityRange, RatingSoftware
|
||||
from ereuse_devicehub.resources.event.models import AggregateRate, BenchmarkDataStorage, \
|
||||
BenchmarkProcessor, EreusePrice, WorkbenchRate
|
||||
from ereuse_devicehub.resources.event.rate import main
|
||||
from tests import conftest
|
||||
|
||||
|
||||
|
@ -26,17 +28,63 @@ def test_workbench_rate_db():
|
|||
db.session.commit()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||
def test_photobox_rate_db():
|
||||
pc = Desktop(serial_number='24', chassis=ComputerChassis.Tower)
|
||||
image = Image(name='foo',
|
||||
content=b'123',
|
||||
file_format=ImageMimeTypes.jpg,
|
||||
orientation=Orientation.Horizontal,
|
||||
image_list=ImageList(device=pc))
|
||||
rate = PhotoboxRate(image=image,
|
||||
software=RatingSoftware.ECost,
|
||||
version=StrictVersion('1.0'),
|
||||
device=pc)
|
||||
db.session.add(rate)
|
||||
db.session.commit()
|
||||
@pytest.mark.xfail(reason='AggreagteRate only takes data from WorkbenchRate as for now')
|
||||
def test_rate_workbench_then_manual():
|
||||
"""Checks that a new AggregateRate is generated with a new rate
|
||||
value when a ManualRate is performed after performing a
|
||||
WorkbenchRate.
|
||||
|
||||
The new AggregateRate needs to be computed by the values of
|
||||
the WorkbenchRate + new values from ManualRate.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||
def test_rate():
|
||||
"""Test generating an AggregateRate for a given PC / components /
|
||||
WorkbenchRate ensuring results and relationships between
|
||||
pc - rate - workbenchRate - price.
|
||||
"""
|
||||
rate = WorkbenchRate(
|
||||
appearance_range=AppearanceRange.A,
|
||||
functionality_range=FunctionalityRange.A
|
||||
)
|
||||
pc = Desktop()
|
||||
hdd = HardDrive(size=476940)
|
||||
hdd.events_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8))
|
||||
cpu = Processor(cores=2, speed=3.4)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=27136.44))
|
||||
pc.components |= {
|
||||
hdd,
|
||||
RamModule(size=4096, speed=1600),
|
||||
RamModule(size=2048, speed=1067),
|
||||
cpu
|
||||
}
|
||||
rate.device = pc
|
||||
events = main.main(rate, RatingSoftware.ECost, StrictVersion('1.0'))
|
||||
price = next(e for e in events if isinstance(e, EreusePrice))
|
||||
assert price.price == 92.2
|
||||
assert price.retailer.standard.amount == 40.97
|
||||
assert price.platform.standard.amount == 18.84
|
||||
assert price.refurbisher.standard.amount == 32.38
|
||||
assert price.price >= price.retailer.standard.amount \
|
||||
+ price.platform.standard.amount \
|
||||
+ price.refurbisher.standard.amount
|
||||
assert price.retailer.warranty2.amount == 55.30
|
||||
assert price.platform.warranty2.amount == 25.43
|
||||
assert price.refurbisher.warranty2.amount == 43.72
|
||||
assert price.warranty2 == 124.45
|
||||
# Checks relationships
|
||||
workbench_rate = next(e for e in events if isinstance(e, WorkbenchRate))
|
||||
aggregate_rate = next(e for e in events if isinstance(e, AggregateRate))
|
||||
assert price.rating == aggregate_rate
|
||||
assert aggregate_rate.workbench == workbench_rate
|
||||
assert aggregate_rate.rating == workbench_rate.rating == 4.61
|
||||
assert aggregate_rate.software == workbench_rate.software == RatingSoftware.ECost
|
||||
assert aggregate_rate.version == StrictVersion('1.0')
|
||||
assert aggregate_rate.appearance == workbench_rate.appearance
|
||||
assert aggregate_rate.functionality == workbench_rate.functionality
|
||||
assert aggregate_rate.rating_range == workbench_rate.rating_range
|
||||
assert cpu.rate == pc.rate == hdd.rate == aggregate_rate
|
||||
assert cpu.price == pc.price == aggregate_rate.price == hdd.price == price
|
||||
|
|
417
tests/test_rate_workbench_v1.py
Normal file
417
tests/test_rate_workbench_v1.py
Normal file
|
@ -0,0 +1,417 @@
|
|||
"""
|
||||
Tests of compute rating for every component in a Device
|
||||
Rates test done:
|
||||
-DataStorage
|
||||
-RamModule
|
||||
-Processor
|
||||
|
||||
Excluded cases in tests
|
||||
|
||||
- No Processor
|
||||
-
|
||||
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from ereuse_devicehub.resources.device.models import Desktop, HardDrive, Processor, RamModule
|
||||
from ereuse_devicehub.resources.enums import AppearanceRange, FunctionalityRange
|
||||
from ereuse_devicehub.resources.event.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
||||
WorkbenchRate
|
||||
from ereuse_devicehub.resources.event.rate.workbench.v1_0 import DataStorageRate, ProcessorRate, \
|
||||
RamRate, Rate
|
||||
|
||||
|
||||
def test_rate_data_storage_rate():
|
||||
"""
|
||||
Test to check if compute data storage rate have same value than previous score version;
|
||||
id = pc_1193, pc_1201, pc_79, pc_798
|
||||
"""
|
||||
|
||||
hdd_1969 = HardDrive(size=476940)
|
||||
hdd_1969.events_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8))
|
||||
|
||||
data_storage_rate = DataStorageRate().compute([hdd_1969], WorkbenchRate())
|
||||
|
||||
assert round(data_storage_rate, 2) == 4.02, 'DataStorageRate returns incorrect value(rate)'
|
||||
|
||||
hdd_3054 = HardDrive(size=476940)
|
||||
hdd_3054.events_one.add(BenchmarkDataStorage(read_speed=158, write_speed=34.7))
|
||||
|
||||
# calculate DataStorage Rate
|
||||
data_storage_rate = DataStorageRate().compute([hdd_3054], WorkbenchRate())
|
||||
|
||||
assert round(data_storage_rate, 2) == 4.07, 'DataStorageRate returns incorrect value(rate)'
|
||||
|
||||
hdd_81 = HardDrive(size=76319)
|
||||
hdd_81.events_one.add(BenchmarkDataStorage(read_speed=72.2, write_speed=24.3))
|
||||
|
||||
data_storage_rate = DataStorageRate().compute([hdd_81], WorkbenchRate())
|
||||
|
||||
assert round(data_storage_rate, 2) == 2.61, 'DataStorageRate returns incorrect value(rate)'
|
||||
|
||||
hdd_1556 = HardDrive(size=152587)
|
||||
hdd_1556.events_one.add(BenchmarkDataStorage(read_speed=78.1, write_speed=24.4))
|
||||
|
||||
data_storage_rate = DataStorageRate().compute([hdd_1556], WorkbenchRate())
|
||||
|
||||
assert round(data_storage_rate, 2) == 3.70, 'DataStorageRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
def test_rate_data_storage_size_is_null():
|
||||
"""
|
||||
Test where input DataStorage.size = NULL, BenchmarkDataStorage.read_speed = 0,
|
||||
BenchmarkDataStorage.write_speed = 0 is like no DataStorage has been detected;
|
||||
id = pc_2992
|
||||
"""
|
||||
|
||||
hdd_null = HardDrive(size=None)
|
||||
hdd_null.events_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0))
|
||||
|
||||
data_storage_rate = DataStorageRate().compute([hdd_null], WorkbenchRate())
|
||||
assert data_storage_rate is None
|
||||
|
||||
|
||||
def test_rate_no_data_storage():
|
||||
"""
|
||||
Test without data storage devices
|
||||
"""
|
||||
hdd_null = HardDrive()
|
||||
hdd_null.events_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0))
|
||||
data_storage_rate = DataStorageRate().compute([hdd_null], WorkbenchRate())
|
||||
assert data_storage_rate is None
|
||||
|
||||
|
||||
# RAM MODULE DEVICE TEST
|
||||
|
||||
|
||||
def test_rate_ram_rate():
|
||||
"""
|
||||
Test to check if compute ram rate have same value than previous score version
|
||||
only with 1 RamModule; id = pc_1201
|
||||
"""
|
||||
|
||||
ram1 = RamModule(size=2048, speed=1333)
|
||||
|
||||
ram_rate = RamRate().compute([ram1], WorkbenchRate())
|
||||
|
||||
assert round(ram_rate, 2) == 2.02, 'RamRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
def test_rate_ram_rate_2modules():
|
||||
"""
|
||||
Test to check if compute ram rate have same value than previous score version
|
||||
with 2 RamModule; id = pc_1193
|
||||
"""
|
||||
|
||||
ram1 = RamModule(size=4096, speed=1600)
|
||||
ram2 = RamModule(size=2048, speed=1067)
|
||||
|
||||
ram_rate = RamRate().compute([ram1, ram2], WorkbenchRate())
|
||||
|
||||
assert round(ram_rate, 2) == 3.79, 'RamRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
def test_rate_ram_rate_4modules():
|
||||
"""
|
||||
Test to check if compute ram rate have same value than previous score version
|
||||
with 2 RamModule; id = pc_79
|
||||
"""
|
||||
|
||||
ram1 = RamModule(size=512, speed=667)
|
||||
ram2 = RamModule(size=512, speed=800)
|
||||
ram3 = RamModule(size=512, speed=667)
|
||||
ram4 = RamModule(size=512, speed=533)
|
||||
|
||||
ram_rate = RamRate().compute([ram1, ram2, ram3, ram4], WorkbenchRate())
|
||||
|
||||
assert round(ram_rate, 2) == 1.99, 'RamRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
def test_rate_ram_module_size_is_0():
|
||||
"""
|
||||
Test where input data RamModule.size = 0; is like no RamModule has been detected; id = pc_798
|
||||
"""
|
||||
|
||||
ram0 = RamModule(size=0, speed=888)
|
||||
|
||||
ram_rate = RamRate().compute([ram0], WorkbenchRate())
|
||||
assert ram_rate is None
|
||||
|
||||
|
||||
def test_rate_ram_speed_is_null():
|
||||
"""
|
||||
Test where RamModule.speed is NULL (not detected) but has size.
|
||||
Pc ID = 795(1542), 745(1535), 804(1549)
|
||||
"""
|
||||
|
||||
ram0 = RamModule(size=2048, speed=None)
|
||||
|
||||
ram_rate = RamRate().compute([ram0], WorkbenchRate())
|
||||
|
||||
assert round(ram_rate, 2) == 1.85, 'RamRate returns incorrect value(rate)'
|
||||
|
||||
ram0 = RamModule(size=1024, speed=None)
|
||||
|
||||
ram_rate = RamRate().compute([ram0], WorkbenchRate())
|
||||
|
||||
assert round(ram_rate, 2) == 1.25, 'RamRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
def test_rate_no_ram_module():
|
||||
"""
|
||||
Test without RamModule
|
||||
"""
|
||||
ram0 = RamModule()
|
||||
|
||||
ram_rate = RamRate().compute([ram0], WorkbenchRate())
|
||||
assert ram_rate is None
|
||||
|
||||
|
||||
# PROCESSOR DEVICE TEST
|
||||
|
||||
def test_rate_processor_rate():
|
||||
"""
|
||||
Test to check if compute processor rate have same value than previous score version
|
||||
only with 1 core; id = 79
|
||||
"""
|
||||
|
||||
cpu = Processor(cores=1, speed=1.6)
|
||||
# add score processor benchmark
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=3192.34))
|
||||
|
||||
processor_rate = ProcessorRate().compute(cpu, WorkbenchRate())
|
||||
|
||||
assert processor_rate == 1, 'ProcessorRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
def test_rate_processor_rate_2cores():
|
||||
"""
|
||||
Test to check if compute processor rate have same value than previous score version
|
||||
with 2 cores; id = pc_1193, pc_1201
|
||||
"""
|
||||
|
||||
cpu = Processor(cores=2, speed=3.4)
|
||||
# add score processor benchmark
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=27136.44))
|
||||
|
||||
processor_rate = ProcessorRate().compute(cpu, WorkbenchRate())
|
||||
|
||||
assert round(processor_rate, 2) == 3.95, 'ProcessorRate returns incorrect value(rate)'
|
||||
|
||||
cpu = Processor(cores=2, speed=3.3)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=26339.48))
|
||||
|
||||
processor_rate = ProcessorRate().compute(cpu, WorkbenchRate())
|
||||
|
||||
assert round(processor_rate, 2) == 3.93, 'ProcessorRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='Debug test')
|
||||
def test_rate_processor_with_null_cores():
|
||||
"""
|
||||
Test with processor device have null number of cores
|
||||
"""
|
||||
cpu = Processor(cores=None, speed=3.3)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=0))
|
||||
|
||||
processor_rate = ProcessorRate().compute(cpu, WorkbenchRate())
|
||||
|
||||
assert processor_rate == 1, 'ProcessorRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='Debug test')
|
||||
def test_rate_processor_with_null_speed():
|
||||
"""
|
||||
Test with processor device have null speed value
|
||||
"""
|
||||
cpu = Processor(cores=1, speed=None)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=0))
|
||||
|
||||
processor_rate = ProcessorRate().compute(cpu, WorkbenchRate())
|
||||
|
||||
assert processor_rate == 1.06, 'ProcessorRate returns incorrect value(rate)'
|
||||
|
||||
|
||||
def test_rate_computer_rate():
|
||||
""" Test rate v1
|
||||
|
||||
pc_1193 = Computer()
|
||||
price = 92.2
|
||||
# add components characteristics of pc with id = 1193
|
||||
hdd_1969 = HardDrive(size=476940)
|
||||
hdd_1969.events_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8))
|
||||
ram1 = RamModule(size=4096, speed=1600)
|
||||
ram2 = RamModule(size=2048, speed=1067)
|
||||
cpu = Processor(cores=2, speed=3.4)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=27136.44))
|
||||
pc_1193.components.add(hdd_1969, ram1, ram2, cpu)
|
||||
# add functionality and appearance range
|
||||
rate_pc_1193 = WorkbenchRate(appearance_range=AppearanceRange.A, functionality_range=FunctionalityRange.A)
|
||||
# add component rate
|
||||
HDD_rate = 4.02
|
||||
RAM_rate = 3.79
|
||||
Processor_rate = 3.95
|
||||
Rating = 4.61
|
||||
|
||||
pc_1201 = Computer()
|
||||
price = 69.6
|
||||
hdd_3054 = HardDrive(size=476940)
|
||||
hdd_3054.events_one.add(BenchmarkDataStorage(read_speed=158, write_speed=34.7))
|
||||
ram1 = RamModule(size=2048, speed=1333)
|
||||
cpu = Processor(cores=2, speed=3.3)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=26339.48))
|
||||
pc_1201.components.add(hdd_3054, ram1, cpu)
|
||||
# add functionality and appearance range
|
||||
rate_pc_1201 = WorkbenchRate(appearance_range=AppearanceRange.B, functionality_range=FunctionalityRange.A)
|
||||
# add component rate
|
||||
HDD_rate = 4.07
|
||||
RAM_rate = 2.02
|
||||
Processor_rate = 3.93
|
||||
Rating = 3.48
|
||||
|
||||
pc_79 = Computer()
|
||||
price = VeryLow
|
||||
hdd_81 = HardDrive(size=76319)
|
||||
hdd_81.events_one.add(BenchmarkDataStorage(read_speed=72.2, write_speed=24.3))
|
||||
ram1 = RamModule(size=512, speed=667)
|
||||
ram2 = RamModule(size=512, speed=800)
|
||||
ram3 = RamModule(size=512, speed=667)
|
||||
ram4 = RamModule(size=512, speed=533)
|
||||
cpu = Processor(cores=1, speed=1.6)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=3192.34))
|
||||
pc_79.components.add(hdd_81, ram1, ram2, ram3, ram4, cpu)
|
||||
# add functionality and appearance range
|
||||
rate_pc_79 = WorkbenchRate(appearance_range=AppearanceRange.C, functionality_range=FunctionalityRange.A)
|
||||
# add component rate
|
||||
HDD_rate = 2.61
|
||||
RAM_rate = 1.99
|
||||
Processor_rate = 1
|
||||
Rating = 1.58
|
||||
|
||||
pc_798 = Computer()
|
||||
price = 50
|
||||
hdd_1556 = HardDrive(size=152587)
|
||||
hdd_1556.events_one.add(BenchmarkDataStorage(read_speed=78.1, write_speed=24.4))
|
||||
ram0 = RamModule(size=0, speed=None)
|
||||
cpu = Processor(cores=2, speed=2.5)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=9974.3))
|
||||
pc_798.components.add(hdd_1556, ram0, cpu)
|
||||
# add functionality and appearance range
|
||||
rate_pc_798 = WorkbenchRate(appearance_range=AppearanceRange.B, functionality_range=FunctionalityRange.A)
|
||||
# add component rate
|
||||
HDD_rate = 3.7
|
||||
RAM_rate = 1
|
||||
Processor_rate = 4.09
|
||||
Rating = 2.5
|
||||
"""
|
||||
|
||||
# Create a new Computer with components characteristics of pc with id = 1193
|
||||
pc_test = Desktop()
|
||||
data_storage = HardDrive(size=476940)
|
||||
data_storage.events_one.add(BenchmarkDataStorage(read_speed=126, write_speed=29.8))
|
||||
cpu = Processor(cores=2, speed=3.4)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=27136.44))
|
||||
pc_test.components |= {
|
||||
data_storage,
|
||||
RamModule(size=4096, speed=1600),
|
||||
RamModule(size=2048, speed=1067),
|
||||
cpu
|
||||
}
|
||||
# add functionality and appearance range
|
||||
rate_pc = WorkbenchRate(appearance_range=AppearanceRange.A,
|
||||
functionality_range=FunctionalityRange.A)
|
||||
# Compute all components rates and general rating
|
||||
Rate().compute(pc_test, rate_pc)
|
||||
|
||||
assert round(rate_pc.ram, 2) == 3.79
|
||||
|
||||
assert round(rate_pc.data_storage, 2) == 4.02
|
||||
|
||||
assert round(rate_pc.processor, 2) == 3.95
|
||||
|
||||
assert round(rate_pc.rating, 2) == 4.61
|
||||
|
||||
# Create a new Computer with components characteristics of pc with id = 1201
|
||||
pc_test = Desktop()
|
||||
data_storage = HardDrive(size=476940)
|
||||
data_storage.events_one.add(BenchmarkDataStorage(read_speed=158, write_speed=34.7))
|
||||
cpu = Processor(cores=2, speed=3.3)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=26339.48))
|
||||
pc_test.components |= {
|
||||
data_storage,
|
||||
RamModule(size=2048, speed=1333),
|
||||
cpu
|
||||
}
|
||||
# add functionality and appearance range
|
||||
rate_pc = WorkbenchRate(appearance_range=AppearanceRange.B,
|
||||
functionality_range=FunctionalityRange.A)
|
||||
# Compute all components rates and general rating
|
||||
Rate().compute(pc_test, rate_pc)
|
||||
|
||||
assert round(rate_pc.ram, 2) == 2.02
|
||||
|
||||
assert round(rate_pc.data_storage, 2) == 4.07
|
||||
|
||||
assert round(rate_pc.processor, 2) == 3.93
|
||||
|
||||
assert round(rate_pc.rating, 2) == 3.48
|
||||
|
||||
# Create a new Computer with components characteristics of pc with id = 79
|
||||
pc_test = Desktop()
|
||||
data_storage = HardDrive(size=76319)
|
||||
data_storage.events_one.add(BenchmarkDataStorage(read_speed=72.2, write_speed=24.3))
|
||||
cpu = Processor(cores=1, speed=1.6)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=3192.34))
|
||||
pc_test.components |= {
|
||||
data_storage,
|
||||
RamModule(size=512, speed=667),
|
||||
RamModule(size=512, speed=800),
|
||||
RamModule(size=512, speed=667),
|
||||
RamModule(size=512, speed=533),
|
||||
cpu
|
||||
}
|
||||
# add functionality and appearance range
|
||||
rate_pc = WorkbenchRate(appearance_range=AppearanceRange.C,
|
||||
functionality_range=FunctionalityRange.A)
|
||||
# Compute all components rates and general rating
|
||||
Rate().compute(pc_test, rate_pc)
|
||||
|
||||
assert round(rate_pc.ram, 2) == 1.99
|
||||
|
||||
assert round(rate_pc.data_storage, 2) == 2.61
|
||||
|
||||
assert round(rate_pc.processor, 2) == 1
|
||||
|
||||
assert round(rate_pc.rating, 2) == 1.58
|
||||
|
||||
# Create a new Computer with components characteristics of pc with id = 798
|
||||
pc_test = Desktop()
|
||||
data_storage = HardDrive(size=152587)
|
||||
data_storage.events_one.add(BenchmarkDataStorage(read_speed=78.1, write_speed=24.4))
|
||||
cpu = Processor(cores=2, speed=2.5)
|
||||
cpu.events_one.add(BenchmarkProcessor(rate=9974.3))
|
||||
pc_test.components |= {
|
||||
data_storage,
|
||||
RamModule(size=0, speed=None),
|
||||
cpu
|
||||
}
|
||||
# add functionality and appearance range
|
||||
rate_pc = WorkbenchRate(appearance_range=AppearanceRange.B,
|
||||
functionality_range=FunctionalityRange.A)
|
||||
# Compute all components rates and general rating
|
||||
Rate().compute(pc_test, rate_pc)
|
||||
|
||||
assert round(rate_pc.ram, 2) == 1
|
||||
|
||||
assert round(rate_pc.data_storage, 2) == 3.7
|
||||
|
||||
assert round(rate_pc.processor, 2) == 4.09
|
||||
|
||||
assert round(rate_pc.rating, 2) == 2.5
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='Data Storage rate actually requires a DSSBenchmark')
|
||||
def test_rate_computer_with_data_storage_without_benchmark():
|
||||
"""For example if the data storage was introduced manually
|
||||
or comes from an old version without benchmark."""
|
|
@ -1,9 +1,9 @@
|
|||
from datetime import datetime, timedelta, timezone
|
||||
from distutils.version import StrictVersion
|
||||
from typing import List, Tuple
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from boltons import urlutils
|
||||
from teal.db import UniqueViolation
|
||||
from teal.marshmallow import ValidationError
|
||||
|
||||
|
@ -13,8 +13,7 @@ from ereuse_devicehub.devicehub import Devicehub
|
|||
from ereuse_devicehub.resources.device import models as m
|
||||
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
||||
from ereuse_devicehub.resources.device.sync import MismatchBetweenTagsAndHid
|
||||
from ereuse_devicehub.resources.enums import Bios, ComputerChassis, RatingSoftware, \
|
||||
SnapshotSoftware
|
||||
from ereuse_devicehub.resources.enums import ComputerChassis, SnapshotSoftware
|
||||
from ereuse_devicehub.resources.event.models import AggregateRate, BenchmarkProcessor, \
|
||||
EraseSectors, Event, Snapshot, SnapshotRequest, WorkbenchRate
|
||||
from ereuse_devicehub.resources.tag import Tag
|
||||
|
@ -37,21 +36,11 @@ def test_snapshot_model():
|
|||
elapsed=timedelta(seconds=25))
|
||||
snapshot.device = device
|
||||
snapshot.request = SnapshotRequest(request={'foo': 'bar'})
|
||||
snapshot.events.add(WorkbenchRate(processor=0.1,
|
||||
ram=1.0,
|
||||
bios=Bios.A,
|
||||
labelling=False,
|
||||
graphic_card=0.1,
|
||||
data_storage=4.1,
|
||||
software=RatingSoftware.ECost,
|
||||
version=StrictVersion('1.0'),
|
||||
device=device))
|
||||
db.session.add(snapshot)
|
||||
db.session.commit()
|
||||
device = m.Desktop.query.one() # type: m.Desktop
|
||||
e1, e2 = device.events
|
||||
e1 = device.events[0]
|
||||
assert isinstance(e1, Snapshot), 'Creation order must be preserved: 1. snapshot, 2. WR'
|
||||
assert isinstance(e2, WorkbenchRate)
|
||||
db.session.delete(device)
|
||||
db.session.commit()
|
||||
assert Snapshot.query.one_or_none() is None
|
||||
|
@ -59,6 +48,8 @@ def test_snapshot_model():
|
|||
assert User.query.one() is not None
|
||||
assert m.Desktop.query.one_or_none() is None
|
||||
assert m.Device.query.one_or_none() is None
|
||||
# Check properties
|
||||
assert device.url == urlutils.URL('http://localhost/devices/1')
|
||||
|
||||
|
||||
def test_snapshot_schema(app: Devicehub):
|
||||
|
@ -321,27 +312,37 @@ def test_erase(user: UserClient):
|
|||
assert step['type'] == 'StepZero'
|
||||
assert step['error'] is False
|
||||
assert 'num' not in step
|
||||
assert storage['privacy'] == erasure['device']['privacy'] == 'EraseSectors'
|
||||
|
||||
# Let's try a second erasure with an error
|
||||
s['uuid'] = uuid4()
|
||||
s['components'][0]['events'][0]['error'] = True
|
||||
snapshot, _ = user.post(s, res=Snapshot)
|
||||
assert snapshot['components'][0]['hid'] == 'c1mr-c1s-c1ml'
|
||||
assert snapshot['components'][0]['privacy'] == 'EraseSectorsError'
|
||||
|
||||
|
||||
def test_snapshot_computer_monitor(user: UserClient):
|
||||
s = file('computer-monitor.snapshot')
|
||||
snapshot_and_check(user, s, event_types=('AppRate',))
|
||||
snapshot_and_check(user, s, event_types=('ManualRate',))
|
||||
# todo check that ManualRate has generated an AggregateRate
|
||||
|
||||
|
||||
def test_snapshot_mobile_smartphone(user: UserClient):
|
||||
s = file('smartphone.snapshot')
|
||||
snapshot_and_check(user, s, event_types=('AppRate',))
|
||||
snapshot_and_check(user, s, event_types=('ManualRate',))
|
||||
# todo check that ManualRate has generated an AggregateRate
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='Test not developed')
|
||||
def test_snapshot_components_none():
|
||||
"""
|
||||
Tests that a snapshot without components does not
|
||||
remove them from the computer.
|
||||
"""
|
||||
# todo test
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='Test not developed')
|
||||
def test_snapshot_components_empty():
|
||||
"""
|
||||
Tests that a snapshot whose components are an empty list remove
|
||||
|
|
|
@ -27,7 +27,7 @@ def test_workbench_server_condensed(user: UserClient):
|
|||
file('workbench-server-3.erase'),
|
||||
file('workbench-server-4.install')
|
||||
))
|
||||
s['components'][5]['events'] = [file('workbench-server-3.erase')]
|
||||
s['components'][5]['events'].append(file('workbench-server-3.erase'))
|
||||
# Create tags
|
||||
for t in s['device']['tags']:
|
||||
user.post({'id': t['id']}, res=Tag)
|
||||
|
@ -35,7 +35,7 @@ def test_workbench_server_condensed(user: UserClient):
|
|||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
events = snapshot['events']
|
||||
assert {(event['type'], event['device']) for event in events} == {
|
||||
# todo missing Rate event aggregating the rates
|
||||
('AggregateRate', 1),
|
||||
('WorkbenchRate', 1),
|
||||
('BenchmarkProcessorSysbench', 5),
|
||||
('StressTest', 1),
|
||||
|
@ -45,10 +45,24 @@ def test_workbench_server_condensed(user: UserClient):
|
|||
('Install', 6),
|
||||
('EraseSectors', 7),
|
||||
('BenchmarkDataStorage', 6),
|
||||
('BenchmarkDataStorage', 7),
|
||||
('TestDataStorage', 6)
|
||||
}
|
||||
assert snapshot['closed']
|
||||
assert not snapshot['error']
|
||||
device, _ = user.get(res=Device, item=snapshot['device']['id'])
|
||||
assert device['dataStorageSize'] == 1100
|
||||
assert device['chassis'] == 'Tower'
|
||||
assert device['hid'] == 'd1mr-d1s-d1ml'
|
||||
assert device['graphicCardModel'] == device['components'][0]['model'] == 'gc1-1ml'
|
||||
assert device['networkSpeeds'] == [1000, 58]
|
||||
assert device['processorModel'] == device['components'][3]['model'] == 'p1-1ml'
|
||||
assert device['ramSize'] == 2048, 'There are 3 RAM: 2 x 1024 and 1 None sizes'
|
||||
assert device['rate']['closed']
|
||||
assert not device['rate']['error']
|
||||
assert device['rate']['rating'] == 0
|
||||
assert device['rate']['workbench']
|
||||
assert device['tags'][0]['id'] == 'tag1'
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='Functionality not yet developed.')
|
||||
|
@ -122,8 +136,9 @@ def test_workbench_server_phases(user: UserClient):
|
|||
def test_real_hp_11(user: UserClient):
|
||||
s = file('real-hp.snapshot.11')
|
||||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
assert snapshot['device']['hid'] == 'hewlett-packard-czc0408yjg-hp_compaq_8100_elite_sff'
|
||||
assert snapshot['device']['chassis'] == 'Tower'
|
||||
pc = snapshot['device']
|
||||
assert pc['hid'] == 'hewlett-packard-czc0408yjg-hp_compaq_8100_elite_sff'
|
||||
assert pc['chassis'] == 'Tower'
|
||||
assert set(e['type'] for e in snapshot['events']) == {
|
||||
'BenchmarkDataStorage',
|
||||
'BenchmarkProcessor',
|
||||
|
@ -133,6 +148,10 @@ def test_real_hp_11(user: UserClient):
|
|||
'StressTest'
|
||||
}
|
||||
assert len(list(e['type'] for e in snapshot['events'])) == 6
|
||||
assert pc['networkSpeeds'] == [1000, None], 'Device has no WiFi'
|
||||
assert pc['processorModel'] == 'intel core i3 cpu 530 @ 2.93ghz'
|
||||
assert pc['ramSize'] == 8192
|
||||
assert pc['dataStorageSize'] == 305245
|
||||
|
||||
|
||||
def test_real_toshiba_11(user: UserClient):
|
||||
|
@ -140,7 +159,7 @@ def test_real_toshiba_11(user: UserClient):
|
|||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
|
||||
|
||||
def test_real_eee_1001pxd(user: UserClient):
|
||||
def test_snapshot_real_eee_1001pxd(user: UserClient):
|
||||
"""
|
||||
Checks the values of the device, components,
|
||||
events and their relationships of a real pc.
|
||||
|
@ -155,6 +174,7 @@ def test_real_eee_1001pxd(user: UserClient):
|
|||
assert pc['manufacturer'] == 'asustek computer inc.'
|
||||
assert pc['hid'] == 'asustek_computer_inc-b8oaas048286-1001pxd'
|
||||
assert pc['tags'] == []
|
||||
assert pc['networkSpeeds'] == [100, 0], 'Although it has WiFi we do not know the speed'
|
||||
components = snapshot['components']
|
||||
wifi = components[0]
|
||||
assert wifi['hid'] == 'qualcomm_atheros-74_2f_68_8b_fd_c8-ar9285_wireless_network_adapter'
|
||||
|
@ -170,7 +190,7 @@ def test_real_eee_1001pxd(user: UserClient):
|
|||
assert cpu['threads'] == 1
|
||||
assert cpu['speed'] == 1.667
|
||||
assert 'hid' not in cpu
|
||||
assert cpu['model'] == 'intel atom cpu n455 @ 1.66ghz'
|
||||
assert pc['processorModel'] == cpu['model'] == 'intel atom cpu n455 @ 1.66ghz'
|
||||
cpu, _ = user.get(res=Device, item=cpu['id'])
|
||||
events = cpu['events']
|
||||
sysbench = next(e for e in events if e['type'] == em.BenchmarkProcessorSysbench.t)
|
||||
|
@ -204,6 +224,7 @@ def test_real_eee_1001pxd(user: UserClient):
|
|||
ram = components[6]
|
||||
assert ram['interface'] == 'DDR2'
|
||||
assert ram['speed'] == 667
|
||||
assert pc['ramSize'] == ram['size'] == 1024
|
||||
hdd = components[7]
|
||||
assert hdd['type'] == 'HardDrive'
|
||||
assert hdd['hid'] == 'hitachi-e2024242cv86hj-hts54322'
|
||||
|
@ -223,6 +244,7 @@ def test_real_eee_1001pxd(user: UserClient):
|
|||
assert erase['startTime']
|
||||
assert erase['zeros'] is False
|
||||
assert erase['error'] is False
|
||||
assert hdd['privacy'] == 'EraseBasic'
|
||||
mother = components[8]
|
||||
assert mother['hid'] == 'asustek_computer_inc-eee0123456789-1001pxd'
|
||||
|
||||
|
@ -243,6 +265,11 @@ def test_real_eee_1000h(user: UserClient):
|
|||
snapshot, _ = user.post(res=em.Snapshot, data=s)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason='We do not have a snapshot file to use')
|
||||
def test_real_full_with_workbench_rate(user: UserClient):
|
||||
pass
|
||||
|
||||
|
||||
SNAPSHOTS_NEED_ID = {
|
||||
'box-xavier.snapshot.json',
|
||||
'custom.lshw.snapshot.json',
|
||||
|
|
Reference in a new issue