Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
6dc033281d | |||
5bcf3b2a63 | |||
fa55ac017d | |||
e2c04e3e48 | |||
6bf91d3362 | |||
4b7bf24d86 | |||
3a1f135310 | |||
1941f28ab3 | |||
25fd9c3b6f | |||
07e506f999 | |||
72e4481b5d | |||
9209e3cfaa | |||
3408af33a8 | |||
d5e93f3a52 | |||
b405f715e0 | |||
c79cfd05fa | |||
ae310ece6d |
|
@ -7,6 +7,12 @@ ml).
|
|||
|
||||
## testing
|
||||
|
||||
## [2.5.4] - 2023-08-7
|
||||
- [added] #460 new device Solar Panel
|
||||
- [fixed] #459 Deploy without Dummy
|
||||
- [fixed] #461 Fix some behavior of orphans components (DataStorage)
|
||||
- [fixed] #462 Remove constraints in weight in placeholder
|
||||
|
||||
## [2.5.3] - 2023-05-13
|
||||
- [added] #450 add new datawipe in csv.
|
||||
- [changed] #447 Share a lot between 2 users, one is owner the other is read only.
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "2.5.3"
|
||||
__version__ = "2.5.4"
|
||||
|
|
|
@ -41,7 +41,7 @@ from ereuse_devicehub.inventory.models import (
|
|||
from ereuse_devicehub.parser.models import PlaceholdersLog, SnapshotsLog
|
||||
from ereuse_devicehub.parser.parser import ParseSnapshotLsHw
|
||||
from ereuse_devicehub.parser.schemas import Snapshot_lite
|
||||
from ereuse_devicehub.resources.action.models import Snapshot, Trade
|
||||
from ereuse_devicehub.resources.action.models import Snapshot, Trade, VisualTest
|
||||
from ereuse_devicehub.resources.action.schemas import Snapshot as SnapshotSchema
|
||||
from ereuse_devicehub.resources.action.views.snapshot import (
|
||||
SnapshotMixin,
|
||||
|
@ -68,6 +68,7 @@ from ereuse_devicehub.resources.device.models import (
|
|||
Projector,
|
||||
Server,
|
||||
Smartphone,
|
||||
SolarPanel,
|
||||
SolidStateDrive,
|
||||
Tablet,
|
||||
TelevisionSet,
|
||||
|
@ -113,7 +114,7 @@ DEVICES = {
|
|||
"SAI",
|
||||
"Keyboard",
|
||||
],
|
||||
"Other Devices": ["Other"],
|
||||
"Other Devices": ["SolarPanel", "Other"],
|
||||
}
|
||||
|
||||
TYPES_DOCUMENTS = [
|
||||
|
@ -131,7 +132,7 @@ MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"]
|
|||
MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"]
|
||||
STORAGE = ["HardDrive", "SolidStateDrive"]
|
||||
ACCESSORIES = ["Mouse", "MemoryCardReader", "SAI", "Keyboard"]
|
||||
OTHERS = ["Other"]
|
||||
OTHERS = ["Other", "SolarPanel"]
|
||||
DATASTORAGE = ['HardDrive', 'SolidStateDrive']
|
||||
|
||||
|
||||
|
@ -433,6 +434,7 @@ class NewDeviceForm(FlaskForm):
|
|||
"Keyboard": Keyboard,
|
||||
"SAI": SAI,
|
||||
"MemoryCardReader": MemoryCardReader,
|
||||
"SolarPanel": SolarPanel,
|
||||
"Other": Other,
|
||||
}
|
||||
|
||||
|
@ -480,6 +482,10 @@ class NewDeviceForm(FlaskForm):
|
|||
if self._obj.type in ['HardDrive', 'SolidStateDrive']:
|
||||
if self._obj.size:
|
||||
self.data_storage_size.data = self._obj.size / 1000
|
||||
if self._obj.appearance():
|
||||
self.appearance.data = self._obj.appearance().name
|
||||
if self._obj.functionality():
|
||||
self.functionality.data = self._obj.functionality().name
|
||||
|
||||
if self._obj.placeholder.is_abstract:
|
||||
self.type.render_kw = disabled
|
||||
|
@ -515,23 +521,23 @@ class NewDeviceForm(FlaskForm):
|
|||
error = ["Not a correct value"]
|
||||
is_valid = super().validate(extra_validators)
|
||||
|
||||
if self.weight.data and not (0.1 <= self.weight.data <= 5):
|
||||
txt = ["Supported values between 0.1 and 5"]
|
||||
if self.weight.data and not (0.1 <= self.weight.data) or self.weight.data == 0:
|
||||
txt = ["Supported values greater than 0.1"]
|
||||
self.weight.errors = txt
|
||||
is_valid = False
|
||||
|
||||
if self.height.data and not (0.1 <= self.height.data <= 5):
|
||||
txt = ["Supported values between 0.1 and 5"]
|
||||
if self.height.data and not (0.1 <= self.height.data) or self.height.data == 0:
|
||||
txt = ["Supported values greater than 0.1"]
|
||||
self.height.errors = txt
|
||||
is_valid = False
|
||||
|
||||
if self.width.data and not (0.1 <= self.width.data <= 5):
|
||||
txt = ["Supported values between 0.1 and 5"]
|
||||
if self.width.data and not (0.1 <= self.width.data) or self.width.data == 0:
|
||||
txt = ["Supported values greater than 0.1"]
|
||||
self.width.errors = txt
|
||||
is_valid = False
|
||||
|
||||
if self.depth.data and not (0.1 <= self.depth.data <= 5):
|
||||
txt = ["Supported values between 0.1 and 5"]
|
||||
if self.depth.data and not (0.1 <= self.depth.data) or self.depth.data == 0:
|
||||
txt = ["Supported values greater than 0.1"]
|
||||
self.depth.errors = txt
|
||||
is_valid = False
|
||||
|
||||
|
@ -698,17 +704,7 @@ class NewDeviceForm(FlaskForm):
|
|||
if self.data_storage_size.data:
|
||||
self._obj.size = self.data_storage_size.data * 1000
|
||||
|
||||
if (
|
||||
self.appearance.data
|
||||
and self.appearance.data != self._obj.appearance().name
|
||||
):
|
||||
self._obj.set_appearance(self.appearance.data)
|
||||
|
||||
if (
|
||||
self.functionality.data
|
||||
and self.functionality.data != self._obj.functionality().name
|
||||
):
|
||||
self._obj.set_functionality(self.functionality.data)
|
||||
self.edit_visual_test(self._obj)
|
||||
|
||||
else:
|
||||
self._obj.placeholder.id_device_supplier = (
|
||||
|
@ -718,11 +714,33 @@ class NewDeviceForm(FlaskForm):
|
|||
self.id_device_internal.data or None
|
||||
)
|
||||
self._obj.placeholder.pallet = self.pallet.data or None
|
||||
|
||||
pl_dev = self._obj.placeholder.device
|
||||
self.edit_visual_test(pl_dev)
|
||||
|
||||
placeholder_log = PlaceholdersLog(
|
||||
type="Update", source='Web form', placeholder=self._obj.placeholder
|
||||
)
|
||||
db.session.add(placeholder_log)
|
||||
|
||||
def edit_visual_test(self, dev):
|
||||
if not dev.appearance() or not dev.functionality():
|
||||
visual_test = VisualTest(
|
||||
appearance_range=self.appearance.data,
|
||||
functionality_range=self.functionality.data,
|
||||
device=dev,
|
||||
)
|
||||
db.session.add(visual_test)
|
||||
else:
|
||||
if self.appearance.data and self.appearance.data != dev.appearance().name:
|
||||
dev.set_appearance(self.appearance.data)
|
||||
|
||||
if (
|
||||
self.functionality.data
|
||||
and self.functionality.data != dev.functionality().name
|
||||
):
|
||||
dev.set_functionality(self.functionality.data)
|
||||
|
||||
|
||||
class TagDeviceForm(FlaskForm):
|
||||
tag = SelectField(
|
||||
|
@ -1211,7 +1229,6 @@ class TradeForm(ActionFormMixin):
|
|||
or email_to == email_from
|
||||
or g.user.email not in [email_from, email_to]
|
||||
):
|
||||
|
||||
errors = ["If you want confirm, you need a correct email"]
|
||||
self.user_to.errors = errors
|
||||
self.user_from.errors = errors
|
||||
|
@ -1917,7 +1934,6 @@ class UploadPlaceholderForm(FlaskForm):
|
|||
return True
|
||||
|
||||
def save(self, commit=True):
|
||||
|
||||
for device, placeholder_log in self.placeholders:
|
||||
db.session.add(device)
|
||||
db.session.add(placeholder_log)
|
||||
|
@ -1946,7 +1962,6 @@ class EditPlaceholderForm(FlaskForm):
|
|||
return True
|
||||
|
||||
def save(self, commit=True):
|
||||
|
||||
for device in self.placeholders:
|
||||
db.session.add(device)
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ Revises: ${down_revision | comma,n}
|
|||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
from alembic import op, context
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
import citext
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
"""reset check_constraint of placeholders
|
||||
|
||||
Revision ID: 57e6201f280c
|
||||
Revises: 8ccba3cb37c2
|
||||
Create Date: 2023-08-02 15:56:12.484340
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import context, op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '57e6201f280c'
|
||||
down_revision = '8ccba3cb37c2'
|
||||
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():
|
||||
sql = "select constraint_name from information_schema.table_constraints "
|
||||
sql += "where table_name='device' and constraint_type='CHECK';"
|
||||
con = op.get_bind()
|
||||
constraints = []
|
||||
for c in con.execute(sql):
|
||||
constraints.append(c.constraint_name)
|
||||
if 'device_depth_check' in constraints:
|
||||
op.drop_constraint(
|
||||
'device_depth_check', "device", type_="check", schema=f'{get_inv()}'
|
||||
)
|
||||
if 'device_height_check' in constraints:
|
||||
op.drop_constraint(
|
||||
'device_height_check', "device", type_="check", schema=f'{get_inv()}'
|
||||
)
|
||||
if 'device_width_check' in constraints:
|
||||
op.drop_constraint(
|
||||
'device_width_check', "device", type_="check", schema=f'{get_inv()}'
|
||||
)
|
||||
if 'device_weight_check' in constraints:
|
||||
op.drop_constraint(
|
||||
'device_weight_check', "device", type_="check", schema=f'{get_inv()}'
|
||||
)
|
||||
|
||||
op.create_check_constraint(
|
||||
"device_depth_check",
|
||||
"device",
|
||||
sa.Column("depth") >= (0.1),
|
||||
schema=f'{get_inv()}',
|
||||
)
|
||||
op.create_check_constraint(
|
||||
"device_height_check",
|
||||
"device",
|
||||
sa.Column("depth") >= (0.1),
|
||||
schema=f'{get_inv()}',
|
||||
)
|
||||
op.create_check_constraint(
|
||||
"device_width_check",
|
||||
"device",
|
||||
sa.Column("depth") >= (0.1),
|
||||
schema=f'{get_inv()}',
|
||||
)
|
||||
op.create_check_constraint(
|
||||
"device_weight_check",
|
||||
"device",
|
||||
sa.Column("depth") >= (0.1),
|
||||
schema=f'{get_inv()}',
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
|
@ -0,0 +1,41 @@
|
|||
"""add solar panel
|
||||
|
||||
Revision ID: 8ccba3cb37c2
|
||||
Revises: 5169765e2653
|
||||
Create Date: 2023-07-26 09:23:21.326465
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import context, op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '8ccba3cb37c2'
|
||||
down_revision = '5169765e2653'
|
||||
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():
|
||||
# creating Solar panel device.
|
||||
|
||||
op.create_table(
|
||||
'solar_panel',
|
||||
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('solar_panel', schema=f'{get_inv()}')
|
|
@ -726,3 +726,34 @@ class OtherDef(DeviceDef):
|
|||
root_path,
|
||||
cli_commands,
|
||||
)
|
||||
|
||||
|
||||
class SolarPanelDef(DeviceDef):
|
||||
VIEW = None
|
||||
SCHEMA = schemas.SolarPanel
|
||||
|
||||
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,
|
||||
)
|
||||
|
|
|
@ -150,13 +150,13 @@ class Device(Thing):
|
|||
generation.comment = """The generation of the device."""
|
||||
version = db.Column(db.CIText())
|
||||
version.comment = """The version code of this device, like v1 or A001."""
|
||||
weight = Column(Float(decimal_return_scale=4), check_range('weight', 0.1, 5))
|
||||
weight = Column(Float(decimal_return_scale=4))
|
||||
weight.comment = """The weight of the device in Kg."""
|
||||
width = Column(Float(decimal_return_scale=4), check_range('width', 0.1, 5))
|
||||
width = Column(Float(decimal_return_scale=4))
|
||||
width.comment = """The width of the device in meters."""
|
||||
height = Column(Float(decimal_return_scale=4), check_range('height', 0.1, 5))
|
||||
height = Column(Float(decimal_return_scale=4))
|
||||
height.comment = """The height of the device in meters."""
|
||||
depth = Column(Float(decimal_return_scale=4), check_range('depth', 0.1, 5))
|
||||
depth = Column(Float(decimal_return_scale=4))
|
||||
depth.comment = """The depth of the device in meters."""
|
||||
color = Column(ColorType)
|
||||
color.comment = """The predominant color of the device."""
|
||||
|
@ -476,7 +476,8 @@ class Device(Thing):
|
|||
"""The trading state, or None if no Trade action has
|
||||
ever been performed to this device. This extract the posibilities for to do.
|
||||
This method is performed for show in the web.
|
||||
If you need to do one simple and generic response you can put simple=True for that."""
|
||||
If you need to do one simple and generic response you can put simple=True for that.
|
||||
"""
|
||||
if not hasattr(lot, 'trade'):
|
||||
return
|
||||
|
||||
|
@ -928,6 +929,7 @@ class Device(Thing):
|
|||
"Cellphone": "bi bi-telephone",
|
||||
"HardDrive": "bi bi-hdd-stack",
|
||||
"SolidStateDrive": "bi bi-hdd",
|
||||
"SolarPanel": "bi-solar-panel",
|
||||
}
|
||||
return types.get(self.type, '')
|
||||
|
||||
|
@ -1986,3 +1988,11 @@ class Other(Device):
|
|||
"""
|
||||
|
||||
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
|
||||
|
||||
|
||||
class SolarPanel(Device):
|
||||
"""
|
||||
Used solar panels devices.
|
||||
"""
|
||||
|
||||
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
|
||||
|
|
|
@ -67,18 +67,10 @@ class Device(Thing):
|
|||
validate=Range(1, 100), description=m.Device.generation.comment
|
||||
)
|
||||
version = SanitizedStr(description=m.Device.version)
|
||||
weight = Float(
|
||||
validate=Range(0.1, 5), unit=UnitCodes.kgm, description=m.Device.weight.comment
|
||||
)
|
||||
width = Float(
|
||||
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment
|
||||
)
|
||||
height = Float(
|
||||
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment
|
||||
)
|
||||
depth = Float(
|
||||
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment
|
||||
)
|
||||
weight = Float(unit=UnitCodes.kgm, description=m.Device.weight.comment)
|
||||
width = Float(unit=UnitCodes.m, description=m.Device.width.comment)
|
||||
height = Float(unit=UnitCodes.m, description=m.Device.height.comment)
|
||||
depth = Float(unit=UnitCodes.m, description=m.Device.depth.comment)
|
||||
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
||||
actions = NestedOn(
|
||||
'Action', many=True, dump_only=True, description=m.Device.actions.__doc__
|
||||
|
@ -590,5 +582,9 @@ class Racket(Recreation):
|
|||
pass
|
||||
|
||||
|
||||
class SolarPanel(Device):
|
||||
pass
|
||||
|
||||
|
||||
class Other(Device):
|
||||
pass
|
||||
|
|
|
@ -92,10 +92,21 @@ class Sync:
|
|||
# We only want to perform Add/Remove to not new components
|
||||
actions = self.add_remove(db_device, not_new_components)
|
||||
db_device.components = db_components
|
||||
self.clean_parent_orphans_components(db_device)
|
||||
|
||||
self.create_placeholder(db_device)
|
||||
return db_device, actions
|
||||
|
||||
def clean_parent_orphans_components(self, device):
|
||||
all_components = Component.query.filter_by(parent_id=device.id)
|
||||
for _c in all_components:
|
||||
if _c not in device.components:
|
||||
_c.parent = None
|
||||
if _c.binding:
|
||||
_c.binding.device.parent = None
|
||||
if _c.placeholder and _c.placeholder.binding:
|
||||
_c.placeholder.binding.parent = None
|
||||
|
||||
def execute_register_component(self, component: Component):
|
||||
"""Synchronizes one component to the DB.
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ class DeviceView(View):
|
|||
"""Gets many devices."""
|
||||
# Compute query
|
||||
query = self.query(args)
|
||||
devices = query.paginate(page=args['page'], per_page=30) # type: Pagination
|
||||
devices = query.paginate(page=args['page'], per_page=100) # type: Pagination
|
||||
return things_response(
|
||||
self.schema.dump(devices.items, many=True, nested=1),
|
||||
devices.page,
|
||||
|
|
|
@ -422,30 +422,37 @@ class DeviceRow(BaseDeviceRow):
|
|||
self['{} {} Speed (MHz)'.format(ctype, i)] = none2str(component.speed)
|
||||
|
||||
def get_erasure_datawipe_mobile(self, device):
|
||||
if isinstance(device, d.DataStorage):
|
||||
if device.placeholder and device.placeholder.binding:
|
||||
binding = device.placeholder.binding
|
||||
return self.get_datastorage('DataStorage', 1, binding)
|
||||
return self.get_datastorage('DataStorage', 1, device)
|
||||
|
||||
if not isinstance(device, d.Mobile):
|
||||
return
|
||||
|
||||
actions = sorted(device.actions)
|
||||
erasures = [a for a in actions if a.type == 'EraseDataWipe']
|
||||
erasure = erasures[-1] if erasures else None
|
||||
if erasure:
|
||||
self['Erasure DataStorage 1'] = none2str(device.chid)
|
||||
if isinstance(device, d.Mobile):
|
||||
serial_number = none2str(device.imei)
|
||||
size = device.data_storage_size
|
||||
size = size * 1000 if size else 0
|
||||
storage_size = none2str(size)
|
||||
|
||||
if isinstance(device, d.DataStorage):
|
||||
serial_number = none2str(device.serial_number)
|
||||
storage_size = none2str(device.size)
|
||||
if not erasure:
|
||||
return
|
||||
|
||||
self['Erasure DataStorage 1 Serial Number'] = serial_number
|
||||
self['Erasure DataStorage 1 Size (MB)'] = storage_size
|
||||
self['Erasure DataStorage 1 Software'] = erasure.document.software
|
||||
self['Erasure DataStorage 1 Result'] = get_result(erasure)
|
||||
self['Erasure DataStorage 1 Type'] = erasure.type
|
||||
self['Erasure DataStorage 1 Date'] = format(erasure.document.date or '')
|
||||
self['Erasure DataStorage 1 Certificate URL'] = (
|
||||
erasure.document.url and erasure.document.url.to_text() or ''
|
||||
)
|
||||
self['Erasure DataStorage 1'] = none2str(device.chid)
|
||||
serial_number = none2str(device.imei)
|
||||
size = device.data_storage_size
|
||||
size = size * 1000 if size else 0
|
||||
storage_size = none2str(size)
|
||||
|
||||
self['Erasure DataStorage 1 Serial Number'] = serial_number
|
||||
self['Erasure DataStorage 1 Size (MB)'] = storage_size
|
||||
self['Erasure DataStorage 1 Result'] = get_result(erasure)
|
||||
self['Erasure DataStorage 1 Type'] = erasure.type
|
||||
self['Erasure DataStorage 1 Software'] = erasure.document.software
|
||||
self['Erasure DataStorage 1 Date'] = format(erasure.document.date or '')
|
||||
self['Erasure DataStorage 1 Certificate URL'] = (
|
||||
erasure.document.url and erasure.document.url.to_text() or ''
|
||||
)
|
||||
|
||||
def get_datastorage(self, ctype, i, component):
|
||||
"""Particular fields for component DataStorage.
|
||||
|
|
|
@ -42,4 +42,15 @@
|
|||
}
|
||||
.printLabelForm {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.bi-solar-panel {
|
||||
background-image: url("/static/img/solar-panel.png");
|
||||
background-repeat:no-repeat;
|
||||
background-size: cover;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin-right: 5px;
|
||||
line-height: 17px;
|
||||
font-size: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
|
BIN
ereuse_devicehub/static/img/solar-panel.png
Normal file
BIN
ereuse_devicehub/static/img/solar-panel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -79,6 +79,8 @@
|
|||
{% if form.type.data == 'Keyboard' %} selected="selected"{% endif %}>Keyboard</option>
|
||||
</optgroup>
|
||||
<optgroup label="Other Type of Device">
|
||||
<option value="SolarPanel"
|
||||
{% if form.type.data == 'SolarPanel' %} selected="selected"{% endif %}>Solar Panel</option>
|
||||
<option value="Other"
|
||||
{% if form.type.data == 'Other' %} selected="selected"{% endif %}>Other</option>
|
||||
</optgroup>
|
||||
|
|
|
@ -72,6 +72,12 @@
|
|||
<option value="Keyboard"
|
||||
{% if form.type.data == 'Keyboard' %} selected="selected"{% endif %}>Keyboard</option>
|
||||
</optgroup>
|
||||
<optgroup label="Other Type of Device">
|
||||
<option value="SolarPanel"
|
||||
{% if form.type.data == 'SolarPanel' %} selected="selected"{% endif %}>Solar Panel</option>
|
||||
<option value="Other"
|
||||
{% if form.type.data == 'Other' %} selected="selected"{% endif %}>Other</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<small class="text-muted form-text">Type of devices</small>
|
||||
{% if form.type.errors %}
|
||||
|
|
|
@ -125,4 +125,4 @@ def test_api_docs(client: Client):
|
|||
'scheme': 'basic',
|
||||
'name': 'Authorization',
|
||||
}
|
||||
assert len(docs['definitions']) == 135
|
||||
assert len(docs['definitions']) == 136
|
||||
|
|
Reference in a new issue