Merge branch 'testing' into bugfix/4122-snapshots-log-type-upload

This commit is contained in:
Cayo Puigdefabregas 2022-11-29 18:10:36 +01:00
commit d2a4c0ef83
7 changed files with 512 additions and 89 deletions

View File

@ -50,11 +50,15 @@ from ereuse_devicehub.resources.device.models import (
Keyboard, Keyboard,
Laptop, Laptop,
MemoryCardReader, MemoryCardReader,
Monitor,
Mouse, Mouse,
Other,
Placeholder, Placeholder,
Projector,
Server, Server,
Smartphone, Smartphone,
Tablet, Tablet,
TelevisionSet,
) )
from ereuse_devicehub.resources.documents.models import DataWipeDocument from ereuse_devicehub.resources.documents.models import DataWipeDocument
from ereuse_devicehub.resources.enums import Severity from ereuse_devicehub.resources.enums import Severity
@ -81,7 +85,6 @@ DEVICES = {
], ],
"Mobile, tablet & smartphone": [ "Mobile, tablet & smartphone": [
"All Mobile", "All Mobile",
"Mobile",
"Tablet", "Tablet",
"Smartphone", "Smartphone",
"Cellphone", "Cellphone",
@ -91,6 +94,14 @@ DEVICES = {
"HardDrive", "HardDrive",
"SolidStageDrive", "SolidStageDrive",
], ],
"Accessories": [
"All Accessories",
"Mouse",
"MemoryCardReader",
"SAI",
"Keyboard",
],
"Other Devices": ["Other"],
} }
COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer'] COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer']
@ -98,6 +109,8 @@ COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer']
MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"] MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"]
MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"] MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"]
STORAGE = ["HardDrive", "SolidStateDrive"] STORAGE = ["HardDrive", "SolidStateDrive"]
ACCESSORIES = ["Mouse", "MemoryCardReader", "SAI", "Keyboard"]
OTHERS = ["Other"]
class AdvancedSearchForm(FlaskForm): class AdvancedSearchForm(FlaskForm):
@ -170,7 +183,7 @@ class FilterForm(FlaskForm):
# Generic Filters # Generic Filters
if "All Devices" == self.device_type: if "All Devices" == self.device_type:
filter_type = COMPUTERS + MONITORS + MOBILE filter_type = COMPUTERS + MONITORS + MOBILE + OTHERS
elif "All Computers" == self.device_type: elif "All Computers" == self.device_type:
filter_type = COMPUTERS filter_type = COMPUTERS
@ -184,6 +197,9 @@ class FilterForm(FlaskForm):
elif "All DataStorage" == self.device_type: elif "All DataStorage" == self.device_type:
filter_type = STORAGE filter_type = STORAGE
elif "All Accessories" == self.device_type:
filter_type = ACCESSORIES
if filter_type: if filter_type:
self.devices = self.devices.filter(Device.type.in_(filter_type)) self.devices = self.devices.filter(Device.type.in_(filter_type))
@ -371,10 +387,14 @@ class NewDeviceForm(FlaskForm):
"Tablet": Tablet, "Tablet": Tablet,
"Cellphone": Cellphone, "Cellphone": Cellphone,
"ComputerMonitor": ComputerMonitor, "ComputerMonitor": ComputerMonitor,
"Monitor": Monitor,
"TelevisionSet": TelevisionSet,
"Projector": Projector,
"Mouse": Mouse, "Mouse": Mouse,
"Keyboard": Keyboard, "Keyboard": Keyboard,
"SAI": SAI, "SAI": SAI,
"MemoryCardReader": MemoryCardReader, "MemoryCardReader": MemoryCardReader,
"Other": Other,
} }
def reset_from_obj(self): def reset_from_obj(self):

View File

@ -0,0 +1,39 @@
"""device other
Revision ID: 410aadae7652
Revises: d65745749e34
Create Date: 2022-11-29 12:00:40.272121
"""
import sqlalchemy as sa
from alembic import context, op
# revision identifiers, used by Alembic.
revision = '410aadae7652'
down_revision = 'd65745749e34'
branch_labels = None
depends_on = None
def get_inv():
INV = context.get_x_argument(as_dictionary=True).get('inventory')
if not INV:
raise ValueError("Inventory value is not specified")
return INV
def upgrade():
op.create_table(
'other',
sa.Column('id', sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(
['id'],
[f'{get_inv()}.device.id'],
),
sa.PrimaryKeyConstraint('id'),
schema=f'{get_inv()}',
)
def downgrade():
op.drop_table('other', schema=f'{get_inv()}')

View File

@ -4,7 +4,11 @@ from teal.resource import Converters, Resource
from ereuse_devicehub.resources.device import schemas from ereuse_devicehub.resources.device import schemas
from ereuse_devicehub.resources.device.models import Manufacturer from ereuse_devicehub.resources.device.models import Manufacturer
from ereuse_devicehub.resources.device.views import DeviceView, DeviceMergeView, ManufacturerView from ereuse_devicehub.resources.device.views import (
DeviceMergeView,
DeviceView,
ManufacturerView,
)
class DeviceDef(Resource): class DeviceDef(Resource):
@ -13,25 +17,42 @@ class DeviceDef(Resource):
ID_CONVERTER = Converters.string ID_CONVERTER = Converters.string
AUTH = False # We manage this at each view AUTH = False # We manage this at each view
def __init__(self, app, def __init__(
import_name=__name__, self,
static_folder='static', app,
static_url_path=None, import_name=__name__,
template_folder='templates', static_folder='static',
url_prefix=None, static_url_path=None,
subdomain=None, template_folder='templates',
url_defaults=None, url_prefix=None,
root_path=None, subdomain=None,
cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): url_defaults=None,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, root_path=None,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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,
)
device_merge = DeviceMergeView.as_view('merge-devices', definition=self, auth=app.auth) device_merge = DeviceMergeView.as_view(
'merge-devices', definition=self, auth=app.auth
)
if self.AUTH: if self.AUTH:
device_merge = app.auth.requires_auth(device_merge) device_merge = app.auth.requires_auth(device_merge)
path = '/<{value}:dev1_id>/merge/<{value}:dev2_id>'.format(value=self.ID_CONVERTER.value) path = '/<{value}:dev1_id>/merge/<{value}:dev2_id>'.format(
value=self.ID_CONVERTER.value
)
# self.add_url_rule(path, view_func=device_merge, methods={'POST'}) # self.add_url_rule(path, view_func=device_merge, methods={'POST'})
@ -40,11 +61,31 @@ class ComputerDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Computer SCHEMA = schemas.Computer
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 DesktopDef(ComputerDef): class DesktopDef(ComputerDef):
@ -66,11 +107,31 @@ class MonitorDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Monitor SCHEMA = schemas.Monitor
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 ComputerMonitorDef(MonitorDef): class ComputerMonitorDef(MonitorDef):
@ -83,15 +144,40 @@ class TelevisionSetDef(MonitorDef):
SCHEMA = schemas.TelevisionSet SCHEMA = schemas.TelevisionSet
class ProjectorDef(MonitorDef):
VIEW = None
SCHEMA = schemas.Projector
class MobileDef(DeviceDef): class MobileDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Mobile SCHEMA = schemas.Mobile
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 SmartphoneDef(MobileDef): class SmartphoneDef(MobileDef):
@ -113,11 +199,31 @@ class ComponentDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Component SCHEMA = schemas.Component
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 GraphicCardDef(ComponentDef): class GraphicCardDef(ComponentDef):
@ -184,11 +290,31 @@ class ComputerAccessoryDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.ComputerAccessory SCHEMA = schemas.ComputerAccessory
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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): class MouseDef(ComputerAccessoryDef):
@ -215,11 +341,31 @@ class NetworkingDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Networking SCHEMA = schemas.Networking
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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): class RouterDef(NetworkingDef):
@ -246,11 +392,31 @@ class PrinterDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Printer SCHEMA = schemas.Printer
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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): class LabelPrinterDef(PrinterDef):
@ -262,11 +428,31 @@ class SoundDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Sound SCHEMA = schemas.Sound
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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): class MicrophoneDef(SoundDef):
@ -278,11 +464,31 @@ class VideoDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Video SCHEMA = schemas.Video
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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): class VideoScalerDef(VideoDef):
@ -299,11 +505,31 @@ class CookingDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Cooking SCHEMA = schemas.Cooking
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 Mixer(CookingDef): class Mixer(CookingDef):
@ -315,11 +541,31 @@ class DIYAndGardeningDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.DIYAndGardening SCHEMA = schemas.DIYAndGardening
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 DrillDef(DIYAndGardeningDef): class DrillDef(DIYAndGardeningDef):
@ -331,22 +577,62 @@ class PackOfScrewdriversDef(DIYAndGardeningDef):
VIEW = None VIEW = None
SCHEMA = schemas.PackOfScrewdrivers SCHEMA = schemas.PackOfScrewdrivers
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 HomeDef(DeviceDef): class HomeDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Home SCHEMA = schemas.Home
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 DehumidifierDef(HomeDef): class DehumidifierDef(HomeDef):
@ -363,11 +649,31 @@ class RecreationDef(DeviceDef):
VIEW = None VIEW = None
SCHEMA = schemas.Recreation SCHEMA = schemas.Recreation
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, def __init__(
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, self,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): app,
super().__init__(app, import_name, static_folder, static_url_path, template_folder, import_name=__name__,
url_prefix, subdomain, url_defaults, root_path, cli_commands) 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 BikeDef(RecreationDef): class BikeDef(RecreationDef):
@ -389,3 +695,35 @@ class ManufacturerDef(Resource):
"""Loads the manufacturers to the database.""" """Loads the manufacturers to the database."""
if exclude_schema != 'common': if exclude_schema != 'common':
Manufacturer.add_all_to_session(db.session) Manufacturer.add_all_to_session(db.session)
class OtherDef(DeviceDef):
VIEW = None
SCHEMA = schemas.Computer
SCHEMA = schemas.Other
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,
)

View File

@ -1637,3 +1637,11 @@ def create_code_tag(mapper, connection, device):
# from flask_sqlalchemy import event # from flask_sqlalchemy import event
# event.listen(Device, 'after_insert', create_code_tag, propagate=True) # event.listen(Device, 'after_insert', create_code_tag, propagate=True)
class Other(Device):
"""
Used for put in there all devices than not have actualy a class
"""
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)

View File

@ -269,6 +269,10 @@ class TelevisionSet(Monitor):
__doc__ = m.TelevisionSet.__doc__ __doc__ = m.TelevisionSet.__doc__
class Projector(Monitor):
__doc__ = m.Projector.__doc__
class Mobile(Device): class Mobile(Device):
__doc__ = m.Mobile.__doc__ __doc__ = m.Mobile.__doc__
@ -577,3 +581,7 @@ class Bike(Recreation):
class Racket(Recreation): class Racket(Recreation):
pass pass
class Other(Device):
pass

View File

@ -46,7 +46,13 @@
</optgroup> </optgroup>
<optgroup label="Monitor"> <optgroup label="Monitor">
<option value="ComputerMonitor" <option value="ComputerMonitor"
{% if form.type.data == 'Monitor' %} selected="selected"{% endif %}>Computer Monitor</option> {% if form.type.data == 'ComputerMonitor' %} selected="selected"{% endif %}>Computer Monitor</option>
<option value="Monitor"
{% if form.type.data == 'Monitor' %} selected="selected"{% endif %}>Monitor</option>
<option value="TelevisionSet"
{% if form.type.data == 'TelevisionSet' %} selected="selected"{% endif %}>TelevisionSet</option>
<option value="Projector"
{% if form.type.data == 'Projector' %} selected="selected"{% endif %}>Projector</option>
</optgroup> </optgroup>
<optgroup label="Mobile"> <optgroup label="Mobile">
<option value="Smartphone" <option value="Smartphone"
@ -66,6 +72,10 @@
<option value="Keyboard" <option value="Keyboard"
{% if form.type.data == 'Keyboard' %} selected="selected"{% endif %}>Keyboard</option> {% if form.type.data == 'Keyboard' %} selected="selected"{% endif %}>Keyboard</option>
</optgroup> </optgroup>
<optgroup label="Other Type of Device">
<option value="Other"
{% if form.type.data == 'Other' %} selected="selected"{% endif %}>Other</option>
</optgroup>
</select> </select>
<small class="text-muted form-text">Type of devices</small> <small class="text-muted form-text">Type of devices</small>
{% if form.type.errors %} {% if form.type.errors %}

View File

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