193 lines
6 KiB
Python
193 lines
6 KiB
Python
from marshmallow import post_load, pre_load
|
|
from marshmallow.fields import Boolean, Float, Integer, Str, String
|
|
from marshmallow.validate import Length, OneOf, Range
|
|
from sqlalchemy.util import OrderedSet
|
|
from stdnum import imei, meid
|
|
from teal.marshmallow import EnumField, SanitizedStr, URL, ValidationError
|
|
from teal.resource import Schema
|
|
|
|
from ereuse_devicehub.marshmallow import NestedOn
|
|
from ereuse_devicehub.resources.device import models as m
|
|
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, DisplayTech, \
|
|
RamFormat, RamInterface
|
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
|
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
|
|
|
|
|
class Device(Thing):
|
|
id = Integer(description=m.Device.id.comment, dump_only=True)
|
|
hid = SanitizedStr(lower=True, dump_only=True, description=m.Device.hid.comment)
|
|
tags = NestedOn('Tag',
|
|
many=True,
|
|
collection_class=OrderedSet,
|
|
description='The set of tags that identify the device.')
|
|
model = SanitizedStr(lower=True, validate=Length(max=STR_BIG_SIZE))
|
|
manufacturer = SanitizedStr(lower=True, validate=Length(max=STR_SIZE))
|
|
serial_number = SanitizedStr(lower=True, data_key='serialNumber')
|
|
weight = Float(validate=Range(0.1, 3), unit=UnitCodes.kgm, description=m.Device.weight.comment)
|
|
width = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description=m.Device.width.comment)
|
|
height = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description=m.Device.height.comment)
|
|
events = NestedOn('Event', many=True, dump_only=True, description=m.Device.events.__doc__)
|
|
events_one = NestedOn('Event', many=True, load_only=True, collection_class=OrderedSet)
|
|
|
|
@pre_load
|
|
def from_events_to_events_one(self, data: dict):
|
|
"""
|
|
Not an elegant way of allowing submitting events to a device
|
|
(in the context of Snapshots) without creating an ``events``
|
|
field at the model (which is not possible).
|
|
:param data:
|
|
:return:
|
|
"""
|
|
# Note that it is secure to allow uploading events_one
|
|
# as the only time an user can send a device object is
|
|
# in snapshots.
|
|
data['events_one'] = data.pop('events', [])
|
|
return data
|
|
|
|
@post_load
|
|
def validate_snapshot_events(self, data):
|
|
"""Validates that only snapshot-related events can be uploaded."""
|
|
from ereuse_devicehub.resources.event.models import EraseBasic, Test, Rate, Install, \
|
|
Benchmark
|
|
for event in data['events_one']:
|
|
if not isinstance(event, (Install, EraseBasic, Rate, Test, Benchmark)):
|
|
raise ValidationError('You cannot upload {}'.format(event), field_names=['events'])
|
|
|
|
|
|
class Computer(Device):
|
|
components = NestedOn('Component', many=True, dump_only=True, collection_class=OrderedSet)
|
|
chassis = EnumField(ComputerChassis, required=True)
|
|
|
|
|
|
class Desktop(Computer):
|
|
pass
|
|
|
|
|
|
class Laptop(Computer):
|
|
pass
|
|
|
|
|
|
class Server(Computer):
|
|
pass
|
|
|
|
|
|
class DisplayMixin:
|
|
size = Float(description=m.DisplayMixin.size.comment, validate=Range(2, 150))
|
|
technology = EnumField(DisplayTech,
|
|
description=m.DisplayMixin.technology.comment)
|
|
resolution_width = Integer(data_key='resolutionWidth',
|
|
validate=Range(10, 20000),
|
|
description=m.DisplayMixin.resolution_width.comment)
|
|
resolution_height = Integer(data_key='resolutionHeight',
|
|
validate=Range(10, 20000),
|
|
description=m.DisplayMixin.resolution_height.comment)
|
|
|
|
|
|
class NetworkMixin:
|
|
speed = Integer(validate=Range(min=10, max=10000),
|
|
unit=UnitCodes.mbps,
|
|
description=m.NetworkAdapter.speed.comment)
|
|
wireless = Boolean(required=True)
|
|
|
|
|
|
class Monitor(DisplayMixin, Device):
|
|
pass
|
|
|
|
|
|
class ComputerMonitor(Monitor):
|
|
pass
|
|
|
|
|
|
class TelevisionSet(Monitor):
|
|
pass
|
|
|
|
|
|
class Mobile(Device):
|
|
imei = Integer(validate=lambda x: imei.validate(str(x)),
|
|
description=m.Mobile.imei.comment)
|
|
meid = Str(validate=meid.validate, description=m.Mobile.meid.comment)
|
|
|
|
@post_load
|
|
def convert_meid(self, data: dict):
|
|
if 'meid' in data:
|
|
data['meid'] = meid.compact(data['meid'])
|
|
|
|
|
|
class Smartphone(Mobile):
|
|
pass
|
|
|
|
|
|
class Tablet(Mobile):
|
|
pass
|
|
|
|
|
|
class Cellphone(Mobile):
|
|
pass
|
|
|
|
|
|
class Component(Device):
|
|
parent = NestedOn(Device, dump_only=True)
|
|
|
|
|
|
class GraphicCard(Component):
|
|
memory = Integer(validate=Range(0, 10000),
|
|
unit=UnitCodes.mbyte,
|
|
description=m.GraphicCard.memory.comment)
|
|
|
|
|
|
class DataStorage(Component):
|
|
size = Integer(validate=Range(0, 10 ** 8),
|
|
unit=UnitCodes.mbyte,
|
|
description=m.DataStorage.size.comment)
|
|
interface = EnumField(DataStorageInterface)
|
|
|
|
|
|
class HardDrive(DataStorage):
|
|
pass
|
|
|
|
|
|
class SolidStateDrive(DataStorage):
|
|
pass
|
|
|
|
|
|
class Motherboard(Component):
|
|
slots = Integer(validate=Range(0, 20),
|
|
description=m.Motherboard.slots.comment)
|
|
usb = Integer(validate=Range(0, 20))
|
|
firewire = Integer(validate=Range(0, 20))
|
|
serial = Integer(validate=Range(0, 20))
|
|
pcmcia = Integer(validate=Range(0, 20))
|
|
|
|
|
|
class NetworkAdapter(NetworkMixin, Component):
|
|
pass
|
|
|
|
|
|
class Processor(Component):
|
|
speed = Float(validate=Range(min=0.1, max=15), unit=UnitCodes.ghz)
|
|
cores = Integer(validate=Range(min=1, max=10))
|
|
threads = Integer(validate=Range(min=1, max=20))
|
|
address = Integer(validate=OneOf({8, 16, 32, 64, 128, 256}))
|
|
|
|
|
|
class RamModule(Component):
|
|
size = Integer(validate=Range(min=128, max=17000), unit=UnitCodes.mbyte)
|
|
speed = Integer(validate=Range(min=100, max=10000), unit=UnitCodes.mhz)
|
|
interface = EnumField(RamInterface)
|
|
format = EnumField(RamFormat)
|
|
|
|
|
|
class SoundCard(Component):
|
|
pass
|
|
|
|
|
|
class Display(DisplayMixin, Component):
|
|
pass
|
|
|
|
|
|
class Manufacturer(Schema):
|
|
name = String(dump_only=True)
|
|
url = URL(dump_only=True)
|
|
logo = URL(dump_only=True)
|