Add computer monitor; dummy; fix
This commit is contained in:
parent
7306055d28
commit
21d1a96aff
13
README.md
13
README.md
|
@ -44,12 +44,23 @@ $ sudo -u postgres -i
|
||||||
postgres$ createdb dh-db1
|
postgres$ createdb dh-db1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Then execute, in the same directory where `app.py` is:
|
||||||
|
```bash
|
||||||
|
$ flask init-db
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates the tables in the database you created before.
|
||||||
|
|
||||||
And then execute, in the same directory where `app.py` is:
|
Finally, run the app:
|
||||||
```bash
|
```bash
|
||||||
$ flask run
|
$ flask run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
See the [Flask quickstart](http://flask.pocoo.org/docs/1.0/quickstart/)
|
See the [Flask quickstart](http://flask.pocoo.org/docs/1.0/quickstart/)
|
||||||
for more info.
|
for more info.
|
||||||
|
|
||||||
|
Devicehub has many commands that allows you to administrate it. You
|
||||||
|
can, for example, create a dummy database of devices with ``flask dummy``
|
||||||
|
or create users with ``flask create-user``. See all the
|
||||||
|
available commands by just executing ``flask``.
|
|
@ -1,14 +1,14 @@
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device import ComponentDef, ComputerDef, DataStorageDef, \
|
from ereuse_devicehub.resources.device import ComponentDef, ComputerDef, ComputerMonitorDef, \
|
||||||
DesktopDef, DeviceDef, GraphicCardDef, HardDriveDef, LaptopDef, MicrotowerDef, \
|
DataStorageDef, DesktopDef, DeviceDef, GraphicCardDef, HardDriveDef, LaptopDef, MicrotowerDef, \
|
||||||
MotherboardDef, NetbookDef, NetworkAdapterDef, ProcessorDef, RamModuleDef, ServerDef, \
|
MotherboardDef, NetbookDef, NetworkAdapterDef, ProcessorDef, RamModuleDef, ServerDef, \
|
||||||
SolidStateDriveDef
|
SolidStateDriveDef
|
||||||
from ereuse_devicehub.resources.event import AddDef, AggregateRateDef, BenchmarkDataStorageDef, \
|
from ereuse_devicehub.resources.event import AddDef, AggregateRateDef, AppRateDef, \
|
||||||
BenchmarkDef, BenchmarkProcessorDef, BenchmarkProcessorSysbenchDef, BenchmarkRamSysbenchDef, \
|
BenchmarkDataStorageDef, BenchmarkDef, BenchmarkProcessorDef, BenchmarkProcessorSysbenchDef, \
|
||||||
BenchmarkWithRateDef, EraseBasicDef, EraseSectorsDef, EventDef, InstallDef, \
|
BenchmarkRamSysbenchDef, BenchmarkWithRateDef, EraseBasicDef, EraseSectorsDef, EventDef, \
|
||||||
PhotoboxSystemRateDef, PhotoboxUserDef, RateDef, RemoveDef, SnapshotDef, StepDef, \
|
InstallDef, PhotoboxSystemRateDef, PhotoboxUserDef, RateDef, RemoveDef, SnapshotDef, StepDef, \
|
||||||
StepRandomDef, StepZeroDef, StressTestDef, TestDataStorageDef, TestDef, WorkbenchRateDef
|
StepRandomDef, StepZeroDef, StressTestDef, TestDataStorageDef, TestDef, WorkbenchRateDef
|
||||||
from ereuse_devicehub.resources.inventory import InventoryDef
|
from ereuse_devicehub.resources.inventory import InventoryDef
|
||||||
from ereuse_devicehub.resources.tag import TagDef
|
from ereuse_devicehub.resources.tag import TagDef
|
||||||
|
@ -19,13 +19,14 @@ from teal.config import Config
|
||||||
class DevicehubConfig(Config):
|
class DevicehubConfig(Config):
|
||||||
RESOURCE_DEFINITIONS = {
|
RESOURCE_DEFINITIONS = {
|
||||||
DeviceDef, ComputerDef, DesktopDef, LaptopDef, NetbookDef, ServerDef,
|
DeviceDef, ComputerDef, DesktopDef, LaptopDef, NetbookDef, ServerDef,
|
||||||
MicrotowerDef, ComponentDef, GraphicCardDef, DataStorageDef, SolidStateDriveDef,
|
MicrotowerDef, ComputerMonitorDef, ComponentDef, GraphicCardDef, DataStorageDef,
|
||||||
|
SolidStateDriveDef,
|
||||||
HardDriveDef, MotherboardDef, NetworkAdapterDef, RamModuleDef, ProcessorDef, UserDef,
|
HardDriveDef, MotherboardDef, NetworkAdapterDef, RamModuleDef, ProcessorDef, UserDef,
|
||||||
OrganizationDef, TagDef, EventDef, AddDef, RemoveDef, EraseBasicDef, EraseSectorsDef,
|
OrganizationDef, TagDef, EventDef, AddDef, RemoveDef, EraseBasicDef, EraseSectorsDef,
|
||||||
StepDef, StepZeroDef, StepRandomDef, RateDef, AggregateRateDef, WorkbenchRateDef,
|
StepDef, StepZeroDef, StepRandomDef, RateDef, AggregateRateDef, WorkbenchRateDef,
|
||||||
PhotoboxUserDef, PhotoboxSystemRateDef, InstallDef, SnapshotDef, TestDef,
|
PhotoboxUserDef, PhotoboxSystemRateDef, InstallDef, SnapshotDef, TestDef,
|
||||||
TestDataStorageDef, StressTestDef, WorkbenchRateDef, InventoryDef, BenchmarkDef,
|
TestDataStorageDef, StressTestDef, WorkbenchRateDef, InventoryDef, BenchmarkDef,
|
||||||
BenchmarkDataStorageDef, BenchmarkWithRateDef, BenchmarkProcessorDef,
|
BenchmarkDataStorageDef, BenchmarkWithRateDef, AppRateDef, BenchmarkProcessorDef,
|
||||||
BenchmarkProcessorSysbenchDef, BenchmarkRamSysbenchDef
|
BenchmarkProcessorSysbenchDef, BenchmarkRamSysbenchDef
|
||||||
}
|
}
|
||||||
PASSWORD_SCHEMES = {'pbkdf2_sha256'} # type: Set[str]
|
PASSWORD_SCHEMES = {'pbkdf2_sha256'} # type: Set[str]
|
||||||
|
|
|
@ -5,12 +5,14 @@ from flask_sqlalchemy import SQLAlchemy
|
||||||
from ereuse_devicehub.auth import Auth
|
from ereuse_devicehub.auth import Auth
|
||||||
from ereuse_devicehub.client import Client
|
from ereuse_devicehub.client import Client
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
|
from ereuse_devicehub.dummy.dummy import Dummy
|
||||||
from teal.config import Config as ConfigClass
|
from teal.config import Config as ConfigClass
|
||||||
from teal.teal import Teal
|
from teal.teal import Teal
|
||||||
|
|
||||||
|
|
||||||
class Devicehub(Teal):
|
class Devicehub(Teal):
|
||||||
test_client_class = Client
|
test_client_class = Client
|
||||||
|
Dummy = Dummy
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
config: ConfigClass,
|
config: ConfigClass,
|
||||||
|
@ -29,5 +31,4 @@ class Devicehub(Teal):
|
||||||
super().__init__(config, db, import_name, static_url_path, static_folder, static_host,
|
super().__init__(config, db, import_name, static_url_path, static_folder, static_host,
|
||||||
host_matching, subdomain_matching, template_folder, instance_path,
|
host_matching, subdomain_matching, template_folder, instance_path,
|
||||||
instance_relative_config, root_path, Auth)
|
instance_relative_config, root_path, Auth)
|
||||||
|
self.dummy = Dummy(self)
|
||||||
|
|
||||||
|
|
0
ereuse_devicehub/dummy/__init__.py
Normal file
0
ereuse_devicehub/dummy/__init__.py
Normal file
62
ereuse_devicehub/dummy/dummy.py
Normal file
62
ereuse_devicehub/dummy/dummy.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
|
import click_spinner
|
||||||
|
import yaml
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from ereuse_devicehub.client import UserClient
|
||||||
|
from ereuse_devicehub.db import db
|
||||||
|
from ereuse_devicehub.resources.event.models import Snapshot
|
||||||
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
|
from ereuse_devicehub.resources.user import User
|
||||||
|
|
||||||
|
|
||||||
|
class Dummy:
|
||||||
|
SNAPSHOTS = (
|
||||||
|
'workbench-server-1',
|
||||||
|
'computer-monitor'
|
||||||
|
)
|
||||||
|
TAGS = (
|
||||||
|
'tag1',
|
||||||
|
'tag2',
|
||||||
|
'tag3'
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, app) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.app = app
|
||||||
|
self.app.cli.command('dummy',
|
||||||
|
short_help='Creates dummy devices and users.')(self.run)
|
||||||
|
|
||||||
|
@click.confirmation_option(prompt='This command deletes the DB in the process. '
|
||||||
|
'Do you want to continue?')
|
||||||
|
def run(self):
|
||||||
|
print('Preparing the database...')
|
||||||
|
with click_spinner.spinner():
|
||||||
|
self.app.init_db(erase=True)
|
||||||
|
user = self.user_client('user@dhub.com', '1234')
|
||||||
|
user.post(res=Tag, query=[('ids', i) for i in self.TAGS], data={})
|
||||||
|
print('Creating devices...')
|
||||||
|
for file_name in tqdm(self.SNAPSHOTS):
|
||||||
|
snapshot = self.file(file_name)
|
||||||
|
user.post(res=Snapshot, data=snapshot)
|
||||||
|
print('Done :-)')
|
||||||
|
|
||||||
|
def user_client(self, email: str, password: str):
|
||||||
|
user = User(email=email, password=password)
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
client = UserClient(application=self.app,
|
||||||
|
response_wrapper=self.app.response_class,
|
||||||
|
email=user.email,
|
||||||
|
password=password)
|
||||||
|
client.user, _ = client.login(client.email, client.password)
|
||||||
|
return client
|
||||||
|
|
||||||
|
def file(self, name: str):
|
||||||
|
with Path(__file__) \
|
||||||
|
.parent \
|
||||||
|
.joinpath('files') \
|
||||||
|
.joinpath(name + '.snapshot.yaml').open() as f:
|
||||||
|
return yaml.load(f)
|
17
ereuse_devicehub/dummy/files/computer-monitor.snapshot.yaml
Normal file
17
ereuse_devicehub/dummy/files/computer-monitor.snapshot.yaml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
type: Snapshot
|
||||||
|
software: AndroidApp
|
||||||
|
version: '1.0'
|
||||||
|
device:
|
||||||
|
type: ComputerMonitor
|
||||||
|
technology: LCD
|
||||||
|
manufacturer: Dell
|
||||||
|
model: 1707FPF
|
||||||
|
serialNumber: CN0FP446728728541C8S
|
||||||
|
resolutionWidth: 1920
|
||||||
|
resolutionHeight: 1080
|
||||||
|
size: 21.5
|
||||||
|
events:
|
||||||
|
- type: AppRate
|
||||||
|
appearanceRange: A
|
||||||
|
functionalityRange: C
|
||||||
|
labelling: False
|
118
ereuse_devicehub/dummy/files/workbench-server-1.snapshot.yaml
Normal file
118
ereuse_devicehub/dummy/files/workbench-server-1.snapshot.yaml
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
# A Snapshot Phase 1 with a device
|
||||||
|
# and 1 GraphicCard, 2 RamModule, 1 Processor, 1 SSD, 1 HDD, 1 Motherboard
|
||||||
|
# Prerequisites:
|
||||||
|
# - 2 tags: tag1 and tag2 from the default org
|
||||||
|
# All numbers are invented
|
||||||
|
|
||||||
|
|
||||||
|
type: Snapshot
|
||||||
|
uuid: cb8ce6b5-6a1b-4084-b5b9-d8fadad2a015
|
||||||
|
version: '11.0'
|
||||||
|
software: Workbench
|
||||||
|
elapsed: 500
|
||||||
|
device:
|
||||||
|
type: Microtower
|
||||||
|
serialNumber: d1s
|
||||||
|
model: d1ml
|
||||||
|
manufacturer: d1mr
|
||||||
|
tags:
|
||||||
|
- type: Tag
|
||||||
|
id: tag1
|
||||||
|
events:
|
||||||
|
- type: WorkbenchRate
|
||||||
|
appearanceRange: A
|
||||||
|
functionalityRange: B
|
||||||
|
- type: BenchmarkRamSysbench
|
||||||
|
rate: 2444
|
||||||
|
- type: StressTest
|
||||||
|
elapsed: 300
|
||||||
|
error: False
|
||||||
|
components:
|
||||||
|
- type: GraphicCard
|
||||||
|
serialNumber: gc1-1s
|
||||||
|
model: gc1-1ml
|
||||||
|
manufacturer: gc1-1mr
|
||||||
|
- type: RamModule
|
||||||
|
serialNumber: rm1-1s
|
||||||
|
model: rm1-1ml
|
||||||
|
manufacturer: rm1-1mr
|
||||||
|
- type: RamModule
|
||||||
|
serialNumber: rm2-1s
|
||||||
|
model: rm2-1ml
|
||||||
|
manufacturer: rm2-1mr
|
||||||
|
- type: Processor
|
||||||
|
model: p1-1s
|
||||||
|
manufacturer: p1-1mr
|
||||||
|
events:
|
||||||
|
- type: BenchmarkProcessor
|
||||||
|
rate: 2410
|
||||||
|
- type: BenchmarkProcessorSysbench
|
||||||
|
rate: 4400
|
||||||
|
- type: SolidStateDrive
|
||||||
|
serialNumber: ssd1-1s
|
||||||
|
model: ssd1-1ml
|
||||||
|
manufacturer: ssd1-1mr
|
||||||
|
events:
|
||||||
|
- type: BenchmarkDataStorage
|
||||||
|
readSpeed: 20
|
||||||
|
writeSpeed: 15
|
||||||
|
elapsed: 21
|
||||||
|
- type: TestDataStorage
|
||||||
|
elapsed: 233
|
||||||
|
firstError: 0
|
||||||
|
error: False
|
||||||
|
status: Completed without error
|
||||||
|
length: Short
|
||||||
|
lifetime: 99
|
||||||
|
passedLifetime: 99
|
||||||
|
assessment: True
|
||||||
|
powerCycleCount: 11
|
||||||
|
reallocatedSectorCount: 2
|
||||||
|
powerCycleCount: 4
|
||||||
|
reportedUncorrectableErrors: 1
|
||||||
|
commandTimeout: 11
|
||||||
|
currentPendingSectorCount: 1
|
||||||
|
offlineUncorrectable: 33
|
||||||
|
remainingLifetimePercentage: 1
|
||||||
|
- type: EraseSectors
|
||||||
|
error: False
|
||||||
|
cleanWithZeros: False
|
||||||
|
startTime: 2018-01-01T10:10:10
|
||||||
|
endTime: 2018-01-01T12:10:10
|
||||||
|
secureRandomSteps: 0
|
||||||
|
steps:
|
||||||
|
- type: StepRandom
|
||||||
|
startTime: 2018-01-01T10:10:10
|
||||||
|
endTime: 2018-01-01T12:10:10
|
||||||
|
error: False
|
||||||
|
cleanWithZeros: False
|
||||||
|
secureRandomSteps: 0
|
||||||
|
- type: HardDrive
|
||||||
|
serialNumber: hdd1-1s
|
||||||
|
model: hdd1-1ml
|
||||||
|
manufacturer: hdd1-1mr
|
||||||
|
events:
|
||||||
|
- type: BenchmarkDataStorage
|
||||||
|
readSpeed: 10
|
||||||
|
writeSpeed: 5
|
||||||
|
- type: EraseSectors
|
||||||
|
error: False
|
||||||
|
cleanWithZeros: False
|
||||||
|
startTime: 2018-01-01T10:10:10
|
||||||
|
endTime: 2018-01-01T12:10:10
|
||||||
|
secureRandomSteps: 0
|
||||||
|
steps:
|
||||||
|
- type: StepRandom
|
||||||
|
startTime: 2018-01-01T10:10:10
|
||||||
|
endTime: 2018-01-01T12:10:10
|
||||||
|
error: False
|
||||||
|
cleanWithZeros: False
|
||||||
|
secureRandomSteps: 0
|
||||||
|
- type: Install
|
||||||
|
elapsed: 420
|
||||||
|
error: False
|
||||||
|
name: LinuxMint 18.01 32b
|
||||||
|
- type: Motherboard
|
||||||
|
serialNumber: mb1-1s
|
||||||
|
model: mb1-1ml
|
||||||
|
manufacturer: mb1-1mr
|
|
@ -1,6 +1,6 @@
|
||||||
from ereuse_devicehub.resources.device.schemas import Component, Computer, DataStorage, Desktop, \
|
from ereuse_devicehub.resources.device.schemas import Component, Computer, ComputerMonitor, \
|
||||||
Device, GraphicCard, HardDrive, Laptop, Microtower, Motherboard, Netbook, NetworkAdapter, \
|
DataStorage, Desktop, Device, GraphicCard, HardDrive, Laptop, Microtower, Motherboard, Netbook, \
|
||||||
Processor, RamModule, Server, SolidStateDrive
|
NetworkAdapter, Processor, RamModule, Server, SolidStateDrive
|
||||||
from ereuse_devicehub.resources.device.views import DeviceView
|
from ereuse_devicehub.resources.device.views import DeviceView
|
||||||
from teal.resource import Converters, Resource
|
from teal.resource import Converters, Resource
|
||||||
|
|
||||||
|
@ -36,6 +36,10 @@ class MicrotowerDef(ComputerDef):
|
||||||
SCHEMA = Microtower
|
SCHEMA = Microtower
|
||||||
|
|
||||||
|
|
||||||
|
class ComputerMonitorDef(DeviceDef):
|
||||||
|
SCHEMA = ComputerMonitor
|
||||||
|
|
||||||
|
|
||||||
class ComponentDef(DeviceDef):
|
class ComponentDef(DeviceDef):
|
||||||
SCHEMA = Component
|
SCHEMA = Component
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ from sqlalchemy.orm import ColumnProperty, backref, relationship
|
||||||
from sqlalchemy.util import OrderedSet
|
from sqlalchemy.util import OrderedSet
|
||||||
from sqlalchemy_utils import ColorType
|
from sqlalchemy_utils import ColorType
|
||||||
|
|
||||||
from ereuse_devicehub.resources.enums import DataStorageInterface, RamFormat, RamInterface
|
from ereuse_devicehub.resources.enums import ComputerMonitorTechnologies, DataStorageInterface, \
|
||||||
|
RamFormat, RamInterface
|
||||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE, STR_SM_SIZE, Thing
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE, STR_SM_SIZE, Thing
|
||||||
from ereuse_utils.naming import Naming
|
from ereuse_utils.naming import Naming
|
||||||
from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, check_range
|
from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, check_range
|
||||||
|
@ -18,14 +19,30 @@ from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, c
|
||||||
|
|
||||||
class Device(Thing):
|
class Device(Thing):
|
||||||
id = Column(BigInteger, Sequence('device_seq'), primary_key=True)
|
id = Column(BigInteger, Sequence('device_seq'), primary_key=True)
|
||||||
|
id.comment = """
|
||||||
|
The identifier of the device for this database.
|
||||||
|
"""
|
||||||
type = Column(Unicode(STR_SM_SIZE), nullable=False)
|
type = Column(Unicode(STR_SM_SIZE), nullable=False)
|
||||||
hid = Column(Unicode(STR_BIG_SIZE), unique=True)
|
hid = Column(Unicode(STR_BIG_SIZE), unique=True)
|
||||||
|
hid.comment = """
|
||||||
|
The Hardware ID (HID) is the unique ID traceability systems
|
||||||
|
use to ID a device globally.
|
||||||
|
"""
|
||||||
model = Column(Unicode(STR_BIG_SIZE))
|
model = Column(Unicode(STR_BIG_SIZE))
|
||||||
manufacturer = Column(Unicode(STR_SIZE))
|
manufacturer = Column(Unicode(STR_SIZE))
|
||||||
serial_number = Column(Unicode(STR_SIZE))
|
serial_number = Column(Unicode(STR_SIZE))
|
||||||
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, 3))
|
||||||
|
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, 3))
|
||||||
|
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, 3))
|
||||||
|
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, 3))
|
||||||
color = Column(ColorType)
|
color = Column(ColorType)
|
||||||
|
|
||||||
|
@ -108,6 +125,28 @@ class Microtower(Computer):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ComputerMonitor(Device):
|
||||||
|
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
|
||||||
|
size = Column(Float(decimal_return_scale=2), check_range('size', 2, 150))
|
||||||
|
size.comment = """
|
||||||
|
The size of the monitor in inches.
|
||||||
|
"""
|
||||||
|
technology = Column(DBEnum(ComputerMonitorTechnologies))
|
||||||
|
technology.comment = """
|
||||||
|
The technology the monitor uses to display the image.
|
||||||
|
"""
|
||||||
|
resolution_width = Column(SmallInteger, check_range('resolution_width', 10, 20000))
|
||||||
|
resolution_width.comment = """
|
||||||
|
The maximum horizontal resolution the monitor can natively support
|
||||||
|
in pixels.
|
||||||
|
"""
|
||||||
|
resolution_height = Column(SmallInteger, check_range('resolution_height', 10, 20000))
|
||||||
|
resolution_height.comment = """
|
||||||
|
The maximum vertical resolution the monitor can natively support
|
||||||
|
in pixels.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Component(Device):
|
class Component(Device):
|
||||||
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
|
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from typing import Dict, List, Set
|
from typing import Dict, List, Set
|
||||||
|
|
||||||
from colour import Color
|
from colour import Color
|
||||||
from sqlalchemy import Column
|
from sqlalchemy import Column, Integer
|
||||||
|
|
||||||
from ereuse_devicehub.resources.enums import DataStorageInterface, RamFormat, RamInterface
|
from ereuse_devicehub.resources.enums import ComputerMonitorTechnologies, DataStorageInterface, \
|
||||||
|
RamFormat, RamInterface
|
||||||
from ereuse_devicehub.resources.event.models import Event, EventWithMultipleDevices, \
|
from ereuse_devicehub.resources.event.models import Event, EventWithMultipleDevices, \
|
||||||
EventWithOneDevice
|
EventWithOneDevice
|
||||||
from ereuse_devicehub.resources.image.models import ImageList
|
from ereuse_devicehub.resources.image.models import ImageList
|
||||||
|
@ -72,6 +73,20 @@ class Microtower(Computer):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ComputerMonitor(Device):
|
||||||
|
technology = ... # type: Column
|
||||||
|
size = ... # type: Column
|
||||||
|
resolution_width = ... # type: Column
|
||||||
|
resolution_height = ... # type: Column
|
||||||
|
|
||||||
|
def __init__(self, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
technology = ... # type: ComputerMonitorTechnologies
|
||||||
|
size = ... # type: Integer
|
||||||
|
resolution_width = ... # type: int
|
||||||
|
resolution_height = ... # type: int
|
||||||
|
|
||||||
|
|
||||||
class Component(Device):
|
class Component(Device):
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
|
@ -1,36 +1,28 @@
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
|
||||||
from ereuse_devicehub.resources.enums import RamFormat, RamInterface
|
|
||||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
|
||||||
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
|
||||||
from marshmallow import post_load, pre_load
|
from marshmallow import post_load, pre_load
|
||||||
from marshmallow.fields import Float, Integer, Str
|
from marshmallow.fields import Float, Integer, Str
|
||||||
from marshmallow.validate import Length, OneOf, Range
|
from marshmallow.validate import Length, OneOf, Range
|
||||||
from marshmallow_enum import EnumField
|
from marshmallow_enum import EnumField
|
||||||
from sqlalchemy.util import OrderedSet
|
from sqlalchemy.util import OrderedSet
|
||||||
|
|
||||||
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
|
from ereuse_devicehub.resources.device import models as m
|
||||||
|
from ereuse_devicehub.resources.enums import ComputerMonitorTechnologies, RamFormat, RamInterface
|
||||||
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
||||||
|
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
||||||
from teal.marshmallow import ValidationError
|
from teal.marshmallow import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class Device(Thing):
|
class Device(Thing):
|
||||||
# todo id is dump_only except when in Snapshot
|
id = Integer(description=m.Device.id, dump_only=True)
|
||||||
id = Integer(description='The identifier of the device for this database.')
|
hid = Str(dump_only=True, description=m.Device.hid)
|
||||||
hid = Str(dump_only=True,
|
|
||||||
description='The Hardware ID is the unique ID traceability systems '
|
|
||||||
'use to ID a device globally.')
|
|
||||||
tags = NestedOn('Tag', many=True, collection_class=OrderedSet)
|
tags = NestedOn('Tag', many=True, collection_class=OrderedSet)
|
||||||
model = Str(validate=Length(max=STR_BIG_SIZE))
|
model = Str(validate=Length(max=STR_BIG_SIZE))
|
||||||
manufacturer = Str(validate=Length(max=STR_SIZE))
|
manufacturer = Str(validate=Length(max=STR_SIZE))
|
||||||
serial_number = Str(data_key='serialNumber')
|
serial_number = Str(data_key='serialNumber')
|
||||||
product_id = Str(data_key='productId')
|
product_id = Str(data_key='productId')
|
||||||
weight = Float(validate=Range(0.1, 3),
|
weight = Float(validate=Range(0.1, 3), unit=UnitCodes.kgm, description=m.Device.weight)
|
||||||
unit=UnitCodes.kgm,
|
width = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description=m.Device.width)
|
||||||
description='The weight of the device in Kgm.')
|
height = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description=m.Device.height)
|
||||||
width = Float(validate=Range(0.1, 3),
|
|
||||||
unit=UnitCodes.m,
|
|
||||||
description='The width of the device in meters.')
|
|
||||||
height = Float(validate=Range(0.1, 3),
|
|
||||||
unit=UnitCodes.m,
|
|
||||||
description='The height of the device in meters.')
|
|
||||||
events = NestedOn('Event', many=True, dump_only=True)
|
events = NestedOn('Event', many=True, dump_only=True)
|
||||||
events_one = NestedOn('Event', many=True, load_only=True, collection_class=OrderedSet)
|
events_one = NestedOn('Event', many=True, load_only=True, collection_class=OrderedSet)
|
||||||
|
|
||||||
|
@ -61,7 +53,6 @@ class Device(Thing):
|
||||||
|
|
||||||
class Computer(Device):
|
class Computer(Device):
|
||||||
components = NestedOn('Component', many=True, dump_only=True, collection_class=OrderedSet)
|
components = NestedOn('Component', many=True, dump_only=True, collection_class=OrderedSet)
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Desktop(Computer):
|
class Desktop(Computer):
|
||||||
|
@ -84,6 +75,18 @@ class Microtower(Computer):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ComputerMonitor(Device):
|
||||||
|
size = Float(description=m.ComputerMonitor.size.comment, validate=Range(2, 150))
|
||||||
|
technology = EnumField(ComputerMonitorTechnologies,
|
||||||
|
description=m.ComputerMonitor.technology.comment)
|
||||||
|
resolution_width = Integer(data_key='resolutionWidth',
|
||||||
|
validate=Range(10, 20000),
|
||||||
|
description=m.ComputerMonitor.resolution_width.comment)
|
||||||
|
resolution_height = Integer(data_key='resolutionHeight',
|
||||||
|
validate=Range(10, 20000),
|
||||||
|
description=m.ComputerMonitor.resolution_height.comment)
|
||||||
|
|
||||||
|
|
||||||
class Component(Device):
|
class Component(Device):
|
||||||
parent = NestedOn(Device, dump_only=True)
|
parent = NestedOn(Device, dump_only=True)
|
||||||
|
|
||||||
|
|
|
@ -149,3 +149,14 @@ class DataStorageInterface(Enum):
|
||||||
ATA = 'ATA'
|
ATA = 'ATA'
|
||||||
USB = 'USB'
|
USB = 'USB'
|
||||||
PCI = 'PCI'
|
PCI = 'PCI'
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class ComputerMonitorTechnologies(Enum):
|
||||||
|
CRT = 'Cathode ray tube (CRT)'
|
||||||
|
TFT = 'Thin-film-transistor liquid-crystal (TFT)'
|
||||||
|
LED = 'LED-backlit (LED)'
|
||||||
|
PDP = 'Plasma display panel (Plasma)'
|
||||||
|
LCD = 'Liquid-crystal display (any of TFT, LED, Blue Phase, IPS)'
|
||||||
|
OLED = 'Organic light-emitting diode (OLED)'
|
||||||
|
AMOLED = 'Organic light-emitting diode (AMOLED)'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from typing import Callable, Iterable, Tuple
|
from typing import Callable, Iterable, Tuple
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device.sync import Sync
|
from ereuse_devicehub.resources.device.sync import Sync
|
||||||
from ereuse_devicehub.resources.event.schemas import Add, AggregateRate, Benchmark, \
|
from ereuse_devicehub.resources.event.schemas import Add, AggregateRate, AppRate, Benchmark, \
|
||||||
BenchmarkDataStorage, BenchmarkProcessor, BenchmarkProcessorSysbench, BenchmarkRamSysbench, \
|
BenchmarkDataStorage, BenchmarkProcessor, BenchmarkProcessorSysbench, BenchmarkRamSysbench, \
|
||||||
BenchmarkWithRate, EraseBasic, EraseSectors, Event, Install, PhotoboxSystemRate, \
|
BenchmarkWithRate, EraseBasic, EraseSectors, Event, Install, PhotoboxSystemRate, \
|
||||||
PhotoboxUserRate, Rate, Remove, Snapshot, Step, StepRandom, StepZero, StressTest, Test, \
|
PhotoboxUserRate, Rate, Remove, Snapshot, Step, StepRandom, StepZero, StressTest, Test, \
|
||||||
|
@ -65,6 +65,10 @@ class PhotoboxSystemRateDef(RateDef):
|
||||||
SCHEMA = PhotoboxSystemRate
|
SCHEMA = PhotoboxSystemRate
|
||||||
|
|
||||||
|
|
||||||
|
class AppRateDef(RateDef):
|
||||||
|
SCHEMA = AppRate
|
||||||
|
|
||||||
|
|
||||||
class InstallDef(EventDef):
|
class InstallDef(EventDef):
|
||||||
SCHEMA = Install
|
SCHEMA = Install
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,6 @@ from collections import Iterable
|
||||||
from typing import Set, Union
|
from typing import Set, Union
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Device
|
|
||||||
from ereuse_devicehub.resources.enums import AppearanceRange, BOX_RATE_3, BOX_RATE_5, Bios, \
|
|
||||||
FunctionalityRange, RATE_NEGATIVE, RATE_POSITIVE, RatingRange, RatingSoftware, \
|
|
||||||
SnapshotExpectedEvents, SnapshotSoftware, TestHardDriveLength
|
|
||||||
from ereuse_devicehub.resources.image.models import Image
|
|
||||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE, STR_SM_SIZE, Thing
|
|
||||||
from ereuse_devicehub.resources.user.models import User
|
|
||||||
from flask import g
|
from flask import g
|
||||||
from sqlalchemy import BigInteger, Boolean, CheckConstraint, Column, DateTime, Enum as DBEnum, \
|
from sqlalchemy import BigInteger, Boolean, CheckConstraint, Column, DateTime, Enum as DBEnum, \
|
||||||
Float, ForeignKey, Interval, JSON, SmallInteger, Unicode, event
|
Float, ForeignKey, Interval, JSON, SmallInteger, Unicode, event
|
||||||
|
@ -20,6 +12,14 @@ from sqlalchemy.orm import backref, relationship
|
||||||
from sqlalchemy.orm.events import AttributeEvents as Events
|
from sqlalchemy.orm.events import AttributeEvents as Events
|
||||||
from sqlalchemy.util import OrderedSet
|
from sqlalchemy.util import OrderedSet
|
||||||
|
|
||||||
|
from ereuse_devicehub.db import db
|
||||||
|
from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Device
|
||||||
|
from ereuse_devicehub.resources.enums import AppearanceRange, BOX_RATE_3, BOX_RATE_5, Bios, \
|
||||||
|
FunctionalityRange, RATE_NEGATIVE, RATE_POSITIVE, RatingRange, RatingSoftware, \
|
||||||
|
SnapshotExpectedEvents, SnapshotSoftware, TestHardDriveLength
|
||||||
|
from ereuse_devicehub.resources.image.models import Image
|
||||||
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE, STR_SM_SIZE, Thing
|
||||||
|
from ereuse_devicehub.resources.user.models import User
|
||||||
from teal.db import ArrayOfEnum, CASCADE, CASCADE_OWN, INHERIT_COND, POLYMORPHIC_ID, \
|
from teal.db import ArrayOfEnum, CASCADE, CASCADE_OWN, INHERIT_COND, POLYMORPHIC_ID, \
|
||||||
POLYMORPHIC_ON, StrictVersionType, check_range
|
POLYMORPHIC_ON, StrictVersionType, check_range
|
||||||
|
|
||||||
|
@ -256,15 +256,18 @@ class StepRandom(Step):
|
||||||
|
|
||||||
|
|
||||||
class Snapshot(JoinedTableMixin, EventWithOneDevice):
|
class Snapshot(JoinedTableMixin, EventWithOneDevice):
|
||||||
uuid = Column(UUID(as_uuid=True), nullable=False, unique=True)
|
uuid = Column(UUID(as_uuid=True), unique=True)
|
||||||
version = Column(StrictVersionType(STR_SM_SIZE), nullable=False)
|
version = Column(StrictVersionType(STR_SM_SIZE), nullable=False)
|
||||||
software = Column(DBEnum(SnapshotSoftware), nullable=False)
|
software = Column(DBEnum(SnapshotSoftware), nullable=False)
|
||||||
elapsed = Column(Interval, nullable=False)
|
elapsed = Column(Interval)
|
||||||
|
elapsed.comment = """
|
||||||
|
For Snapshots made with Workbench, the total amount of time
|
||||||
|
it took to complete.
|
||||||
|
"""
|
||||||
expected_events = Column(ArrayOfEnum(DBEnum(SnapshotExpectedEvents)))
|
expected_events = Column(ArrayOfEnum(DBEnum(SnapshotExpectedEvents)))
|
||||||
|
|
||||||
|
|
||||||
class Install(JoinedTableMixin, EventWithOneDevice):
|
class Install(JoinedTableMixin, EventWithOneDevice):
|
||||||
name = Column(Unicode(STR_BIG_SIZE), nullable=False)
|
|
||||||
elapsed = Column(Interval, nullable=False)
|
elapsed = Column(Interval, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
@ -290,6 +293,20 @@ class Rate(JoinedTableMixin, EventWithOneDevice):
|
||||||
def rating_range(self) -> RatingRange:
|
def rating_range(self) -> RatingRange:
|
||||||
return RatingRange.from_score(self.rating)
|
return RatingRange.from_score(self.rating)
|
||||||
|
|
||||||
|
@declared_attr
|
||||||
|
def __mapper_args__(cls):
|
||||||
|
"""
|
||||||
|
Defines inheritance.
|
||||||
|
|
||||||
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
|
extensions/declarative/api.html
|
||||||
|
#sqlalchemy.ext.declarative.declared_attr>`_
|
||||||
|
"""
|
||||||
|
args = {POLYMORPHIC_ID: cls.t}
|
||||||
|
if cls.t == 'Rate':
|
||||||
|
args[POLYMORPHIC_ON] = cls.type
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
class IndividualRate(Rate):
|
class IndividualRate(Rate):
|
||||||
pass
|
pass
|
||||||
|
@ -319,18 +336,26 @@ class RateAggregateRate(db.Model):
|
||||||
primary_key=True)
|
primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
class WorkbenchRate(IndividualRate):
|
class ManualRate(IndividualRate):
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||||
|
labelling = Column(Boolean)
|
||||||
|
appearance_range = Column(DBEnum(AppearanceRange))
|
||||||
|
functionality_range = Column(DBEnum(FunctionalityRange))
|
||||||
|
|
||||||
|
|
||||||
|
class WorkbenchRate(ManualRate):
|
||||||
|
id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id), primary_key=True)
|
||||||
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE))
|
processor = Column(Float(decimal_return_scale=2), check_range('processor', *RATE_POSITIVE))
|
||||||
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE))
|
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE))
|
||||||
data_storage = Column(Float(decimal_return_scale=2),
|
data_storage = Column(Float(decimal_return_scale=2),
|
||||||
check_range('data_storage', *RATE_POSITIVE))
|
check_range('data_storage', *RATE_POSITIVE))
|
||||||
graphic_card = Column(Float(decimal_return_scale=2),
|
graphic_card = Column(Float(decimal_return_scale=2),
|
||||||
check_range('graphic_card', *RATE_POSITIVE))
|
check_range('graphic_card', *RATE_POSITIVE))
|
||||||
labelling = Column(Boolean)
|
|
||||||
bios = Column(DBEnum(Bios))
|
bios = Column(DBEnum(Bios))
|
||||||
appearance_range = Column(DBEnum(AppearanceRange))
|
|
||||||
functionality_range = Column(DBEnum(FunctionalityRange))
|
|
||||||
|
class AppRate(ManualRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PhotoboxRate(IndividualRate):
|
class PhotoboxRate(IndividualRate):
|
||||||
|
@ -369,6 +394,20 @@ class PhotoboxSystemRate(PhotoboxRate):
|
||||||
class Test(JoinedTableMixin, EventWithOneDevice):
|
class Test(JoinedTableMixin, EventWithOneDevice):
|
||||||
elapsed = Column(Interval, nullable=False)
|
elapsed = Column(Interval, nullable=False)
|
||||||
|
|
||||||
|
@declared_attr
|
||||||
|
def __mapper_args__(cls):
|
||||||
|
"""
|
||||||
|
Defines inheritance.
|
||||||
|
|
||||||
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
|
extensions/declarative/api.html
|
||||||
|
#sqlalchemy.ext.declarative.declared_attr>`_
|
||||||
|
"""
|
||||||
|
args = {POLYMORPHIC_ID: cls.t}
|
||||||
|
if cls.t == 'Test':
|
||||||
|
args[POLYMORPHIC_ON] = cls.type
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
class TestDataStorage(Test):
|
class TestDataStorage(Test):
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
|
id = Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
|
||||||
|
@ -396,6 +435,20 @@ class StressTest(Test):
|
||||||
class Benchmark(JoinedTableMixin, EventWithOneDevice):
|
class Benchmark(JoinedTableMixin, EventWithOneDevice):
|
||||||
elapsed = Column(Interval)
|
elapsed = Column(Interval)
|
||||||
|
|
||||||
|
@declared_attr
|
||||||
|
def __mapper_args__(cls):
|
||||||
|
"""
|
||||||
|
Defines inheritance.
|
||||||
|
|
||||||
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
|
extensions/declarative/api.html
|
||||||
|
#sqlalchemy.ext.declarative.declared_attr>`_
|
||||||
|
"""
|
||||||
|
args = {POLYMORPHIC_ID: cls.t}
|
||||||
|
if cls.t == 'Benchmark':
|
||||||
|
args[POLYMORPHIC_ON] = cls.type
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
class BenchmarkDataStorage(Benchmark):
|
class BenchmarkDataStorage(Benchmark):
|
||||||
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
|
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
|
||||||
|
|
|
@ -139,17 +139,26 @@ class AggregateRate(Rate):
|
||||||
self.ratings = ... # type: Set[IndividualRate]
|
self.ratings = ... # type: Set[IndividualRate]
|
||||||
|
|
||||||
|
|
||||||
class WorkbenchRate(IndividualRate):
|
class ManualRate(IndividualRate):
|
||||||
|
def __init__(self, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.labelling = ... # type: bool
|
||||||
|
self.appearance_range = ... # type: AppearanceRange
|
||||||
|
self.functionality_range = ... # type: FunctionalityRange
|
||||||
|
|
||||||
|
|
||||||
|
class WorkbenchRate(ManualRate):
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.processor = ... # type: float
|
self.processor = ... # type: float
|
||||||
self.ram = ... # type: float
|
self.ram = ... # type: float
|
||||||
self.data_storage = ... # type: float
|
self.data_storage = ... # type: float
|
||||||
self.graphic_card = ... # type: float
|
self.graphic_card = ... # type: float
|
||||||
self.labelling = ... # type: bool
|
|
||||||
self.bios = ... # type: Bios
|
self.bios = ... # type: Bios
|
||||||
self.appearance_range = ... # type: AppearanceRange
|
|
||||||
self.functionality_range = ... # type: FunctionalityRange
|
|
||||||
|
class AppRate(ManualRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PhotoboxRate(IndividualRate):
|
class PhotoboxRate(IndividualRate):
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
from flask import current_app as app
|
||||||
|
from marshmallow import ValidationError, validates_schema
|
||||||
|
from marshmallow.fields import Boolean, DateTime, Float, Integer, List, Nested, String, TimeDelta, \
|
||||||
|
UUID
|
||||||
|
from marshmallow.validate import Length, Range
|
||||||
|
from marshmallow_enum import EnumField
|
||||||
|
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
from ereuse_devicehub.resources.device.schemas import Component, Device
|
from ereuse_devicehub.resources.device.schemas import Component, Device
|
||||||
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, FunctionalityRange, \
|
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, FunctionalityRange, \
|
||||||
|
@ -6,13 +13,6 @@ 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
|
||||||
from ereuse_devicehub.resources.user.schemas import User
|
from ereuse_devicehub.resources.user.schemas import User
|
||||||
from flask import current_app as app
|
|
||||||
from marshmallow import ValidationError, validates_schema
|
|
||||||
from marshmallow.fields import Boolean, DateTime, Float, Integer, List, Nested, String, TimeDelta, \
|
|
||||||
UUID
|
|
||||||
from marshmallow.validate import Length, Range
|
|
||||||
from marshmallow_enum import EnumField
|
|
||||||
|
|
||||||
from teal.marshmallow import Version
|
from teal.marshmallow import Version
|
||||||
from teal.resource import Schema
|
from teal.resource import Schema
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ class PhotoboxRate(IndividualRate):
|
||||||
# todo Image
|
# todo Image
|
||||||
|
|
||||||
|
|
||||||
class PhotoboxUserRate(PhotoboxRate):
|
class PhotoboxUserRate(IndividualRate):
|
||||||
assembling = Integer()
|
assembling = Integer()
|
||||||
parts = Integer()
|
parts = Integer()
|
||||||
buttons = Integer()
|
buttons = Integer()
|
||||||
|
@ -135,18 +135,11 @@ class PhotoboxUserRate(PhotoboxRate):
|
||||||
dirt = Integer()
|
dirt = Integer()
|
||||||
|
|
||||||
|
|
||||||
class PhotoboxSystemRate(PhotoboxRate):
|
class PhotoboxSystemRate(IndividualRate):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WorkbenchRate(IndividualRate):
|
class ManualRate(IndividualRate):
|
||||||
processor = Float()
|
|
||||||
ram = Float()
|
|
||||||
data_storage = Float()
|
|
||||||
graphic_card = Float()
|
|
||||||
labelling = Boolean(description='Sets if there are labels stuck that should be removed.')
|
|
||||||
bios = EnumField(Bios, description='How difficult it has been to set the bios to '
|
|
||||||
'boot from the network.')
|
|
||||||
appearance_range = EnumField(AppearanceRange,
|
appearance_range = EnumField(AppearanceRange,
|
||||||
required=True,
|
required=True,
|
||||||
data_key='appearanceRange',
|
data_key='appearanceRange',
|
||||||
|
@ -156,6 +149,20 @@ class WorkbenchRate(IndividualRate):
|
||||||
required=True,
|
required=True,
|
||||||
data_key='functionalityRange',
|
data_key='functionalityRange',
|
||||||
description='Grades the defects of a device that affect its usage.')
|
description='Grades the defects of a device that affect its usage.')
|
||||||
|
labelling = Boolean(description='Sets if there are labels stuck that should be removed.')
|
||||||
|
|
||||||
|
|
||||||
|
class AppRate(ManualRate):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WorkbenchRate(ManualRate):
|
||||||
|
processor = Float()
|
||||||
|
ram = Float()
|
||||||
|
data_storage = Float()
|
||||||
|
graphic_card = Float()
|
||||||
|
bios = EnumField(Bios, description='How difficult it has been to set the bios to '
|
||||||
|
'boot from the network.')
|
||||||
|
|
||||||
|
|
||||||
class Install(EventWithOneDevice):
|
class Install(EventWithOneDevice):
|
||||||
|
@ -172,7 +179,7 @@ class Snapshot(EventWithOneDevice):
|
||||||
|
|
||||||
See docs for more info.
|
See docs for more info.
|
||||||
"""
|
"""
|
||||||
uuid = UUID(required=True)
|
uuid = UUID()
|
||||||
software = EnumField(SnapshotSoftware,
|
software = EnumField(SnapshotSoftware,
|
||||||
required=True,
|
required=True,
|
||||||
description='The software that generated this Snapshot.')
|
description='The software that generated this Snapshot.')
|
||||||
|
@ -185,7 +192,7 @@ class Snapshot(EventWithOneDevice):
|
||||||
'the async Snapshot.')
|
'the async Snapshot.')
|
||||||
|
|
||||||
device = NestedOn(Device)
|
device = NestedOn(Device)
|
||||||
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
|
elapsed = TimeDelta(precision=TimeDelta.SECONDS)
|
||||||
components = NestedOn(Component,
|
components = NestedOn(Component,
|
||||||
many=True,
|
many=True,
|
||||||
description='A list of components that are inside of the device'
|
description='A list of components that are inside of the device'
|
||||||
|
@ -206,10 +213,29 @@ class Snapshot(EventWithOneDevice):
|
||||||
@validates_schema
|
@validates_schema
|
||||||
def validate_components_only_workbench(self, data: dict):
|
def validate_components_only_workbench(self, data: dict):
|
||||||
if data['software'] != SnapshotSoftware.Workbench:
|
if data['software'] != SnapshotSoftware.Workbench:
|
||||||
if data['components'] is not None:
|
if data.get('components', None) is not None:
|
||||||
raise ValidationError('Only Workbench can add component info',
|
raise ValidationError('Only Workbench can add component info',
|
||||||
field_names=['components'])
|
field_names=['components'])
|
||||||
|
|
||||||
|
@validates_schema
|
||||||
|
def validate_only_workbench_fields(self, data: dict):
|
||||||
|
"""Ensures workbench has ``elapsed`` and ``uuid`` and no others."""
|
||||||
|
# todo test
|
||||||
|
if data['software'] == SnapshotSoftware.Workbench:
|
||||||
|
if not data.get('uuid', None):
|
||||||
|
raise ValidationError('Snapshots from Workbench must have uuid',
|
||||||
|
field_names=['uuid'])
|
||||||
|
if not data.get('elapsed', None):
|
||||||
|
raise ValidationError('Snapshots from Workbench must have elapsed',
|
||||||
|
field_names=['elapsed'])
|
||||||
|
else:
|
||||||
|
if data.get('uuid', None):
|
||||||
|
raise ValidationError('Only Snapshots from Workbench can have uuid',
|
||||||
|
field_names=['uuid'])
|
||||||
|
if data.get('elapsed', None):
|
||||||
|
raise ValidationError('Only Snapshots from Workbench can have elapsed',
|
||||||
|
field_names=['elapsed'])
|
||||||
|
|
||||||
|
|
||||||
class Test(EventWithOneDevice):
|
class Test(EventWithOneDevice):
|
||||||
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
|
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
|
||||||
|
|
|
@ -8,7 +8,8 @@ from sqlalchemy.util import OrderedSet
|
||||||
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 RatingSoftware, SnapshotSoftware
|
from ereuse_devicehub.resources.enums import RatingSoftware, SnapshotSoftware
|
||||||
from ereuse_devicehub.resources.event.models import Event, Snapshot, TestDataStorage, WorkbenchRate
|
from ereuse_devicehub.resources.event.models import Event, ManualRate, Snapshot, TestDataStorage, \
|
||||||
|
WorkbenchRate
|
||||||
from teal.resource import View
|
from teal.resource import View
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,14 +41,15 @@ class SnapshotView(View):
|
||||||
|
|
||||||
# Remove new events from devices so they don't interfere with sync
|
# Remove new events from devices so they don't interfere with sync
|
||||||
events_device = set(e for e in device.events_one)
|
events_device = set(e for e in device.events_one)
|
||||||
events_components = tuple(set(e for e in component.events_one) for component in components)
|
|
||||||
device.events_one.clear()
|
device.events_one.clear()
|
||||||
|
if components:
|
||||||
|
events_components = tuple(set(e for e in c.events_one) for c in components)
|
||||||
for component in components:
|
for component in components:
|
||||||
component.events_one.clear()
|
component.events_one.clear()
|
||||||
|
|
||||||
# noinspection PyArgumentList
|
# noinspection PyArgumentList
|
||||||
assert not device.events_one
|
assert not device.events_one
|
||||||
assert all(not c.events_one for c in components)
|
assert all(not c.events_one for c in components) if components else True
|
||||||
db_device, remove_events = self.resource_def.sync.run(device, components)
|
db_device, remove_events = self.resource_def.sync.run(device, components)
|
||||||
snapshot.device = db_device
|
snapshot.device = db_device
|
||||||
snapshot.events |= remove_events | events_device
|
snapshot.events |= remove_events | events_device
|
||||||
|
@ -56,16 +58,18 @@ class SnapshotView(View):
|
||||||
ordered_components = OrderedSet(x for x in snapshot.components)
|
ordered_components = OrderedSet(x for x in snapshot.components)
|
||||||
|
|
||||||
for event in events_device:
|
for event in events_device:
|
||||||
|
if isinstance(event, ManualRate):
|
||||||
|
event.algorithm_software = RatingSoftware.Ereuse
|
||||||
|
event.algorithm_version = StrictVersion('1.0')
|
||||||
if isinstance(event, WorkbenchRate):
|
if isinstance(event, WorkbenchRate):
|
||||||
# todo process workbench rate
|
# todo process workbench rate
|
||||||
event.data_storage = 2
|
event.data_storage = 2
|
||||||
event.graphic_card = 4
|
event.graphic_card = 4
|
||||||
event.processor = 1
|
event.processor = 1
|
||||||
event.algorithm_software = RatingSoftware.Ereuse
|
|
||||||
event.algorithm_version = StrictVersion('1.0')
|
|
||||||
|
|
||||||
# Add the new events to the db-existing devices and components
|
# Add the new events to the db-existing devices and components
|
||||||
db_device.events_one |= events_device
|
db_device.events_one |= events_device
|
||||||
|
if components:
|
||||||
for component, events in zip(ordered_components, events_components):
|
for component, events in zip(ordered_components, events_components):
|
||||||
component.events_one |= events
|
component.events_one |= events
|
||||||
snapshot.events |= events
|
snapshot.events |= events
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from click import argument, option
|
from click import argument, option
|
||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
|
|
||||||
from ereuse_devicehub import devicehub
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.user.models import Organization, User
|
from ereuse_devicehub.resources.user.models import Organization, User
|
||||||
from ereuse_devicehub.resources.user.schemas import User as UserS
|
from ereuse_devicehub.resources.user.schemas import User as UserS
|
||||||
|
@ -16,7 +15,7 @@ class UserDef(Resource):
|
||||||
ID_CONVERTER = Converters.uuid
|
ID_CONVERTER = Converters.uuid
|
||||||
AUTH = True
|
AUTH = True
|
||||||
|
|
||||||
def __init__(self, app: 'devicehub.Devicehub', import_name=__package__, static_folder=None,
|
def __init__(self, app, import_name=__package__, static_folder=None,
|
||||||
static_url_path=None, template_folder=None, url_prefix=None, subdomain=None,
|
static_url_path=None, template_folder=None, url_prefix=None, subdomain=None,
|
||||||
url_defaults=None, root_path=None):
|
url_defaults=None, root_path=None):
|
||||||
cli_commands = ((self.create_user, 'create-user'),)
|
cli_commands = ((self.create_user, 'create-user'),)
|
||||||
|
|
8
setup.py
8
setup.py
|
@ -11,14 +11,16 @@ setup(
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
description='A system to manage devices focusing reuse.',
|
description='A system to manage devices focusing reuse.',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'teal>=0.2.0a1',
|
'teal>=0.2.0a2',
|
||||||
'marshmallow_enum',
|
'marshmallow_enum',
|
||||||
'ereuse-utils [Naming]>=0.3.0b1',
|
'ereuse-utils [Naming]>=0.3.0b2',
|
||||||
'psycopg2-binary',
|
'psycopg2-binary',
|
||||||
'sqlalchemy-utils',
|
'sqlalchemy-utils',
|
||||||
'requests',
|
'requests',
|
||||||
'requests-toolbelt',
|
'requests-toolbelt',
|
||||||
'hashids'
|
'hashids',
|
||||||
|
'tqdm',
|
||||||
|
'click-spinner'
|
||||||
],
|
],
|
||||||
tests_requires=[
|
tests_requires=[
|
||||||
'pytest',
|
'pytest',
|
||||||
|
|
|
@ -53,11 +53,12 @@ def app_context(app: Devicehub):
|
||||||
def user(app: Devicehub) -> UserClient:
|
def user(app: Devicehub) -> UserClient:
|
||||||
"""Gets a client with a logged-in dummy user."""
|
"""Gets a client with a logged-in dummy user."""
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
user = create_user()
|
password = 'foo'
|
||||||
|
user = create_user(password=password)
|
||||||
client = UserClient(application=app,
|
client = UserClient(application=app,
|
||||||
response_wrapper=app.response_class,
|
response_wrapper=app.response_class,
|
||||||
email=user.email,
|
email=user.email,
|
||||||
password='foo')
|
password=password)
|
||||||
client.user, _ = client.login(client.email, client.password)
|
client.user, _ = client.login(client.email, client.password)
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
17
tests/files/computer-monitor.snapshot.yaml
Normal file
17
tests/files/computer-monitor.snapshot.yaml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
type: Snapshot
|
||||||
|
software: AndroidApp
|
||||||
|
version: '1.0'
|
||||||
|
device:
|
||||||
|
type: ComputerMonitor
|
||||||
|
technology: LCD
|
||||||
|
manufacturer: Dell
|
||||||
|
model: 1707FPF
|
||||||
|
serialNumber: CN0FP446728728541C8S
|
||||||
|
resolutionWidth: 1920
|
||||||
|
resolutionHeight: 1080
|
||||||
|
size: 21.5
|
||||||
|
events:
|
||||||
|
- type: AppRate
|
||||||
|
appearanceRange: A
|
||||||
|
functionalityRange: C
|
||||||
|
labelling: False
|
|
@ -10,11 +10,12 @@ from ereuse_devicehub.client import UserClient
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.devicehub import Devicehub
|
from ereuse_devicehub.devicehub import Devicehub
|
||||||
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer, Desktop, Device, \
|
from ereuse_devicehub.resources.device.models import Component, Computer, ComputerMonitor, Desktop, \
|
||||||
GraphicCard, Laptop, Microtower, Motherboard, NetworkAdapter
|
Device, GraphicCard, Laptop, Microtower, Motherboard, NetworkAdapter
|
||||||
from ereuse_devicehub.resources.device.schemas import Device as DeviceS
|
from ereuse_devicehub.resources.device.schemas import Device as DeviceS
|
||||||
from ereuse_devicehub.resources.device.sync import MismatchBetweenTags, MismatchBetweenTagsAndHid, \
|
from ereuse_devicehub.resources.device.sync import MismatchBetweenTags, MismatchBetweenTagsAndHid, \
|
||||||
Sync
|
Sync
|
||||||
|
from ereuse_devicehub.resources.enums import ComputerMonitorTechnologies
|
||||||
from ereuse_devicehub.resources.event.models import Remove, Test
|
from ereuse_devicehub.resources.event.models import Remove, Test
|
||||||
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
|
||||||
|
@ -396,3 +397,16 @@ def test_get_devices(app: Devicehub, user: UserClient):
|
||||||
assert tuple(d['id'] for d in devices) == (1, 2, 3, 4, 5)
|
assert tuple(d['id'] for d in devices) == (1, 2, 3, 4, 5)
|
||||||
assert tuple(d['type'] for d in devices) == ('Desktop', 'Microtower',
|
assert tuple(d['type'] for d in devices) == ('Desktop', 'Microtower',
|
||||||
'Laptop', 'NetworkAdapter', 'GraphicCard')
|
'Laptop', 'NetworkAdapter', 'GraphicCard')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('app_context')
|
||||||
|
def test_computer_monitor():
|
||||||
|
m = ComputerMonitor(technology=ComputerMonitorTechnologies.LCD,
|
||||||
|
manufacturer='foo',
|
||||||
|
model='bar',
|
||||||
|
serial_number='foo-bar',
|
||||||
|
resolution_width=1920,
|
||||||
|
resolution_height=1080,
|
||||||
|
size=14.5)
|
||||||
|
db.session.add(m)
|
||||||
|
db.session.commit()
|
||||||
|
|
|
@ -69,10 +69,12 @@ def snapshot_and_check(user: UserClient,
|
||||||
assert event['type'] != 'Receive', 'All Remove events must be before the Add ones'
|
assert event['type'] != 'Receive', 'All Remove events must be before the Add ones'
|
||||||
assert input_snapshot['device']
|
assert input_snapshot['device']
|
||||||
assert_similar_device(input_snapshot['device'], snapshot['device'])
|
assert_similar_device(input_snapshot['device'], snapshot['device'])
|
||||||
|
if input_snapshot.get('components', None):
|
||||||
assert_similar_components(input_snapshot['components'], snapshot['components'])
|
assert_similar_components(input_snapshot['components'], snapshot['components'])
|
||||||
assert all(c['parent'] == snapshot['device']['id'] for c in snapshot['components']), \
|
assert all(c['parent'] == snapshot['device']['id'] for c in snapshot['components']), \
|
||||||
'Components must be in their parent'
|
'Components must be in their parent'
|
||||||
if perform_second_snapshot:
|
if perform_second_snapshot:
|
||||||
|
if 'uuid' in input_snapshot:
|
||||||
input_snapshot['uuid'] = uuid4()
|
input_snapshot['uuid'] = uuid4()
|
||||||
return snapshot_and_check(user, input_snapshot, event_types, perform_second_snapshot=False)
|
return snapshot_and_check(user, input_snapshot, event_types, perform_second_snapshot=False)
|
||||||
else:
|
else:
|
||||||
|
@ -330,3 +332,24 @@ def test_erase(user: UserClient):
|
||||||
assert step['secureRandomSteps'] == 1
|
assert step['secureRandomSteps'] == 1
|
||||||
assert step['cleanWithZeros'] is True
|
assert step['cleanWithZeros'] is True
|
||||||
assert 'num' not in step
|
assert 'num' not in step
|
||||||
|
|
||||||
|
|
||||||
|
def test_snapshot_computer_monitor(user: UserClient):
|
||||||
|
s = file('computer-monitor.snapshot')
|
||||||
|
snapshot_and_check(user, s, event_types=('AppRate',))
|
||||||
|
|
||||||
|
|
||||||
|
def test_snapshot_components_none():
|
||||||
|
"""
|
||||||
|
Tests that a snapshot without components does not
|
||||||
|
remove them from the computer.
|
||||||
|
"""
|
||||||
|
# todo test
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_snapshot_components_empty():
|
||||||
|
"""
|
||||||
|
Tests that a snapshot whose components are an empty list remove
|
||||||
|
all its components.
|
||||||
|
"""
|
||||||
|
|
Reference in a new issue