588 lines
16 KiB
Python
588 lines
16 KiB
Python
import datetime
|
|
|
|
from marshmallow import fields as f
|
|
from marshmallow import post_load, pre_load
|
|
from marshmallow.fields import (
|
|
UUID,
|
|
Boolean,
|
|
Date,
|
|
DateTime,
|
|
Dict,
|
|
Float,
|
|
Integer,
|
|
List,
|
|
Str,
|
|
String,
|
|
)
|
|
from marshmallow.validate import Length, OneOf, Range
|
|
from sqlalchemy.util import OrderedSet
|
|
from stdnum import imei, meid
|
|
from teal.enums import Layouts
|
|
from teal.marshmallow import URL, EnumField, SanitizedStr, ValidationError
|
|
from teal.resource import Schema
|
|
|
|
from ereuse_devicehub.marshmallow import NestedOn
|
|
from ereuse_devicehub.resources import enums
|
|
from ereuse_devicehub.resources.device import models as m
|
|
from ereuse_devicehub.resources.device import states
|
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
|
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
|
|
|
|
|
class Device(Thing):
|
|
__doc__ = m.Device.__doc__
|
|
id = Integer(description=m.Device.id.comment, dump_only=True)
|
|
hid = SanitizedStr(lower=True, description=m.Device.hid.comment)
|
|
tags = NestedOn(
|
|
'Tag',
|
|
many=True,
|
|
collection_class=OrderedSet,
|
|
description='A set of tags that identify the device.',
|
|
)
|
|
model = SanitizedStr(
|
|
lower=True,
|
|
validate=Length(max=STR_BIG_SIZE),
|
|
description=m.Device.model.comment,
|
|
)
|
|
manufacturer = SanitizedStr(
|
|
lower=True,
|
|
validate=Length(max=STR_SIZE),
|
|
description=m.Device.manufacturer.comment,
|
|
)
|
|
serial_number = SanitizedStr(
|
|
lower=True, validate=Length(max=STR_BIG_SIZE), data_key='serialNumber'
|
|
)
|
|
part_number = SanitizedStr(
|
|
lower=True, validate=Length(max=STR_BIG_SIZE), data_key='partNumber'
|
|
)
|
|
brand = SanitizedStr(
|
|
validate=Length(max=STR_BIG_SIZE), description=m.Device.brand.comment
|
|
)
|
|
generation = Integer(
|
|
validate=Range(1, 100), description=m.Device.generation.comment
|
|
)
|
|
version = SanitizedStr(description=m.Device.version)
|
|
weight = Float(
|
|
validate=Range(0.1, 5), unit=UnitCodes.kgm, description=m.Device.weight.comment
|
|
)
|
|
width = Float(
|
|
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment
|
|
)
|
|
height = Float(
|
|
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment
|
|
)
|
|
depth = Float(
|
|
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment
|
|
)
|
|
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
|
actions = NestedOn(
|
|
'Action', many=True, dump_only=True, description=m.Device.actions.__doc__
|
|
)
|
|
# TODO TimeOut 2. Comment actions_one and lots if there are time out.
|
|
actions_one = NestedOn(
|
|
'Action', many=True, load_only=True, collection_class=OrderedSet
|
|
)
|
|
problems = NestedOn(
|
|
'Action', many=True, dump_only=True, description=m.Device.problems.__doc__
|
|
)
|
|
url = URL(dump_only=True, description=m.Device.url.__doc__)
|
|
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
|
lots = NestedOn(
|
|
'Lot',
|
|
many=True,
|
|
dump_only=True,
|
|
description='The lots where this device is directly under.',
|
|
)
|
|
rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__)
|
|
price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__)
|
|
tradings = Dict(dump_only=True, description='')
|
|
physical = EnumField(
|
|
states.Physical, dump_only=True, description=m.Device.physical.__doc__
|
|
)
|
|
traking = EnumField(
|
|
states.Traking, dump_only=True, description=m.Device.physical.__doc__
|
|
)
|
|
usage = EnumField(
|
|
states.Usage, dump_only=True, description=m.Device.physical.__doc__
|
|
)
|
|
revoke = UUID(dump_only=True)
|
|
physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor')
|
|
production_date = DateTime(
|
|
'iso', description=m.Device.updated.comment, data_key='productionDate'
|
|
)
|
|
working = NestedOn(
|
|
'Action', many=True, dump_only=True, description=m.Device.working.__doc__
|
|
)
|
|
variant = SanitizedStr(description=m.Device.variant.comment)
|
|
sku = SanitizedStr(description=m.Device.sku.comment)
|
|
image = URL(description=m.Device.image.comment)
|
|
allocated = Boolean(description=m.Device.allocated.comment)
|
|
dhid = SanitizedStr(
|
|
data_key='devicehubID', description=m.Device.devicehub_id.comment
|
|
)
|
|
|
|
@pre_load
|
|
def from_actions_to_actions_one(self, data: dict):
|
|
"""
|
|
Not an elegant way of allowing submitting actions to a device
|
|
(in the context of Snapshots) without creating an ``actions``
|
|
field at the model (which is not possible).
|
|
:param data:
|
|
:return:
|
|
"""
|
|
# Note that it is secure to allow uploading actions_one
|
|
# as the only time an user can send a device object is
|
|
# in snapshots.
|
|
data['actions_one'] = data.pop('actions', [])
|
|
return data
|
|
|
|
@post_load
|
|
def validate_snapshot_actions(self, data):
|
|
"""Validates that only snapshot-related actions can be uploaded."""
|
|
from ereuse_devicehub.resources.action.models import (
|
|
Benchmark,
|
|
EraseBasic,
|
|
Install,
|
|
Rate,
|
|
Test,
|
|
)
|
|
|
|
for action in data['actions_one']:
|
|
if not isinstance(action, (Install, EraseBasic, Rate, Test, Benchmark)):
|
|
raise ValidationError(
|
|
'You cannot upload {}'.format(action), field_names=['actions']
|
|
)
|
|
|
|
|
|
class Computer(Device):
|
|
__doc__ = m.Computer.__doc__
|
|
# TODO TimeOut 1. Comment components if there are time out.
|
|
components = NestedOn(
|
|
'Component',
|
|
many=True,
|
|
dump_only=True,
|
|
collection_class=OrderedSet,
|
|
description='The components that are inside this computer.',
|
|
)
|
|
chassis = EnumField(enums.ComputerChassis, description=m.Computer.chassis.comment)
|
|
ram_size = Integer(
|
|
dump_only=True, data_key='ramSize', description=m.Computer.ram_size.__doc__
|
|
)
|
|
data_storage_size = Integer(
|
|
dump_only=True,
|
|
data_key='dataStorageSize',
|
|
description=m.Computer.data_storage_size.__doc__,
|
|
)
|
|
processor_model = Str(
|
|
dump_only=True,
|
|
data_key='processorModel',
|
|
description=m.Computer.processor_model.__doc__,
|
|
)
|
|
graphic_card_model = Str(
|
|
dump_only=True,
|
|
data_key='graphicCardModel',
|
|
description=m.Computer.graphic_card_model.__doc__,
|
|
)
|
|
network_speeds = List(
|
|
Integer(dump_only=True),
|
|
dump_only=True,
|
|
data_key='networkSpeeds',
|
|
description=m.Computer.network_speeds.__doc__,
|
|
)
|
|
privacy = NestedOn(
|
|
'Action',
|
|
many=True,
|
|
dump_only=True,
|
|
collection_class=set,
|
|
description=m.Computer.privacy.__doc__,
|
|
)
|
|
amount = Integer(
|
|
validate=f.validate.Range(min=0, max=100), description=m.Computer.amount.__doc__
|
|
)
|
|
# author_id = NestedOn(s_user.User, only_query='author_id')
|
|
owner_id = UUID(data_key='ownerID')
|
|
transfer_state = EnumField(
|
|
enums.TransferState, description=m.Computer.transfer_state.comment
|
|
)
|
|
receiver_id = UUID(data_key='receiverID')
|
|
system_uuid = UUID(required=False)
|
|
|
|
|
|
class Desktop(Computer):
|
|
__doc__ = m.Desktop.__doc__
|
|
|
|
|
|
class Laptop(Computer):
|
|
__doc__ = m.Laptop.__doc__
|
|
layout = EnumField(Layouts, description=m.Laptop.layout.comment)
|
|
|
|
|
|
class Server(Computer):
|
|
__doc__ = m.Server.__doc__
|
|
|
|
|
|
class DisplayMixin:
|
|
__doc__ = m.DisplayMixin.__doc__
|
|
size = Float(description=m.DisplayMixin.size.comment, validate=Range(2, 150))
|
|
technology = EnumField(
|
|
enums.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,
|
|
)
|
|
refresh_rate = Integer(data_key='refreshRate', validate=Range(10, 1000))
|
|
contrast_ratio = Integer(data_key='contrastRatio', validate=Range(100, 100000))
|
|
touchable = Boolean(description=m.DisplayMixin.touchable.comment)
|
|
aspect_ratio = String(
|
|
dump_only=True, description=m.DisplayMixin.aspect_ratio.__doc__
|
|
)
|
|
widescreen = Boolean(dump_only=True, description=m.DisplayMixin.widescreen.__doc__)
|
|
|
|
|
|
class NetworkMixin:
|
|
__doc__ = m.NetworkMixin.__doc__
|
|
|
|
speed = Integer(
|
|
validate=Range(min=10, max=10000),
|
|
unit=UnitCodes.mbps,
|
|
description=m.NetworkAdapter.speed.comment,
|
|
)
|
|
wireless = Boolean(required=True)
|
|
|
|
|
|
class Monitor(DisplayMixin, Device):
|
|
__doc__ = m.Monitor.__doc__
|
|
|
|
|
|
class ComputerMonitor(Monitor):
|
|
__doc__ = m.ComputerMonitor.__doc__
|
|
|
|
|
|
class TelevisionSet(Monitor):
|
|
__doc__ = m.TelevisionSet.__doc__
|
|
|
|
|
|
class Projector(Monitor):
|
|
__doc__ = m.Projector.__doc__
|
|
|
|
|
|
class Mobile(Device):
|
|
__doc__ = m.Mobile.__doc__
|
|
|
|
imei = Integer(description=m.Mobile.imei.comment)
|
|
meid = Str(description=m.Mobile.meid.comment)
|
|
ram_size = Integer(
|
|
validate=Range(min=128, max=36000),
|
|
data_key='ramSize',
|
|
unit=UnitCodes.mbyte,
|
|
description=m.Mobile.ram_size.comment,
|
|
)
|
|
data_storage_size = Integer(
|
|
validate=Range(0, 10**8),
|
|
data_key='dataStorageSize',
|
|
description=m.Mobile.data_storage_size,
|
|
)
|
|
display_size = Float(
|
|
validate=Range(min=0.1, max=30.0),
|
|
data_key='displaySize',
|
|
description=m.Mobile.display_size.comment,
|
|
)
|
|
|
|
@pre_load
|
|
def convert_check_imei(self, data):
|
|
if data.get('imei', None):
|
|
data['imei'] = int(imei.validate(data['imei']))
|
|
return data
|
|
|
|
@pre_load
|
|
def convert_check_meid(self, data: dict):
|
|
if data.get('meid', None):
|
|
data['meid'] = meid.compact(data['meid'])
|
|
return data
|
|
|
|
|
|
class Smartphone(Mobile):
|
|
__doc__ = m.Smartphone.__doc__
|
|
|
|
|
|
class Tablet(Mobile):
|
|
__doc__ = m.Tablet.__doc__
|
|
|
|
|
|
class Cellphone(Mobile):
|
|
__doc__ = m.Cellphone.__doc__
|
|
|
|
|
|
class Component(Device):
|
|
__doc__ = m.Component.__doc__
|
|
|
|
parent = NestedOn(Device, dump_only=True)
|
|
|
|
|
|
class GraphicCard(Component):
|
|
__doc__ = m.GraphicCard.__doc__
|
|
|
|
memory = Integer(
|
|
validate=Range(0, 10000),
|
|
unit=UnitCodes.mbyte,
|
|
description=m.GraphicCard.memory.comment,
|
|
)
|
|
|
|
|
|
class DataStorage(Component):
|
|
__doc__ = m.DataStorage.__doc__
|
|
|
|
size = Integer(
|
|
validate=Range(0, 10**8),
|
|
unit=UnitCodes.mbyte,
|
|
description=m.DataStorage.size.comment,
|
|
)
|
|
interface = EnumField(enums.DataStorageInterface)
|
|
privacy = NestedOn('Action', dump_only=True)
|
|
|
|
|
|
class HardDrive(DataStorage):
|
|
__doc__ = m.HardDrive.__doc__
|
|
|
|
|
|
class SolidStateDrive(DataStorage):
|
|
__doc__ = m.SolidStateDrive.__doc__
|
|
|
|
|
|
class Motherboard(Component):
|
|
__doc__ = m.Motherboard.__doc__
|
|
|
|
slots = Integer(validate=Range(0, 20), description=m.Motherboard.slots.comment)
|
|
usb = Integer(validate=Range(0, 20), description=m.Motherboard.usb.comment)
|
|
firewire = Integer(
|
|
validate=Range(0, 20), description=m.Motherboard.firewire.comment
|
|
)
|
|
serial = Integer(validate=Range(0, 20), description=m.Motherboard.serial.comment)
|
|
pcmcia = Integer(validate=Range(0, 20), description=m.Motherboard.pcmcia.comment)
|
|
bios_date = Date(
|
|
validate=Range(
|
|
datetime.date(year=1980, month=1, day=1),
|
|
datetime.date(year=2030, month=1, day=1),
|
|
),
|
|
data_key='biosDate',
|
|
description=m.Motherboard.bios_date,
|
|
)
|
|
ram_slots = Integer(validate=Range(1), data_key='ramSlots')
|
|
ram_max_size = Integer(validate=Range(1), data_key='ramMaxSize')
|
|
|
|
|
|
class NetworkAdapter(NetworkMixin, Component):
|
|
__doc__ = m.NetworkAdapter.__doc__
|
|
|
|
|
|
class Processor(Component):
|
|
__doc__ = m.Processor.__doc__
|
|
|
|
speed = Float(
|
|
validate=Range(min=0.1, max=15),
|
|
unit=UnitCodes.ghz,
|
|
description=m.Processor.speed.comment,
|
|
)
|
|
cores = Integer(
|
|
validate=Range(min=1, max=10), description=m.Processor.cores.comment
|
|
)
|
|
threads = Integer(
|
|
validate=Range(min=1, max=20), description=m.Processor.threads.comment
|
|
)
|
|
address = Integer(
|
|
validate=OneOf({8, 16, 32, 64, 128, 256}),
|
|
description=m.Processor.address.comment,
|
|
)
|
|
abi = SanitizedStr(lower=True, description=m.Processor.abi.comment)
|
|
|
|
|
|
class RamModule(Component):
|
|
__doc__ = m.RamModule.__doc__
|
|
|
|
size = Integer(
|
|
validate=Range(min=128, max=17000),
|
|
unit=UnitCodes.mbyte,
|
|
description=m.RamModule.size.comment,
|
|
)
|
|
speed = Integer(validate=Range(min=100, max=10000), unit=UnitCodes.mhz)
|
|
interface = EnumField(enums.RamInterface)
|
|
format = EnumField(enums.RamFormat)
|
|
|
|
|
|
class SoundCard(Component):
|
|
__doc__ = m.SoundCard.__doc__
|
|
|
|
|
|
class Display(DisplayMixin, Component):
|
|
__doc__ = m.Display.__doc__
|
|
|
|
|
|
class Battery(Component):
|
|
__doc__ = m.Battery.__doc__
|
|
|
|
wireless = Boolean(description=m.Battery.wireless.comment)
|
|
technology = EnumField(
|
|
enums.BatteryTechnology, description=m.Battery.technology.comment
|
|
)
|
|
size = Integer(required=True, description=m.Battery.size.comment)
|
|
|
|
|
|
class Camera(Component):
|
|
__doc__ = m.Camera.__doc__
|
|
|
|
focal_length = Integer(data_key='focalLength')
|
|
video_height = Integer(data_key='videoHeight')
|
|
video_width = Integer(data_key='videoWidth')
|
|
horizontal_view_angle = Integer(data_key='horizontalViewAngle')
|
|
facing = Integer()
|
|
vertical_view_angle = Integer(data_key='verticalViewAngle')
|
|
video_stabilization = Integer(data_key='videoStabilization')
|
|
flash = Integer()
|
|
|
|
|
|
class Manufacturer(Schema):
|
|
__doc__ = m.Manufacturer.__doc__
|
|
|
|
name = String(dump_only=True)
|
|
url = URL(dump_only=True)
|
|
logo = URL(dump_only=True)
|
|
|
|
|
|
class ComputerAccessory(Device):
|
|
__doc__ = m.ComputerAccessory.__doc__
|
|
|
|
|
|
class Mouse(ComputerAccessory):
|
|
__doc__ = m.Mouse.__doc__
|
|
|
|
|
|
class MemoryCardReader(ComputerAccessory):
|
|
__doc__ = m.MemoryCardReader.__doc__
|
|
|
|
|
|
class SAI(ComputerAccessory):
|
|
__doc__ = m.SAI.__doc__
|
|
|
|
|
|
class Keyboard(ComputerAccessory):
|
|
__doc__ = m.Keyboard.__doc__
|
|
|
|
layout = EnumField(Layouts)
|
|
|
|
|
|
class Networking(NetworkMixin, Device):
|
|
__doc__ = m.Networking.__doc__
|
|
|
|
|
|
class Router(Networking):
|
|
__doc__ = m.Router.__doc__
|
|
|
|
|
|
class Switch(Networking):
|
|
__doc__ = m.Switch.__doc__
|
|
|
|
|
|
class Hub(Networking):
|
|
__doc__ = m.Hub.__doc__
|
|
|
|
|
|
class WirelessAccessPoint(Networking):
|
|
__doc__ = m.WirelessAccessPoint.__doc__
|
|
|
|
|
|
class Printer(Device):
|
|
__doc__ = m.Printer.__doc__
|
|
|
|
wireless = Boolean(
|
|
required=True, missing=False, description=m.Printer.wireless.comment
|
|
)
|
|
scanning = Boolean(
|
|
required=True, missing=False, description=m.Printer.scanning.comment
|
|
)
|
|
technology = EnumField(
|
|
enums.PrinterTechnology, required=True, description=m.Printer.technology.comment
|
|
)
|
|
monochrome = Boolean(
|
|
required=True, missing=True, description=m.Printer.monochrome.comment
|
|
)
|
|
|
|
|
|
class LabelPrinter(Printer):
|
|
__doc__ = m.LabelPrinter.__doc__
|
|
|
|
|
|
class Sound(Device):
|
|
__doc__ = m.Sound.__doc__
|
|
|
|
|
|
class Microphone(Sound):
|
|
__doc__ = m.Microphone.__doc__
|
|
|
|
|
|
class Video(Device):
|
|
__doc__ = m.Video.__doc__
|
|
|
|
|
|
class VideoScaler(Video):
|
|
__doc__ = m.VideoScaler.__doc__
|
|
|
|
|
|
class Videoconference(Video):
|
|
__doc__ = m.Videoconference.__doc__
|
|
|
|
|
|
class Cooking(Device):
|
|
__doc__ = m.Cooking.__doc__
|
|
|
|
|
|
class Mixer(Cooking):
|
|
__doc__ = m.Mixer.__doc__
|
|
|
|
|
|
class DIYAndGardening(Device):
|
|
pass
|
|
|
|
|
|
class Drill(DIYAndGardening):
|
|
max_drill_bit_size = Integer(data_key='maxDrillBitSize')
|
|
|
|
|
|
class PackOfScrewdrivers(DIYAndGardening):
|
|
size = Integer()
|
|
|
|
|
|
class Home(Device):
|
|
pass
|
|
|
|
|
|
class Dehumidifier(Home):
|
|
size = Integer()
|
|
|
|
|
|
class Stairs(Home):
|
|
max_allowed_weight = Integer(data_key='maxAllowedWeight')
|
|
|
|
|
|
class Recreation(Device):
|
|
pass
|
|
|
|
|
|
class Bike(Recreation):
|
|
wheel_size = Integer(data_key='wheelSize')
|
|
gears = Integer()
|
|
|
|
|
|
class Racket(Recreation):
|
|
pass
|
|
|
|
|
|
class Other(Device):
|
|
pass
|