Add ComputerAccessory, Networking, Printer, Sound, Video devices

This commit is contained in:
Xavier Bustamante Talavera 2018-10-23 15:37:37 +02:00
parent 46f765a683
commit 10c73a4e75
11 changed files with 582 additions and 127 deletions

View File

@ -0,0 +1,14 @@
type: Snapshot
version: '1.0'
software: Web
device:
type: Keyboard
model: FOO
serialNumber: BAR
manufacturer: BAZ
layout: ES
events:
- type: ManualRate
appearanceRange: A
functionalityRange: A
labelling: False

View File

@ -161,6 +161,121 @@ class DisplayDef(ComponentDef):
SCHEMA = schemas.Display SCHEMA = schemas.Display
class ComputerAccessoryDef(DeviceDef):
VIEW = None
SCHEMA = schemas.ComputerAccessory
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
url_prefix, subdomain, url_defaults, root_path, cli_commands)
class MouseDef(ComputerAccessoryDef):
VIEW = None
SCHEMA = schemas.Mouse
class KeyboardDef(ComputerAccessoryDef):
VIEW = None
SCHEMA = schemas.Keyboard
class SAIDef(ComputerAccessoryDef):
VIEW = None
SCHEMA = schemas.SAI
class MemoryCardReaderDef(ComputerAccessoryDef):
VIEW = None
SCHEMA = schemas.MemoryCardReader
class NetworkingDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Networking
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
url_prefix, subdomain, url_defaults, root_path, cli_commands)
class RouterDef(NetworkingDef):
VIEW = None
SCHEMA = schemas.Router
class SwitchDef(NetworkingDef):
VIEW = None
SCHEMA = schemas.Switch
class HubDef(NetworkingDef):
VIEW = None
SCHEMA = schemas.Hub
class WirelessAccessPointDef(NetworkingDef):
VIEW = None
SCHEMA = schemas.WirelessAccessPoint
class PrinterDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Printer
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
url_prefix, subdomain, url_defaults, root_path, cli_commands)
class LabelPrinterDef(PrinterDef):
VIEW = None
SCHEMA = schemas.LabelPrinter
class SoundDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Sound
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
url_prefix, subdomain, url_defaults, root_path, cli_commands)
class MicrophoneDef(SoundDef):
VIEW = None
SCHEMA = schemas.Microphone
class VideoDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Video
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
url_prefix, subdomain, url_defaults, root_path, cli_commands)
class VideoScalerDef(VideoDef):
VIEW = None
SCHEMA = schemas.VideoScaler
class VideoconferenceDef(VideoDef):
VIEW = None
SCHEMA = schemas.Videoconference
class ManufacturerDef(Resource): class ManufacturerDef(Resource):
VIEW = ManufacturerView VIEW = ManufacturerView
SCHEMA = schemas.Manufacturer SCHEMA = schemas.Manufacturer

View File

@ -17,12 +17,13 @@ from sqlalchemy_utils import ColorType
from stdnum import imei, meid from stdnum import imei, meid
from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, URL, check_lower, \ from teal.db import CASCADE, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, URL, check_lower, \
check_range check_range
from teal.enums import Layouts
from teal.marshmallow import ValidationError from teal.marshmallow import ValidationError
from teal.resource import url_for_resource from teal.resource import url_for_resource
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \ from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \
DataStoragePrivacyCompliance, DisplayTech, RamFormat, RamInterface DataStoragePrivacyCompliance, DisplayTech, PrinterTechnology, RamFormat, RamInterface
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
@ -62,6 +63,19 @@ class Device(Thing):
""" """
color = Column(ColorType) color = Column(ColorType)
color.comment = """The predominant color of the device.""" color.comment = """The predominant color of the device."""
production_date = Column(db.TIMESTAMP(timezone=True))
production_date.comment = """The date of production of the item."""
_NON_PHYSICAL_PROPS = {
'id',
'type',
'created',
'updated',
'parent_id',
'hid',
'production_date',
'color'
}
@property @property
def events(self) -> list: def events(self) -> list:
@ -94,7 +108,7 @@ class Device(Thing):
for c in inspect(self.__class__).attrs for c in inspect(self.__class__).attrs
if isinstance(c, ColumnProperty) if isinstance(c, ColumnProperty)
and not getattr(c, 'foreign_keys', None) and not getattr(c, 'foreign_keys', None)
and c.key not in {'id', 'type', 'created', 'updated', 'parent_id', 'hid'}} and c.key not in self._NON_PHYSICAL_PROPS}
@property @property
def url(self) -> urlutils.URL: def url(self) -> urlutils.URL:
@ -194,6 +208,11 @@ class Device(Thing):
class DisplayMixin: class DisplayMixin:
"""
Aspect ratio can be computed as in
https://github.com/mirukan/whratio/blob/master/whratio/ratio.py and
could be a future property.
"""
size = Column(Float(decimal_return_scale=2), check_range('size', 2, 150)) size = Column(Float(decimal_return_scale=2), check_range('size', 2, 150))
size.comment = """ size.comment = """
The size of the monitor in inches. The size of the monitor in inches.
@ -212,6 +231,10 @@ class DisplayMixin:
The maximum vertical resolution the monitor can natively support The maximum vertical resolution the monitor can natively support
in pixels. in pixels.
""" """
refresh_rate = Column(SmallInteger, check_range('refresh_rate', 10, 1000))
contrast_ratio = Column(SmallInteger, check_range('contrast_ratio', 100, 100000))
touchable = Column(Boolean, nullable=False, default=False)
touchable.comment = """Whether it is a touchscreen."""
def __format__(self, format_spec: str) -> str: def __format__(self, format_spec: str) -> str:
v = '' v = ''
@ -289,7 +312,9 @@ class Desktop(Computer):
class Laptop(Computer): class Laptop(Computer):
pass layout = Column(DBEnum(Layouts))
layout.comment = """Layout of a built-in keyboard of the computer,
if any."""
class Server(Computer): class Server(Computer):
@ -308,6 +333,10 @@ class TelevisionSet(Monitor):
pass pass
class Projector(Monitor):
pass
class Mobile(Device): class Mobile(Device):
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True) id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
imei = Column(BigInteger) imei = Column(BigInteger)
@ -483,6 +512,83 @@ class Display(JoinedComponentTableMixin, DisplayMixin, Component):
pass pass
class ComputerAccessory(Device):
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
pass
class SAI(ComputerAccessory):
pass
class Keyboard(ComputerAccessory):
layout = Column(DBEnum(Layouts)) # If we want to do it not null
class Mouse(ComputerAccessory):
pass
class MemoryCardReader(ComputerAccessory):
pass
class Networking(NetworkMixin, Device):
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
class Router(Networking):
pass
class Switch(Networking):
pass
class Hub(Networking):
pass
class WirelessAccessPoint(Networking):
pass
class Printer(Device):
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
wireless = Column(Boolean, nullable=False, default=False)
wireless.comment = """Whether it is a wireless printer."""
scanning = Column(Boolean, nullable=False, default=False)
scanning.comment = """Whether the printer has scanning capabilities."""
technology = Column(DBEnum(PrinterTechnology))
technology.comment = """Technology used to print."""
monochrome = Column(Boolean, nullable=False, default=True)
monochrome.comment = """Whether the printer is only monochrome."""
class LabelPrinter(Printer):
pass
class Sound(Device):
pass
class Microphone(Sound):
pass
class Video(Device):
pass
class VideoScaler(Video):
pass
class Videoconference(Video):
pass
class Manufacturer(db.Model): class Manufacturer(db.Model):
__table_args__ = {'schema': 'common'} __table_args__ = {'schema': 'common'}
CSV_DELIMITER = csv.get_dialect('excel').delimiter CSV_DELIMITER = csv.get_dialect('excel').delimiter

View File

@ -1,3 +1,4 @@
from datetime import datetime
from typing import Dict, List, Set, Type, Union from typing import Dict, List, Set, Type, Union
from boltons import urlutils from boltons import urlutils
@ -6,11 +7,12 @@ from colour import Color
from sqlalchemy import Column, Integer from sqlalchemy import Column, Integer
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from teal.db import Model from teal.db import Model
from teal.enums import Layouts
from ereuse_devicehub.resources.agent.models import Agent from ereuse_devicehub.resources.agent.models import Agent
from ereuse_devicehub.resources.device import states from ereuse_devicehub.resources.device import states
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \ from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \
DataStoragePrivacyCompliance, DisplayTech, RamFormat, RamInterface DataStoragePrivacyCompliance, DisplayTech, PrinterTechnology, RamFormat, RamInterface
from ereuse_devicehub.resources.event import models as e from ereuse_devicehub.resources.event import models as e
from ereuse_devicehub.resources.image.models import ImageList from ereuse_devicehub.resources.image.models import ImageList
from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.lot.models import Lot
@ -31,6 +33,7 @@ class Device(Thing):
depth = ... # type: Column depth = ... # type: Column
color = ... # type: Column color = ... # type: Column
lots = ... # type: relationship lots = ... # type: relationship
production_date = ... # type: Column
def __init__(self, **kwargs) -> None: def __init__(self, **kwargs) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)
@ -52,6 +55,7 @@ class Device(Thing):
self.images = ... # type: ImageList self.images = ... # type: ImageList
self.tags = ... # type: Set[Tag] self.tags = ... # type: Set[Tag]
self.lots = ... # type: Set[Lot] self.lots = ... # type: Set[Lot]
self.production_date = ... # type: datetime
@property @property
def url(self) -> urlutils.URL: def url(self) -> urlutils.URL:
@ -86,6 +90,9 @@ class DisplayMixin:
size = ... # type: Column size = ... # type: Column
resolution_width = ... # type: Column resolution_width = ... # type: Column
resolution_height = ... # type: Column resolution_height = ... # type: Column
refresh_rate = ... # type: Column
contrast_ratio = ... # type: Column
touchable = ... # type: Column
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
@ -93,6 +100,9 @@ class DisplayMixin:
self.size = ... # type: Integer self.size = ... # type: Integer
self.resolution_width = ... # type: int self.resolution_width = ... # type: int
self.resolution_height = ... # type: int self.resolution_height = ... # type: int
self.refresh_rate = ... # type: int
self.contrast_ratio = ... # type: int
self.touchable = ... # type: bool
class Computer(DisplayMixin, Device): class Computer(DisplayMixin, Device):
@ -135,7 +145,11 @@ class Desktop(Computer):
class Laptop(Computer): class Laptop(Computer):
pass layout = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.layout = ... # type: Layouts
class Server(Computer): class Server(Computer):
@ -233,12 +247,18 @@ class Motherboard(Component):
self.pcmcia = ... # type: int self.pcmcia = ... # type: int
class NetworkAdapter(Component): class NetworkMixin:
speed = ... # type: Column speed = ... # type: Column
wireless = ... # type: Column
def __init__(self, **kwargs) -> None: def __init__(self, **kwargs) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)
self.speed = ... # type: int self.speed = ... # type: int
self.wireless = ... # type: bool
class NetworkAdapter(NetworkMixin, Component):
pass
class Processor(Component): class Processor(Component):
@ -271,6 +291,88 @@ class Display(DisplayMixin, Component):
pass pass
class ComputerAccessory(Device):
pass
class SAI(ComputerAccessory):
pass
class Keyboard(ComputerAccessory):
layout = ... # type: Column
def __init__(self, layout: Layouts, **kwargs):
super().__init__(**kwargs)
self.layout = ... # type: Layouts
class Mouse(ComputerAccessory):
pass
class MemoryCardReader(ComputerAccessory):
pass
class Networking(NetworkMixin, Device):
pass
class Router(Networking):
pass
class Switch(Networking):
pass
class Hub(Networking):
pass
class WirelessAccessPoint(Networking):
pass
class Printer(Device):
wireless = ... # type: Column
scanning = ... # type: Column
technology = ... # type: Column
monochrome = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.wireless = ... # type: bool
self.scanning = ... # type: bool
self.technology = ... # type: PrinterTechnology
self.monochrome = ... # type: bool
class LabelPrinter(Printer):
pass
class Sound(Device):
pass
class Microphone(Sound):
pass
class Video(Device):
pass
class VideoScaler(Video):
pass
class Videoconference(Video):
pass
class Manufacturer(Model): class Manufacturer(Model):
CUSTOM_MANUFACTURERS = ... # type: set CUSTOM_MANUFACTURERS = ... # type: set
name = ... # type: Column name = ... # type: Column

View File

@ -1,15 +1,16 @@
from marshmallow import post_load, pre_load from marshmallow import post_load, pre_load
from marshmallow.fields import Boolean, Float, Integer, List, Str, String from marshmallow.fields import Boolean, DateTime, Float, Integer, List, Str, String
from marshmallow.validate import Length, OneOf, Range from marshmallow.validate import Length, OneOf, Range
from sqlalchemy.util import OrderedSet from sqlalchemy.util import OrderedSet
from stdnum import imei, meid from stdnum import imei, meid
from teal.enums import Layouts
from teal.marshmallow import EnumField, SanitizedStr, URL, ValidationError from teal.marshmallow import EnumField, SanitizedStr, URL, ValidationError
from teal.resource import Schema from teal.resource import Schema
from ereuse_devicehub.marshmallow import NestedOn from ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources.device import models as m, states from ereuse_devicehub.resources.device import models as m, states
from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \ from ereuse_devicehub.resources.enums import ComputerChassis, DataStorageInterface, \
DataStoragePrivacyCompliance, DisplayTech, RamFormat, RamInterface DataStoragePrivacyCompliance, DisplayTech, PrinterTechnology, RamFormat, RamInterface
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, UnitCodes from ereuse_devicehub.resources.schemas import Thing, UnitCodes
@ -40,6 +41,9 @@ class Device(Thing):
trading = EnumField(states.Trading, dump_only=True, description=m.Device.trading.__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 = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__)
physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor') physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor')
production_date = DateTime('iso',
description=m.Device.updated.comment,
data_key='productionDate')
@pre_load @pre_load
def from_events_to_events_one(self, data: dict): def from_events_to_events_one(self, data: dict):
@ -98,6 +102,9 @@ class DisplayMixin:
resolution_height = Integer(data_key='resolutionHeight', resolution_height = Integer(data_key='resolutionHeight',
validate=Range(10, 20000), validate=Range(10, 20000),
description=m.DisplayMixin.resolution_height.comment) 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(missing=False, description=m.DisplayMixin.touchable.comment)
class NetworkMixin: class NetworkMixin:
@ -212,3 +219,74 @@ class Manufacturer(Schema):
name = String(dump_only=True) name = String(dump_only=True)
url = URL(dump_only=True) url = URL(dump_only=True)
logo = URL(dump_only=True) logo = URL(dump_only=True)
class ComputerAccessory(Device):
pass
class Mouse(ComputerAccessory):
pass
class MemoryCardReader(ComputerAccessory):
pass
class SAI(ComputerAccessory):
pass
class Keyboard(ComputerAccessory):
layout = EnumField(Layouts)
class Networking(NetworkMixin, Device):
pass
class Router(Networking):
pass
class Switch(Networking):
pass
class Hub(Networking):
pass
class WirelessAccessPoint(Networking):
pass
class Printer(Device):
wireless = Boolean(required=True, missing=False)
scanning = Boolean(required=True, missing=False)
technology = EnumField(PrinterTechnology, required=True)
monochrome = Boolean(required=True, missing=True)
class LabelPrinter(Printer):
pass
class Sound(Device):
pass
class Microphone(Sound):
pass
class Video(Device):
pass
class VideoScaler(Video):
pass
class Videoconference(Video):
pass

View File

@ -276,3 +276,12 @@ class DataStoragePrivacyCompliance(Enum):
return cls.EraseSectors if not erasure.error else cls.EraseSectorsError return cls.EraseSectors if not erasure.error else cls.EraseSectorsError
else: else:
return cls.EraseBasic if not erasure.error else cls.EraseBasicError return cls.EraseBasic if not erasure.error else cls.EraseBasicError
class PrinterTechnology(Enum):
"""Technology of the printer."""
Toner = 'Toner / Laser'
Inkjet = 'Liquid inkjet'
SolidInk = 'Solid ink'
Dye = 'Dye-sublimation'
Thermal = 'Thermal'

View File

@ -1,5 +1,6 @@
from sqlalchemy import Column from datetime import datetime
from sqlalchemy import Column
from teal.db import Model from teal.db import Model
STR_SIZE = 64 STR_SIZE = 64
@ -13,3 +14,8 @@ class Thing(Model):
type = ... # type: str type = ... # type: str
updated = ... # type: Column updated = ... # type: Column
created = ... # type: Column created = ... # type: Column
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.updated = ... # type: datetime
self.created = ... # type: datetime

View File

@ -22,8 +22,8 @@ class UnitCodes(Enum):
class Thing(Schema): class Thing(Schema):
type = String(description='Only required when it is nested.') type = String(description='Only required when it is nested.')
same_as = List(URL(dump_only=True), dump_only=True, data_key='sameAs') same_as = List(URL(dump_only=True), dump_only=True, data_key='sameAs')
updated = DateTime('iso', dump_only=True, description=m.Thing.updated.comment.strip()) updated = DateTime('iso', dump_only=True, description=m.Thing.updated.comment)
created = DateTime('iso', dump_only=True, description=m.Thing.created.comment.strip()) created = DateTime('iso', dump_only=True, description=m.Thing.created.comment)
@post_load @post_load
def remove_type(self, data: dict): def remove_type(self, data: dict):

View File

@ -40,4 +40,4 @@ def test_api_docs(client: Client):
'scheme': 'basic', 'scheme': 'basic',
'name': 'Authorization' 'name': 'Authorization'
} }
assert 75 == len(docs['definitions']) assert 92 == len(docs['definitions'])

View File

@ -9,14 +9,14 @@ from ereuse_utils.test import ANY
from pytest import raises from pytest import raises
from sqlalchemy.util import OrderedSet from sqlalchemy.util import OrderedSet
from teal.db import ResourceNotFound from teal.db import ResourceNotFound
from teal.enums import Layouts
from ereuse_devicehub.client import Client, UserClient from ereuse_devicehub.client import Client, 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.agent.models import Person from ereuse_devicehub.resources.agent.models import Person
from ereuse_devicehub.resources.device import models as d
from ereuse_devicehub.resources.device.exceptions import NeedsId from ereuse_devicehub.resources.device.exceptions import NeedsId
from ereuse_devicehub.resources.device.models import Component, ComputerMonitor, DataStorage, \
Desktop, Device, GraphicCard, Laptop, 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
@ -34,43 +34,43 @@ def test_device_model():
""" """
Tests that the correctness of the device model and its relationships. Tests that the correctness of the device model and its relationships.
""" """
pc = Desktop(model='p1mo', pc = d.Desktop(model='p1mo',
manufacturer='p1ma', manufacturer='p1ma',
serial_number='p1s', serial_number='p1s',
chassis=ComputerChassis.Tower) chassis=ComputerChassis.Tower)
net = NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s') net = d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s')
graphic = GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500) graphic = d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500)
pc.components.add(net) pc.components.add(net)
pc.components.add(graphic) pc.components.add(graphic)
db.session.add(pc) db.session.add(pc)
db.session.commit() db.session.commit()
pc = Desktop.query.one() pc = d.Desktop.query.one()
assert pc.serial_number == 'p1s' assert pc.serial_number == 'p1s'
assert pc.components == OrderedSet([net, graphic]) assert pc.components == OrderedSet([net, graphic])
network_adapter = NetworkAdapter.query.one() network_adapter = d.NetworkAdapter.query.one()
assert network_adapter.parent == pc assert network_adapter.parent == pc
# Removing a component from pc doesn't delete the component # Removing a component from pc doesn't delete the component
pc.components.remove(net) pc.components.remove(net)
db.session.commit() db.session.commit()
pc = Device.query.first() # this is the same as querying for Desktop directly pc = d.Device.query.first() # this is the same as querying for d.Desktop directly
assert pc.components == {graphic} assert pc.components == {graphic}
network_adapter = NetworkAdapter.query.one() network_adapter = d.NetworkAdapter.query.one()
assert network_adapter not in pc.components assert network_adapter not in pc.components
assert network_adapter.parent is None assert network_adapter.parent is None
# Deleting the pc deletes everything # Deleting the pc deletes everything
gcard = GraphicCard.query.one() gcard = d.GraphicCard.query.one()
db.session.delete(pc) db.session.delete(pc)
db.session.flush() db.session.flush()
assert pc.id == 1 assert pc.id == 1
assert Desktop.query.first() is None assert d.Desktop.query.first() is None
db.session.commit() db.session.commit()
assert Desktop.query.first() is None assert d.Desktop.query.first() is None
assert network_adapter.id == 2 assert network_adapter.id == 2
assert NetworkAdapter.query.first() is not None, 'We removed the network adaptor' assert d.NetworkAdapter.query.first() is not None, 'We removed the network adaptor'
assert gcard.id == 3, 'We should still hold a reference to a zombie graphic card' assert gcard.id == 3, 'We should still hold a reference to a zombie graphic card'
assert GraphicCard.query.first() is None, 'We should have deleted it it was inside the pc' assert d.GraphicCard.query.first() is None, 'We should have deleted it it was inside the pc'
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
@ -78,19 +78,19 @@ def test_device_schema():
"""Ensures the user does not upload non-writable or extra fields.""" """Ensures the user does not upload non-writable or extra fields."""
device_s = DeviceS() device_s = DeviceS()
device_s.load({'serialNumber': 'foo1', 'model': 'foo', 'manufacturer': 'bar2'}) device_s.load({'serialNumber': 'foo1', 'model': 'foo', 'manufacturer': 'bar2'})
device_s.dump(Device(id=1)) device_s.dump(d.Device(id=1))
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_physical_properties(): def test_physical_properties():
c = Motherboard(slots=2, c = d.Motherboard(slots=2,
usb=3, usb=3,
serial_number='sn', serial_number='sn',
model='ml', model='ml',
manufacturer='mr', manufacturer='mr',
width=2.0, width=2.0,
color=Color()) color=Color())
pc = Desktop(chassis=ComputerChassis.Tower, pc = d.Desktop(chassis=ComputerChassis.Tower,
model='foo', model='foo',
manufacturer='bar', manufacturer='bar',
serial_number='foo-bar', serial_number='foo-bar',
@ -113,7 +113,6 @@ def test_physical_properties():
'weight': None, 'weight': None,
'height': None, 'height': None,
'width': 2.0, 'width': 2.0,
'color': Color(),
'depth': None 'depth': None
} }
assert pc.physical_properties == { assert pc.physical_properties == {
@ -124,7 +123,6 @@ def test_physical_properties():
'width': 1.4, 'width': 1.4,
'height': 2.1, 'height': 2.1,
'depth': None, 'depth': None,
'color': Color('LightSeaGreen'),
'chassis': ComputerChassis.Tower 'chassis': ComputerChassis.Tower
} }
@ -132,18 +130,18 @@ def test_physical_properties():
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_component_similar_one(): def test_component_similar_one():
snapshot = conftest.file('pc-components.db') snapshot = conftest.file('pc-components.db')
d = snapshot['device'] pc = snapshot['device']
snapshot['components'][0]['serial_number'] = snapshot['components'][1]['serial_number'] = None snapshot['components'][0]['serial_number'] = snapshot['components'][1]['serial_number'] = None
pc = Desktop(**d, components=OrderedSet(Component(**c) for c in snapshot['components'])) pc = d.Desktop(**pc, components=OrderedSet(d.Component(**c) for c in snapshot['components']))
component1, component2 = pc.components # type: Component component1, component2 = pc.components # type: d.Component
db.session.add(pc) db.session.add(pc)
db.session.flush() db.session.flush()
# Let's create a new component named 'A' similar to 1 # Let's create a new component named 'A' similar to 1
componentA = Component(model=component1.model, manufacturer=component1.manufacturer) componentA = d.Component(model=component1.model, manufacturer=component1.manufacturer)
similar_to_a = componentA.similar_one(pc, set()) similar_to_a = componentA.similar_one(pc, set())
assert similar_to_a == component1 assert similar_to_a == component1
# Component B does not have the same model # d.Component B does not have the same model
componentB = Component(model='nope', manufacturer=component1.manufacturer) componentB = d.Component(model='nope', manufacturer=component1.manufacturer)
with pytest.raises(ResourceNotFound): with pytest.raises(ResourceNotFound):
assert componentB.similar_one(pc, set()) assert componentB.similar_one(pc, set())
# If we blacklist component A we won't get anything # If we blacklist component A we won't get anything
@ -159,14 +157,14 @@ def test_add_remove():
# c4 is not with any pc # c4 is not with any pc
values = conftest.file('pc-components.db') values = conftest.file('pc-components.db')
pc = values['device'] pc = values['device']
c1, c2 = (Component(**c) for c in values['components']) c1, c2 = (d.Component(**c) for c in values['components'])
pc = Desktop(**pc, components=OrderedSet([c1, c2])) pc = d.Desktop(**pc, components=OrderedSet([c1, c2]))
db.session.add(pc) db.session.add(pc)
c3 = Component(serial_number='nc1') c3 = d.Component(serial_number='nc1')
pc2 = Desktop(serial_number='s2', pc2 = d.Desktop(serial_number='s2',
components=OrderedSet([c3]), components=OrderedSet([c3]),
chassis=ComputerChassis.Microtower) chassis=ComputerChassis.Microtower)
c4 = Component(serial_number='c4s') c4 = d.Component(serial_number='c4s')
db.session.add(pc2) db.session.add(pc2)
db.session.add(c4) db.session.add(c4)
db.session.commit() db.session.commit()
@ -189,12 +187,12 @@ def test_sync_run_components_empty():
remove all the components from the device. remove all the components from the device.
""" """
s = conftest.file('pc-components.db') s = conftest.file('pc-components.db')
pc = Desktop(**s['device'], components=OrderedSet(Component(**c) for c in s['components'])) pc = d.Desktop(**s['device'], components=OrderedSet(d.Component(**c) for c in s['components']))
db.session.add(pc) db.session.add(pc)
db.session.commit() db.session.commit()
# Create a new transient non-db synced object # Create a new transient non-db synced object
pc = Desktop(**s['device']) pc = d.Desktop(**s['device'])
db_pc, _ = Sync().run(pc, components=OrderedSet()) db_pc, _ = Sync().run(pc, components=OrderedSet())
assert not db_pc.components assert not db_pc.components
assert not pc.components assert not pc.components
@ -207,25 +205,25 @@ def test_sync_run_components_none():
keep all the components from the device. keep all the components from the device.
""" """
s = conftest.file('pc-components.db') s = conftest.file('pc-components.db')
pc = Desktop(**s['device'], components=OrderedSet(Component(**c) for c in s['components'])) pc = d.Desktop(**s['device'], components=OrderedSet(d.Component(**c) for c in s['components']))
db.session.add(pc) db.session.add(pc)
db.session.commit() db.session.commit()
# Create a new transient non-db synced object # Create a new transient non-db synced object
transient_pc = Desktop(**s['device']) transient_pc = d.Desktop(**s['device'])
db_pc, _ = Sync().run(transient_pc, components=None) db_pc, _ = Sync().run(transient_pc, components=None)
assert db_pc.components assert db_pc.components
assert db_pc.components == pc.components assert db_pc.components == pc.components
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_sync_execute_register_desktop_new_Desktop_no_tag(): def test_sync_execute_register_desktop_new_desktop_no_tag():
""" """
Syncs a new Desktop with HID and without a tag, creating it. Syncs a new d.Desktop with HID and without a tag, creating it.
:return: :return:
""" """
# Case 1: device does not exist on DB # Case 1: device does not exist on DB
pc = Desktop(**conftest.file('pc-components.db')['device']) pc = d.Desktop(**conftest.file('pc-components.db')['device'])
db_pc = Sync().execute_register(pc) db_pc = Sync().execute_register(pc)
assert pc.physical_properties == db_pc.physical_properties assert pc.physical_properties == db_pc.physical_properties
@ -233,13 +231,13 @@ def test_sync_execute_register_desktop_new_Desktop_no_tag():
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_sync_execute_register_desktop_existing_no_tag(): def test_sync_execute_register_desktop_existing_no_tag():
""" """
Syncs an existing Desktop with HID and without a tag. Syncs an existing d.Desktop with HID and without a tag.
""" """
pc = Desktop(**conftest.file('pc-components.db')['device']) pc = d.Desktop(**conftest.file('pc-components.db')['device'])
db.session.add(pc) db.session.add(pc)
db.session.commit() db.session.commit()
pc = Desktop( pc = d.Desktop(
**conftest.file('pc-components.db')['device']) # Create a new transient non-db object **conftest.file('pc-components.db')['device']) # Create a new transient non-db object
# 1: device exists on DB # 1: device exists on DB
db_pc = Sync().execute_register(pc) db_pc = Sync().execute_register(pc)
@ -249,11 +247,11 @@ def test_sync_execute_register_desktop_existing_no_tag():
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_sync_execute_register_desktop_no_hid_no_tag(): def test_sync_execute_register_desktop_no_hid_no_tag():
""" """
Syncs a Desktop without HID and no tag. Syncs a d.Desktop without HID and no tag.
This should fail as we don't have a way to identify it. This should fail as we don't have a way to identify it.
""" """
pc = Desktop(**conftest.file('pc-components.db')['device']) pc = d.Desktop(**conftest.file('pc-components.db')['device'])
# 1: device has no HID # 1: device has no HID
pc.hid = pc.model = None pc.hid = pc.model = None
with pytest.raises(NeedsId): with pytest.raises(NeedsId):
@ -263,7 +261,7 @@ def test_sync_execute_register_desktop_no_hid_no_tag():
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_sync_execute_register_desktop_tag_not_linked(): def test_sync_execute_register_desktop_tag_not_linked():
""" """
Syncs a new Desktop with HID and a non-linked tag. Syncs a new d.Desktop with HID and a non-linked tag.
It is OK if the tag was not linked, it will be linked in this process. It is OK if the tag was not linked, it will be linked in this process.
""" """
@ -272,24 +270,24 @@ def test_sync_execute_register_desktop_tag_not_linked():
db.session.commit() db.session.commit()
# Create a new transient non-db object # Create a new transient non-db object
pc = Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([Tag(id='foo')])) pc = d.Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([Tag(id='foo')]))
returned_pc = Sync().execute_register(pc) returned_pc = Sync().execute_register(pc)
assert returned_pc == pc assert returned_pc == pc
assert tag.device == pc, 'Tag has to be linked' assert tag.device == pc, 'Tag has to be linked'
assert Desktop.query.one() == pc, 'Desktop had to be set to db' assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str): def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
""" """
Validates registering a Desktop without HID and a non-linked tag. Validates registering a d.Desktop without HID and a non-linked tag.
In this case it is ok still, as the non-linked tag proves that In this case it is ok still, as the non-linked tag proves that
the Desktop was not existing before (otherwise the tag would the d.Desktop was not existing before (otherwise the tag would
be linked), and thus it creates a new Desktop. be linked), and thus it creates a new d.Desktop.
""" """
tag = Tag(id=tag_id) tag = Tag(id=tag_id)
pc = Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([tag])) pc = d.Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([tag]))
returned_pc = Sync().execute_register(pc) returned_pc = Sync().execute_register(pc)
db.session.commit() db.session.commit()
assert returned_pc == pc assert returned_pc == pc
@ -299,7 +297,7 @@ def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
# they have the same pk though # they have the same pk though
assert tag != db_tag, 'They are not the same tags though' assert tag != db_tag, 'They are not the same tags though'
assert db_tag.id == tag.id assert db_tag.id == tag.id
assert Desktop.query.one() == pc, 'Desktop had to be set to db' assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
@ -310,7 +308,7 @@ def test_sync_execute_register_tag_does_not_exist():
Tags have to be created before trying to link them through a Snapshot. Tags have to be created before trying to link them through a Snapshot.
""" """
pc = Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([Tag('foo')])) pc = d.Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([Tag('foo')]))
with raises(ResourceNotFound): with raises(ResourceNotFound):
Sync().execute_register(pc) Sync().execute_register(pc)
@ -323,11 +321,11 @@ def test_sync_execute_register_tag_linked_same_device():
(If it has HID it validates both HID and tag point at the same (If it has HID it validates both HID and tag point at the same
device, this his checked in ). device, this his checked in ).
""" """
orig_pc = Desktop(**conftest.file('pc-components.db')['device']) orig_pc = d.Desktop(**conftest.file('pc-components.db')['device'])
db.session.add(Tag(id='foo', device=orig_pc)) db.session.add(Tag(id='foo', device=orig_pc))
db.session.commit() db.session.commit()
pc = Desktop( pc = d.Desktop(
**conftest.file('pc-components.db')['device']) # Create a new transient non-db object **conftest.file('pc-components.db')['device']) # Create a new transient non-db object
pc.tags.add(Tag(id='foo')) pc.tags.add(Tag(id='foo'))
db_pc = Sync().execute_register(pc) db_pc = Sync().execute_register(pc)
@ -342,15 +340,15 @@ def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags():
Checks that sync raises an error if finds that at least two passed-in Checks that sync raises an error if finds that at least two passed-in
tags are not linked to the same device. tags are not linked to the same device.
""" """
pc1 = Desktop(**conftest.file('pc-components.db')['device']) pc1 = d.Desktop(**conftest.file('pc-components.db')['device'])
db.session.add(Tag(id='foo-1', device=pc1)) db.session.add(Tag(id='foo-1', device=pc1))
pc2 = Desktop(**conftest.file('pc-components.db')['device']) pc2 = d.Desktop(**conftest.file('pc-components.db')['device'])
pc2.serial_number = 'pc2-serial' pc2.serial_number = 'pc2-serial'
pc2.hid = Naming.hid(pc2.manufacturer, pc2.serial_number, pc2.model) pc2.hid = Naming.hid(pc2.manufacturer, pc2.serial_number, pc2.model)
db.session.add(Tag(id='foo-2', device=pc2)) db.session.add(Tag(id='foo-2', device=pc2))
db.session.commit() db.session.commit()
pc1 = Desktop( pc1 = d.Desktop(
**conftest.file('pc-components.db')['device']) # Create a new transient non-db object **conftest.file('pc-components.db')['device']) # Create a new transient non-db object
pc1.tags.add(Tag(id='foo-1')) pc1.tags.add(Tag(id='foo-1'))
pc1.tags.add(Tag(id='foo-2')) pc1.tags.add(Tag(id='foo-2'))
@ -366,15 +364,15 @@ def test_sync_execute_register_mismatch_between_tags_and_hid():
In this case we set HID -> pc1 but tag -> pc2 In this case we set HID -> pc1 but tag -> pc2
""" """
pc1 = Desktop(**conftest.file('pc-components.db')['device']) pc1 = d.Desktop(**conftest.file('pc-components.db')['device'])
db.session.add(Tag(id='foo-1', device=pc1)) db.session.add(Tag(id='foo-1', device=pc1))
pc2 = Desktop(**conftest.file('pc-components.db')['device']) pc2 = d.Desktop(**conftest.file('pc-components.db')['device'])
pc2.serial_number = 'pc2-serial' pc2.serial_number = 'pc2-serial'
pc2.hid = Naming.hid(pc2.manufacturer, pc2.serial_number, pc2.model) pc2.hid = Naming.hid(pc2.manufacturer, pc2.serial_number, pc2.model)
db.session.add(Tag(id='foo-2', device=pc2)) db.session.add(Tag(id='foo-2', device=pc2))
db.session.commit() db.session.commit()
pc1 = Desktop( pc1 = d.Desktop(
**conftest.file('pc-components.db')['device']) # Create a new transient non-db object **conftest.file('pc-components.db')['device']) # Create a new transient non-db object
pc1.tags.add(Tag(id='foo-2')) pc1.tags.add(Tag(id='foo-2'))
with raises(MismatchBetweenTagsAndHid): with raises(MismatchBetweenTagsAndHid):
@ -382,15 +380,15 @@ def test_sync_execute_register_mismatch_between_tags_and_hid():
def test_get_device(app: Devicehub, user: UserClient): def test_get_device(app: Devicehub, user: UserClient):
"""Checks GETting a Desktop with its components.""" """Checks GETting a d.Desktop with its components."""
with app.app_context(): with app.app_context():
pc = Desktop(model='p1mo', pc = d.Desktop(model='p1mo',
manufacturer='p1ma', manufacturer='p1ma',
serial_number='p1s', serial_number='p1s',
chassis=ComputerChassis.Tower) chassis=ComputerChassis.Tower)
pc.components = OrderedSet([ pc.components = OrderedSet([
NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s'), d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s'),
GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500) d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500)
]) ])
db.session.add(pc) db.session.add(pc)
db.session.add(Test(device=pc, db.session.add(Test(device=pc,
@ -399,7 +397,7 @@ def test_get_device(app: Devicehub, user: UserClient):
agent=Person(name='Timmy'), agent=Person(name='Timmy'),
author=User(email='bar@bar.com'))) author=User(email='bar@bar.com')))
db.session.commit() db.session.commit()
pc, _ = user.get(res=Device, item=1) pc, _ = user.get(res=d.Device, item=1)
assert len(pc['events']) == 1 assert len(pc['events']) == 1
assert pc['events'][0]['type'] == 'Test' assert pc['events'][0]['type'] == 'Test'
assert pc['events'][0]['device'] == 1 assert pc['events'][0]['device'] == 1
@ -414,40 +412,40 @@ def test_get_device(app: Devicehub, user: UserClient):
assert pc['model'] == 'p1mo' assert pc['model'] == 'p1mo'
assert pc['manufacturer'] == 'p1ma' assert pc['manufacturer'] == 'p1ma'
assert pc['serialNumber'] == 'p1s' assert pc['serialNumber'] == 'p1s'
assert pc['type'] == 'Desktop' assert pc['type'] == d.Desktop.t
def test_get_devices(app: Devicehub, user: UserClient): def test_get_devices(app: Devicehub, user: UserClient):
"""Checks GETting multiple devices.""" """Checks GETting multiple devices."""
with app.app_context(): with app.app_context():
pc = Desktop(model='p1mo', pc = d.Desktop(model='p1mo',
manufacturer='p1ma', manufacturer='p1ma',
serial_number='p1s', serial_number='p1s',
chassis=ComputerChassis.Tower) chassis=ComputerChassis.Tower)
pc.components = OrderedSet([ pc.components = OrderedSet([
NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s'), d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s'),
GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500) d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500)
]) ])
pc1 = Desktop(model='p2mo', pc1 = d.Desktop(model='p2mo',
manufacturer='p2ma', manufacturer='p2ma',
serial_number='p2s', serial_number='p2s',
chassis=ComputerChassis.Tower) chassis=ComputerChassis.Tower)
pc2 = Laptop(model='p3mo', pc2 = d.Laptop(model='p3mo',
manufacturer='p3ma', manufacturer='p3ma',
serial_number='p3s', serial_number='p3s',
chassis=ComputerChassis.Netbook) chassis=ComputerChassis.Netbook)
db.session.add_all((pc, pc1, pc2)) db.session.add_all((pc, pc1, pc2))
db.session.commit() db.session.commit()
devices, _ = user.get(res=Device) devices, _ = user.get(res=d.Device)
assert tuple(d['id'] for d in devices['items']) == (1, 2, 3, 4, 5) assert tuple(dev['id'] for dev in devices['items']) == (1, 2, 3, 4, 5)
assert tuple(d['type'] for d in devices['items']) == ( assert tuple(dev['type'] for dev in devices['items']) == (
'Desktop', 'Desktop', 'Laptop', 'NetworkAdapter', 'GraphicCard' d.Desktop.t, d.Desktop.t, d.Laptop.t, d.NetworkAdapter.t, d.GraphicCard.t
) )
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_computer_monitor(): def test_computer_monitor():
m = ComputerMonitor(technology=DisplayTech.LCD, m = d.ComputerMonitor(technology=DisplayTech.LCD,
manufacturer='foo', manufacturer='foo',
model='bar', model='bar',
serial_number='foo-bar', serial_number='foo-bar',
@ -489,7 +487,7 @@ def test_manufacturer_enforced():
def test_device_properties_format(app: Devicehub, user: UserClient): def test_device_properties_format(app: Devicehub, user: UserClient):
user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot) user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
with app.app_context(): with app.app_context():
pc = Laptop.query.one() # type: Laptop pc = d.Laptop.query.one() # type: d.Laptop
assert format(pc) == 'Laptop 1: model 1000h, S/N 94oaaq021116' assert format(pc) == 'Laptop 1: model 1000h, S/N 94oaaq021116'
assert format(pc, 't') == 'Netbook 1000h' assert format(pc, 't') == 'Netbook 1000h'
assert format(pc, 's') == '(asustek computer inc.) S/N 94OAAQ021116' assert format(pc, 's') == '(asustek computer inc.) S/N 94OAAQ021116'
@ -497,12 +495,12 @@ def test_device_properties_format(app: Devicehub, user: UserClient):
assert pc.data_storage_size == 152627 assert pc.data_storage_size == 152627
assert pc.graphic_card_model == 'mobile 945gse express integrated graphics controller' assert pc.graphic_card_model == 'mobile 945gse express integrated graphics controller'
assert pc.processor_model == 'intel atom cpu n270 @ 1.60ghz' assert pc.processor_model == 'intel atom cpu n270 @ 1.60ghz'
net = next(c for c in pc.components if isinstance(c, NetworkAdapter)) net = next(c for c in pc.components if isinstance(c, d.NetworkAdapter))
assert format(net) == 'NetworkAdapter 2: model ar8121/ar8113/ar8114 ' \ assert format(net) == 'NetworkAdapter 2: model ar8121/ar8113/ar8114 ' \
'gigabit or fast ethernet, S/N 00:24:8c:7f:cf:2d' 'gigabit or fast ethernet, S/N 00:24:8c:7f:cf:2d'
assert format(net, 't') == 'NetworkAdapter ar8121/ar8113/ar8114 gigabit or fast ethernet' assert format(net, 't') == 'NetworkAdapter ar8121/ar8113/ar8114 gigabit or fast ethernet'
assert format(net, 's') == '(qualcomm atheros) S/N 00:24:8C:7F:CF:2D 100 Mbps' assert format(net, 's') == '(qualcomm atheros) S/N 00:24:8C:7F:CF:2D 100 Mbps'
hdd = next(c for c in pc.components if isinstance(c, DataStorage)) hdd = next(c for c in pc.components if isinstance(c, d.DataStorage))
assert format(hdd) == 'HardDrive 7: model st9160310as, S/N 5sv4tqa6' assert format(hdd) == 'HardDrive 7: model st9160310as, S/N 5sv4tqa6'
assert format(hdd, 't') == 'HardDrive st9160310as' assert format(hdd, 't') == 'HardDrive st9160310as'
assert format(hdd, 's') == '(seagate) S/N 5SV4TQA6 152 GB' assert format(hdd, 's') == '(seagate) S/N 5SV4TQA6 152 GB'
@ -510,6 +508,26 @@ def test_device_properties_format(app: Devicehub, user: UserClient):
def test_device_public(user: UserClient, client: Client): def test_device_public(user: UserClient, client: Client):
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot) s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
html, _ = client.get(res=Device, item=s['device']['id'], accept=ANY) html, _ = client.get(res=d.Device, item=s['device']['id'], accept=ANY)
assert 'intel atom cpu n270 @ 1.60ghz' in html assert 'intel atom cpu n270 @ 1.60ghz' in html
assert 'S/N 00:24:8C:7F:CF:2D 100 Mbps' in html assert 'S/N 00:24:8C:7F:CF:2D 100 Mbps' in html
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_computer_accessory_model():
sai = d.SAI()
db.session.add(sai)
keyboard = d.Keyboard(layout=Layouts.ES)
db.session.add(keyboard)
mouse = d.Mouse()
db.session.add(mouse)
db.session.commit()
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_networking_model():
router = d.Router(speed=1000, wireless=True)
db.session.add(router)
switch = d.Switch(speed=1000, wireless=False)
db.session.add(switch)
db.session.commit()

View File

@ -411,3 +411,10 @@ def snapshot_and_check(user: UserClient,
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:
return snapshot return snapshot
def test_snapshot_keyboard(user: UserClient):
s = file('keyboard.snapshot')
snapshot = snapshot_and_check(user, s, event_types=('ManualRate',))
keyboard = snapshot['device']
assert keyboard['layout'] == 'ES'