Add ComputerAccessory, Networking, Printer, Sound, Video devices
This commit is contained in:
parent
46f765a683
commit
10c73a4e75
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
|
@ -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,26 +78,26 @@ 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',
|
||||||
weight=2.8,
|
weight=2.8,
|
||||||
width=1.4,
|
width=1.4,
|
||||||
height=2.1,
|
height=2.1,
|
||||||
color=Color('LightSeaGreen'))
|
color=Color('LightSeaGreen'))
|
||||||
pc.components.add(c)
|
pc.components.add(c)
|
||||||
db.session.add(pc)
|
db.session.add(pc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -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,46 +412,46 @@ 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',
|
||||||
resolution_width=1920,
|
resolution_width=1920,
|
||||||
resolution_height=1080,
|
resolution_height=1080,
|
||||||
size=14.5)
|
size=14.5)
|
||||||
db.session.add(m)
|
db.session.add(m)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
|
@ -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'
|
||||||
|
|
Reference in New Issue