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
|
||||
```
|
||||
|
||||
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
|
||||
$ flask run
|
||||
```
|
||||
|
||||
|
||||
See the [Flask quickstart](http://flask.pocoo.org/docs/1.0/quickstart/)
|
||||
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 typing import Set
|
||||
|
||||
from ereuse_devicehub.resources.device import ComponentDef, ComputerDef, DataStorageDef, \
|
||||
DesktopDef, DeviceDef, GraphicCardDef, HardDriveDef, LaptopDef, MicrotowerDef, \
|
||||
from ereuse_devicehub.resources.device import ComponentDef, ComputerDef, ComputerMonitorDef, \
|
||||
DataStorageDef, DesktopDef, DeviceDef, GraphicCardDef, HardDriveDef, LaptopDef, MicrotowerDef, \
|
||||
MotherboardDef, NetbookDef, NetworkAdapterDef, ProcessorDef, RamModuleDef, ServerDef, \
|
||||
SolidStateDriveDef
|
||||
from ereuse_devicehub.resources.event import AddDef, AggregateRateDef, BenchmarkDataStorageDef, \
|
||||
BenchmarkDef, BenchmarkProcessorDef, BenchmarkProcessorSysbenchDef, BenchmarkRamSysbenchDef, \
|
||||
BenchmarkWithRateDef, EraseBasicDef, EraseSectorsDef, EventDef, InstallDef, \
|
||||
PhotoboxSystemRateDef, PhotoboxUserDef, RateDef, RemoveDef, SnapshotDef, StepDef, \
|
||||
from ereuse_devicehub.resources.event import AddDef, AggregateRateDef, AppRateDef, \
|
||||
BenchmarkDataStorageDef, BenchmarkDef, BenchmarkProcessorDef, BenchmarkProcessorSysbenchDef, \
|
||||
BenchmarkRamSysbenchDef, BenchmarkWithRateDef, EraseBasicDef, EraseSectorsDef, EventDef, \
|
||||
InstallDef, PhotoboxSystemRateDef, PhotoboxUserDef, RateDef, RemoveDef, SnapshotDef, StepDef, \
|
||||
StepRandomDef, StepZeroDef, StressTestDef, TestDataStorageDef, TestDef, WorkbenchRateDef
|
||||
from ereuse_devicehub.resources.inventory import InventoryDef
|
||||
from ereuse_devicehub.resources.tag import TagDef
|
||||
|
@ -19,13 +19,14 @@ from teal.config import Config
|
|||
class DevicehubConfig(Config):
|
||||
RESOURCE_DEFINITIONS = {
|
||||
DeviceDef, ComputerDef, DesktopDef, LaptopDef, NetbookDef, ServerDef,
|
||||
MicrotowerDef, ComponentDef, GraphicCardDef, DataStorageDef, SolidStateDriveDef,
|
||||
MicrotowerDef, ComputerMonitorDef, ComponentDef, GraphicCardDef, DataStorageDef,
|
||||
SolidStateDriveDef,
|
||||
HardDriveDef, MotherboardDef, NetworkAdapterDef, RamModuleDef, ProcessorDef, UserDef,
|
||||
OrganizationDef, TagDef, EventDef, AddDef, RemoveDef, EraseBasicDef, EraseSectorsDef,
|
||||
StepDef, StepZeroDef, StepRandomDef, RateDef, AggregateRateDef, WorkbenchRateDef,
|
||||
PhotoboxUserDef, PhotoboxSystemRateDef, InstallDef, SnapshotDef, TestDef,
|
||||
TestDataStorageDef, StressTestDef, WorkbenchRateDef, InventoryDef, BenchmarkDef,
|
||||
BenchmarkDataStorageDef, BenchmarkWithRateDef, BenchmarkProcessorDef,
|
||||
BenchmarkDataStorageDef, BenchmarkWithRateDef, AppRateDef, BenchmarkProcessorDef,
|
||||
BenchmarkProcessorSysbenchDef, BenchmarkRamSysbenchDef
|
||||
}
|
||||
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.client import Client
|
||||
from ereuse_devicehub.db import db
|
||||
from ereuse_devicehub.dummy.dummy import Dummy
|
||||
from teal.config import Config as ConfigClass
|
||||
from teal.teal import Teal
|
||||
|
||||
|
||||
class Devicehub(Teal):
|
||||
test_client_class = Client
|
||||
Dummy = Dummy
|
||||
|
||||
def __init__(self,
|
||||
config: ConfigClass,
|
||||
|
@ -29,5 +31,4 @@ class Devicehub(Teal):
|
|||
super().__init__(config, db, import_name, static_url_path, static_folder, static_host,
|
||||
host_matching, subdomain_matching, template_folder, instance_path,
|
||||
instance_relative_config, root_path, Auth)
|
||||
|
||||
|
||||
self.dummy = Dummy(self)
|
||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -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, \
|
||||
Device, GraphicCard, HardDrive, Laptop, Microtower, Motherboard, Netbook, NetworkAdapter, \
|
||||
Processor, RamModule, Server, SolidStateDrive
|
||||
from ereuse_devicehub.resources.device.schemas import Component, Computer, ComputerMonitor, \
|
||||
DataStorage, Desktop, Device, GraphicCard, HardDrive, Laptop, Microtower, Motherboard, Netbook, \
|
||||
NetworkAdapter, Processor, RamModule, Server, SolidStateDrive
|
||||
from ereuse_devicehub.resources.device.views import DeviceView
|
||||
from teal.resource import Converters, Resource
|
||||
|
||||
|
@ -36,6 +36,10 @@ class MicrotowerDef(ComputerDef):
|
|||
SCHEMA = Microtower
|
||||
|
||||
|
||||
class ComputerMonitorDef(DeviceDef):
|
||||
SCHEMA = ComputerMonitor
|
||||
|
||||
|
||||
class ComponentDef(DeviceDef):
|
||||
SCHEMA = Component
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ from sqlalchemy.orm import ColumnProperty, backref, relationship
|
|||
from sqlalchemy.util import OrderedSet
|
||||
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_utils.naming import Naming
|
||||
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):
|
||||
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)
|
||||
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))
|
||||
manufacturer = Column(Unicode(STR_SIZE))
|
||||
serial_number = Column(Unicode(STR_SIZE))
|
||||
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.comment = """
|
||||
The width of the device in meters.
|
||||
"""
|
||||
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))
|
||||
color = Column(ColorType)
|
||||
|
||||
|
@ -108,6 +125,28 @@ class Microtower(Computer):
|
|||
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):
|
||||
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
from typing import Dict, List, Set
|
||||
|
||||
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, \
|
||||
EventWithOneDevice
|
||||
from ereuse_devicehub.resources.image.models import ImageList
|
||||
|
@ -72,6 +73,20 @@ class Microtower(Computer):
|
|||
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):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
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.fields import Float, Integer, Str
|
||||
from marshmallow.validate import Length, OneOf, Range
|
||||
from marshmallow_enum import EnumField
|
||||
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
|
||||
|
||||
|
||||
class Device(Thing):
|
||||
# todo id is dump_only except when in Snapshot
|
||||
id = Integer(description='The identifier of the device for this database.')
|
||||
hid = Str(dump_only=True,
|
||||
description='The Hardware ID is the unique ID traceability systems '
|
||||
'use to ID a device globally.')
|
||||
id = Integer(description=m.Device.id, dump_only=True)
|
||||
hid = Str(dump_only=True, description=m.Device.hid)
|
||||
tags = NestedOn('Tag', many=True, collection_class=OrderedSet)
|
||||
model = Str(validate=Length(max=STR_BIG_SIZE))
|
||||
manufacturer = Str(validate=Length(max=STR_SIZE))
|
||||
serial_number = Str(data_key='serialNumber')
|
||||
product_id = Str(data_key='productId')
|
||||
weight = Float(validate=Range(0.1, 3),
|
||||
unit=UnitCodes.kgm,
|
||||
description='The weight of the device in Kgm.')
|
||||
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.')
|
||||
weight = Float(validate=Range(0.1, 3), unit=UnitCodes.kgm, description=m.Device.weight)
|
||||
width = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description=m.Device.width)
|
||||
height = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description=m.Device.height)
|
||||
events = NestedOn('Event', many=True, dump_only=True)
|
||||
events_one = NestedOn('Event', many=True, load_only=True, collection_class=OrderedSet)
|
||||
|
||||
|
@ -61,7 +53,6 @@ class Device(Thing):
|
|||
|
||||
class Computer(Device):
|
||||
components = NestedOn('Component', many=True, dump_only=True, collection_class=OrderedSet)
|
||||
pass
|
||||
|
||||
|
||||
class Desktop(Computer):
|
||||
|
@ -84,6 +75,18 @@ class Microtower(Computer):
|
|||
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):
|
||||
parent = NestedOn(Device, dump_only=True)
|
||||
|
||||
|
|
|
@ -149,3 +149,14 @@ class DataStorageInterface(Enum):
|
|||
ATA = 'ATA'
|
||||
USB = 'USB'
|
||||
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 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, \
|
||||
BenchmarkWithRate, EraseBasic, EraseSectors, Event, Install, PhotoboxSystemRate, \
|
||||
PhotoboxUserRate, Rate, Remove, Snapshot, Step, StepRandom, StepZero, StressTest, Test, \
|
||||
|
@ -65,6 +65,10 @@ class PhotoboxSystemRateDef(RateDef):
|
|||
SCHEMA = PhotoboxSystemRate
|
||||
|
||||
|
||||
class AppRateDef(RateDef):
|
||||
SCHEMA = AppRate
|
||||
|
||||
|
||||
class InstallDef(EventDef):
|
||||
SCHEMA = Install
|
||||
|
||||
|
|
|
@ -2,14 +2,6 @@ from collections import Iterable
|
|||
from typing import Set, Union
|
||||
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 sqlalchemy import BigInteger, Boolean, CheckConstraint, Column, DateTime, Enum as DBEnum, \
|
||||
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.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, \
|
||||
POLYMORPHIC_ON, StrictVersionType, check_range
|
||||
|
||||
|
@ -256,15 +256,18 @@ class StepRandom(Step):
|
|||
|
||||
|
||||
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)
|
||||
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)))
|
||||
|
||||
|
||||
class Install(JoinedTableMixin, EventWithOneDevice):
|
||||
name = Column(Unicode(STR_BIG_SIZE), nullable=False)
|
||||
elapsed = Column(Interval, nullable=False)
|
||||
|
||||
|
||||
|
@ -290,6 +293,20 @@ class Rate(JoinedTableMixin, EventWithOneDevice):
|
|||
def rating_range(self) -> RatingRange:
|
||||
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):
|
||||
pass
|
||||
|
@ -319,18 +336,26 @@ class RateAggregateRate(db.Model):
|
|||
primary_key=True)
|
||||
|
||||
|
||||
class WorkbenchRate(IndividualRate):
|
||||
class ManualRate(IndividualRate):
|
||||
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))
|
||||
ram = Column(Float(decimal_return_scale=2), check_range('ram', *RATE_POSITIVE))
|
||||
data_storage = Column(Float(decimal_return_scale=2),
|
||||
check_range('data_storage', *RATE_POSITIVE))
|
||||
graphic_card = Column(Float(decimal_return_scale=2),
|
||||
check_range('graphic_card', *RATE_POSITIVE))
|
||||
labelling = Column(Boolean)
|
||||
bios = Column(DBEnum(Bios))
|
||||
appearance_range = Column(DBEnum(AppearanceRange))
|
||||
functionality_range = Column(DBEnum(FunctionalityRange))
|
||||
|
||||
|
||||
class AppRate(ManualRate):
|
||||
pass
|
||||
|
||||
|
||||
class PhotoboxRate(IndividualRate):
|
||||
|
@ -369,6 +394,20 @@ class PhotoboxSystemRate(PhotoboxRate):
|
|||
class Test(JoinedTableMixin, EventWithOneDevice):
|
||||
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):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
|
||||
|
@ -396,6 +435,20 @@ class StressTest(Test):
|
|||
class Benchmark(JoinedTableMixin, EventWithOneDevice):
|
||||
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):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
|
||||
|
|
|
@ -139,17 +139,26 @@ class AggregateRate(Rate):
|
|||
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:
|
||||
super().__init__(**kwargs)
|
||||
self.processor = ... # type: float
|
||||
self.ram = ... # type: float
|
||||
self.data_storage = ... # type: float
|
||||
self.graphic_card = ... # type: float
|
||||
self.labelling = ... # type: bool
|
||||
self.bios = ... # type: Bios
|
||||
self.appearance_range = ... # type: AppearanceRange
|
||||
self.functionality_range = ... # type: FunctionalityRange
|
||||
|
||||
|
||||
class AppRate(ManualRate):
|
||||
pass
|
||||
|
||||
|
||||
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.resources.device.schemas import Component, Device
|
||||
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.schemas import Thing
|
||||
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.resource import Schema
|
||||
|
||||
|
@ -124,7 +124,7 @@ class PhotoboxRate(IndividualRate):
|
|||
# todo Image
|
||||
|
||||
|
||||
class PhotoboxUserRate(PhotoboxRate):
|
||||
class PhotoboxUserRate(IndividualRate):
|
||||
assembling = Integer()
|
||||
parts = Integer()
|
||||
buttons = Integer()
|
||||
|
@ -135,18 +135,11 @@ class PhotoboxUserRate(PhotoboxRate):
|
|||
dirt = Integer()
|
||||
|
||||
|
||||
class PhotoboxSystemRate(PhotoboxRate):
|
||||
class PhotoboxSystemRate(IndividualRate):
|
||||
pass
|
||||
|
||||
|
||||
class WorkbenchRate(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.')
|
||||
class ManualRate(IndividualRate):
|
||||
appearance_range = EnumField(AppearanceRange,
|
||||
required=True,
|
||||
data_key='appearanceRange',
|
||||
|
@ -156,6 +149,20 @@ class WorkbenchRate(IndividualRate):
|
|||
required=True,
|
||||
data_key='functionalityRange',
|
||||
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):
|
||||
|
@ -172,7 +179,7 @@ class Snapshot(EventWithOneDevice):
|
|||
|
||||
See docs for more info.
|
||||
"""
|
||||
uuid = UUID(required=True)
|
||||
uuid = UUID()
|
||||
software = EnumField(SnapshotSoftware,
|
||||
required=True,
|
||||
description='The software that generated this Snapshot.')
|
||||
|
@ -185,7 +192,7 @@ class Snapshot(EventWithOneDevice):
|
|||
'the async Snapshot.')
|
||||
|
||||
device = NestedOn(Device)
|
||||
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
|
||||
elapsed = TimeDelta(precision=TimeDelta.SECONDS)
|
||||
components = NestedOn(Component,
|
||||
many=True,
|
||||
description='A list of components that are inside of the device'
|
||||
|
@ -206,10 +213,29 @@ class Snapshot(EventWithOneDevice):
|
|||
@validates_schema
|
||||
def validate_components_only_workbench(self, data: dict):
|
||||
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',
|
||||
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):
|
||||
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.resources.device.models import Component, Computer
|
||||
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
|
||||
|
||||
|
||||
|
@ -40,14 +41,15 @@ class SnapshotView(View):
|
|||
|
||||
# Remove new events from devices so they don't interfere with sync
|
||||
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()
|
||||
if components:
|
||||
events_components = tuple(set(e for e in c.events_one) for c in components)
|
||||
for component in components:
|
||||
component.events_one.clear()
|
||||
|
||||
# noinspection PyArgumentList
|
||||
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)
|
||||
snapshot.device = db_device
|
||||
snapshot.events |= remove_events | events_device
|
||||
|
@ -56,16 +58,18 @@ class SnapshotView(View):
|
|||
ordered_components = OrderedSet(x for x in snapshot.components)
|
||||
|
||||
for event in events_device:
|
||||
if isinstance(event, ManualRate):
|
||||
event.algorithm_software = RatingSoftware.Ereuse
|
||||
event.algorithm_version = StrictVersion('1.0')
|
||||
if isinstance(event, WorkbenchRate):
|
||||
# todo process workbench rate
|
||||
event.data_storage = 2
|
||||
event.graphic_card = 4
|
||||
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
|
||||
db_device.events_one |= events_device
|
||||
if components:
|
||||
for component, events in zip(ordered_components, events_components):
|
||||
component.events_one |= events
|
||||
snapshot.events |= events
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from click import argument, option
|
||||
from flask import current_app as app
|
||||
|
||||
from ereuse_devicehub import devicehub
|
||||
from ereuse_devicehub.db import db
|
||||
from ereuse_devicehub.resources.user.models import Organization, User
|
||||
from ereuse_devicehub.resources.user.schemas import User as UserS
|
||||
|
@ -16,7 +15,7 @@ class UserDef(Resource):
|
|||
ID_CONVERTER = Converters.uuid
|
||||
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,
|
||||
url_defaults=None, root_path=None):
|
||||
cli_commands = ((self.create_user, 'create-user'),)
|
||||
|
|
8
setup.py
8
setup.py
|
@ -11,14 +11,16 @@ setup(
|
|||
include_package_data=True,
|
||||
description='A system to manage devices focusing reuse.',
|
||||
install_requires=[
|
||||
'teal>=0.2.0a1',
|
||||
'teal>=0.2.0a2',
|
||||
'marshmallow_enum',
|
||||
'ereuse-utils [Naming]>=0.3.0b1',
|
||||
'ereuse-utils [Naming]>=0.3.0b2',
|
||||
'psycopg2-binary',
|
||||
'sqlalchemy-utils',
|
||||
'requests',
|
||||
'requests-toolbelt',
|
||||
'hashids'
|
||||
'hashids',
|
||||
'tqdm',
|
||||
'click-spinner'
|
||||
],
|
||||
tests_requires=[
|
||||
'pytest',
|
||||
|
|
|
@ -53,11 +53,12 @@ def app_context(app: Devicehub):
|
|||
def user(app: Devicehub) -> UserClient:
|
||||
"""Gets a client with a logged-in dummy user."""
|
||||
with app.app_context():
|
||||
user = create_user()
|
||||
password = 'foo'
|
||||
user = create_user(password=password)
|
||||
client = UserClient(application=app,
|
||||
response_wrapper=app.response_class,
|
||||
email=user.email,
|
||||
password='foo')
|
||||
password=password)
|
||||
client.user, _ = client.login(client.email, client.password)
|
||||
return client
|
||||
|
||||
|
|
|
@ -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.devicehub import Devicehub
|
||||
from ereuse_devicehub.resources.device.exceptions import NeedsId
|
||||
from ereuse_devicehub.resources.device.models import Component, Computer, Desktop, Device, \
|
||||
GraphicCard, Laptop, Microtower, Motherboard, NetworkAdapter
|
||||
from ereuse_devicehub.resources.device.models import Component, Computer, ComputerMonitor, Desktop, \
|
||||
Device, GraphicCard, Laptop, Microtower, Motherboard, NetworkAdapter
|
||||
from ereuse_devicehub.resources.device.schemas import Device as DeviceS
|
||||
from ereuse_devicehub.resources.device.sync import MismatchBetweenTags, MismatchBetweenTagsAndHid, \
|
||||
Sync
|
||||
from ereuse_devicehub.resources.enums import ComputerMonitorTechnologies
|
||||
from ereuse_devicehub.resources.event.models import Remove, Test
|
||||
from ereuse_devicehub.resources.tag.model import Tag
|
||||
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['type'] for d in devices) == ('Desktop', 'Microtower',
|
||||
'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 input_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 all(c['parent'] == snapshot['device']['id'] for c in snapshot['components']), \
|
||||
'Components must be in their parent'
|
||||
if perform_second_snapshot:
|
||||
if 'uuid' in input_snapshot:
|
||||
input_snapshot['uuid'] = uuid4()
|
||||
return snapshot_and_check(user, input_snapshot, event_types, perform_second_snapshot=False)
|
||||
else:
|
||||
|
@ -330,3 +332,24 @@ def test_erase(user: UserClient):
|
|||
assert step['secureRandomSteps'] == 1
|
||||
assert step['cleanWithZeros'] is True
|
||||
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 New Issue