This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
devicehub-teal/ereuse_devicehub/resources/device/schemas.py

452 lines
15 KiB
Python

import datetime
from marshmallow import post_load, pre_load
from marshmallow.fields import Boolean, Date, DateTime, 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 EnumField, SanitizedStr, URL, 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, 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, dump_only=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')
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)
actions = NestedOn('Action', many=True, dump_only=True, description=m.Device.actions.__doc__)
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__)
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__)
trading = EnumField(states.Trading, dump_only=True, description=m.Device.trading.__doc__)
physical = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__)
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)
@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 EraseBasic, Test, Rate, Install, \
Benchmark
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__
components = NestedOn('Component',
many=True,
dump_only=True,
collection_class=OrderedSet,
description='The components that are inside this computer.')
chassis = EnumField(enums.ComputerChassis,
required=True,
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__)
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), required=True)
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,
required=True)
resolution_height = Integer(data_key='resolutionHeight',
validate=Range(10, 20000),
description=m.DisplayMixin.resolution_height.comment,
required=True)
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 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)
@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
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
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