refactor: move form stuff out of source code
This commit is contained in:
parent
35dcff90ea
commit
29bce9e07a
@ -1,7 +1,6 @@
|
|||||||
from owrx.config import Config
|
from owrx.config import Config
|
||||||
from owrx.controllers.admin import AuthorizationMixin
|
from owrx.controllers.admin import AuthorizationMixin
|
||||||
from owrx.controllers.template import WebpageController
|
from owrx.controllers.template import WebpageController
|
||||||
from owrx.form.error import FormError
|
|
||||||
from owrx.breadcrumb import Breadcrumb, BreadcrumbItem, BreadcrumbMixin
|
from owrx.breadcrumb import Breadcrumb, BreadcrumbItem, BreadcrumbMixin
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
@ -11,45 +10,6 @@ import logging
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Section(object):
|
|
||||||
def __init__(self, title, *inputs):
|
|
||||||
self.title = title
|
|
||||||
self.inputs = inputs
|
|
||||||
|
|
||||||
def render_input(self, input, data, errors):
|
|
||||||
return input.render(data, errors)
|
|
||||||
|
|
||||||
def render_inputs(self, data, errors):
|
|
||||||
return "".join([self.render_input(i, data, errors) for i in self.inputs])
|
|
||||||
|
|
||||||
def classes(self):
|
|
||||||
return ["col-12", "settings-section"]
|
|
||||||
|
|
||||||
def render(self, data, errors):
|
|
||||||
return """
|
|
||||||
<div class="{classes}">
|
|
||||||
<h3 class="settings-header">
|
|
||||||
{title}
|
|
||||||
</h3>
|
|
||||||
{inputs}
|
|
||||||
</div>
|
|
||||||
""".format(
|
|
||||||
classes=" ".join(self.classes()), title=self.title, inputs=self.render_inputs(data, errors)
|
|
||||||
)
|
|
||||||
|
|
||||||
def parse(self, data):
|
|
||||||
parsed_data = {}
|
|
||||||
errors = []
|
|
||||||
for i in self.inputs:
|
|
||||||
try:
|
|
||||||
parsed_data.update(i.parse(data))
|
|
||||||
except FormError as e:
|
|
||||||
errors.append(e)
|
|
||||||
except Exception as e:
|
|
||||||
errors.append(FormError(i.id, "{}: {}".format(type(e).__name__, e)))
|
|
||||||
return parsed_data, errors
|
|
||||||
|
|
||||||
|
|
||||||
class SettingsController(AuthorizationMixin, WebpageController):
|
class SettingsController(AuthorizationMixin, WebpageController):
|
||||||
def indexAction(self):
|
def indexAction(self):
|
||||||
self.serve_template("settings.html", **self.template_variables())
|
self.serve_template("settings.html", **self.template_variables())
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from owrx.controllers.settings import SettingsFormController, Section
|
from owrx.controllers.settings import SettingsFormController
|
||||||
|
from owrx.form.section import Section
|
||||||
from owrx.form.input import CheckboxInput, ServicesCheckboxInput
|
from owrx.form.input import CheckboxInput, ServicesCheckboxInput
|
||||||
from owrx.breadcrumb import Breadcrumb, BreadcrumbItem
|
from owrx.breadcrumb import Breadcrumb, BreadcrumbItem
|
||||||
from owrx.controllers.settings import SettingsBreadcrumb
|
from owrx.controllers.settings import SettingsBreadcrumb
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from owrx.controllers.settings import SettingsFormController, Section, SettingsBreadcrumb
|
from owrx.controllers.settings import SettingsFormController, SettingsBreadcrumb
|
||||||
|
from owrx.form.section import Section
|
||||||
from owrx.form.input import CheckboxInput, NumberInput, DropdownInput, Js8ProfileCheckboxInput, MultiCheckboxInput, Option
|
from owrx.form.input import CheckboxInput, NumberInput, DropdownInput, Js8ProfileCheckboxInput, MultiCheckboxInput, Option
|
||||||
from owrx.form.input.wfm import WfmTauValues
|
from owrx.form.input.wfm import WfmTauValues
|
||||||
from owrx.form.input.wsjt import Q65ModeMatrix, WsjtDecodingDepthsInput
|
from owrx.form.input.wsjt import Q65ModeMatrix, WsjtDecodingDepthsInput
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from owrx.controllers.settings import Section, SettingsFormController
|
from owrx.controllers.settings import SettingsFormController
|
||||||
|
from owrx.form.section import Section
|
||||||
from owrx.config.core import CoreConfig
|
from owrx.config.core import CoreConfig
|
||||||
from owrx.form.input import (
|
from owrx.form.input import (
|
||||||
TextInput,
|
TextInput,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from owrx.controllers.settings import SettingsFormController, Section, SettingsBreadcrumb
|
from owrx.controllers.settings import SettingsFormController, SettingsBreadcrumb
|
||||||
|
from owrx.form.section import Section
|
||||||
from owrx.form.input.converter import OptionalConverter
|
from owrx.form.input.converter import OptionalConverter
|
||||||
from owrx.form.input.aprs import AprsBeaconSymbols, AprsAntennaDirections
|
from owrx.form.input.aprs import AprsBeaconSymbols, AprsAntennaDirections
|
||||||
from owrx.form.input import TextInput, CheckboxInput, DropdownInput, NumberInput
|
from owrx.form.input import TextInput, CheckboxInput, DropdownInput, NumberInput
|
||||||
|
@ -4,7 +4,8 @@ from owrx.controllers.settings import SettingsFormController
|
|||||||
from owrx.source import SdrDeviceDescription, SdrDeviceDescriptionMissing, SdrClientClass
|
from owrx.source import SdrDeviceDescription, SdrDeviceDescriptionMissing, SdrClientClass
|
||||||
from owrx.config import Config
|
from owrx.config import Config
|
||||||
from owrx.connection import OpenWebRxReceiverClient
|
from owrx.connection import OpenWebRxReceiverClient
|
||||||
from owrx.controllers.settings import Section, SettingsBreadcrumb
|
from owrx.controllers.settings import SettingsBreadcrumb
|
||||||
|
from owrx.form.section import Section
|
||||||
from urllib.parse import quote, unquote
|
from urllib.parse import quote, unquote
|
||||||
from owrx.sdr import SdrService
|
from owrx.sdr import SdrService
|
||||||
from owrx.form.input import TextInput, DropdownInput, Option
|
from owrx.form.input import TextInput, DropdownInput, Option
|
||||||
|
126
owrx/form/section.py
Normal file
126
owrx/form/section.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
from owrx.form.error import FormError
|
||||||
|
from owrx.form.input import Input
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class Section(object):
|
||||||
|
def __init__(self, title, *inputs):
|
||||||
|
self.title = title
|
||||||
|
self.inputs = inputs
|
||||||
|
|
||||||
|
def render_input(self, input, data, errors):
|
||||||
|
return input.render(data, errors)
|
||||||
|
|
||||||
|
def render_inputs(self, data, errors):
|
||||||
|
return "".join([self.render_input(i, data, errors) for i in self.inputs])
|
||||||
|
|
||||||
|
def classes(self):
|
||||||
|
return ["col-12", "settings-section"]
|
||||||
|
|
||||||
|
def render(self, data, errors):
|
||||||
|
return """
|
||||||
|
<div class="{classes}">
|
||||||
|
<h3 class="settings-header">
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
{inputs}
|
||||||
|
</div>
|
||||||
|
""".format(
|
||||||
|
classes=" ".join(self.classes()), title=self.title, inputs=self.render_inputs(data, errors)
|
||||||
|
)
|
||||||
|
|
||||||
|
def parse(self, data):
|
||||||
|
parsed_data = {}
|
||||||
|
errors = []
|
||||||
|
for i in self.inputs:
|
||||||
|
try:
|
||||||
|
parsed_data.update(i.parse(data))
|
||||||
|
except FormError as e:
|
||||||
|
errors.append(e)
|
||||||
|
except Exception as e:
|
||||||
|
errors.append(FormError(i.id, "{}: {}".format(type(e).__name__, e)))
|
||||||
|
return parsed_data, errors
|
||||||
|
|
||||||
|
|
||||||
|
class OptionalSection(Section):
|
||||||
|
def __init__(self, title, inputs: List[Input], mandatory, optional):
|
||||||
|
super().__init__(title, *inputs)
|
||||||
|
self.mandatory = mandatory
|
||||||
|
self.optional = optional
|
||||||
|
self.optional_inputs = []
|
||||||
|
|
||||||
|
def classes(self):
|
||||||
|
classes = super().classes()
|
||||||
|
classes.append("optional-section")
|
||||||
|
return classes
|
||||||
|
|
||||||
|
def _is_optional(self, input):
|
||||||
|
return input.id in self.optional
|
||||||
|
|
||||||
|
def render_optional_select(self):
|
||||||
|
return """
|
||||||
|
<hr class="row" />
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-form-label col-form-label-sm col-3">
|
||||||
|
Additional optional settings
|
||||||
|
</label>
|
||||||
|
<div class="add-group col-9 p-0">
|
||||||
|
<div class="add-group-select">
|
||||||
|
<select class="form-control form-control-sm optional-select">
|
||||||
|
{options}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-sm btn-success option-add-button">Add</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
""".format(
|
||||||
|
options="".join(
|
||||||
|
"""
|
||||||
|
<option value="{value}">{name}</option>
|
||||||
|
""".format(
|
||||||
|
value=input.id,
|
||||||
|
name=input.getLabel(),
|
||||||
|
)
|
||||||
|
for input in self.optional_inputs
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def render_optional_inputs(self, data, errors):
|
||||||
|
return """
|
||||||
|
<div class="optional-inputs" style="display: none;">
|
||||||
|
{inputs}
|
||||||
|
</div>
|
||||||
|
""".format(
|
||||||
|
inputs="".join(self.render_input(input, data, errors) for input in self.optional_inputs)
|
||||||
|
)
|
||||||
|
|
||||||
|
def render_inputs(self, data, errors):
|
||||||
|
return (
|
||||||
|
super().render_inputs(data, errors)
|
||||||
|
+ self.render_optional_select()
|
||||||
|
+ self.render_optional_inputs(data, errors)
|
||||||
|
)
|
||||||
|
|
||||||
|
def render(self, data, errors):
|
||||||
|
indexed_inputs = {input.id: input for input in self.inputs}
|
||||||
|
visible_keys = set(self.mandatory + [k for k in self.optional if k in data])
|
||||||
|
optional_keys = set(k for k in self.optional if k not in data)
|
||||||
|
self.inputs = [input for k, input in indexed_inputs.items() if k in visible_keys]
|
||||||
|
for input in self.inputs:
|
||||||
|
if self._is_optional(input):
|
||||||
|
input.setRemovable()
|
||||||
|
self.optional_inputs = [input for k, input in indexed_inputs.items() if k in optional_keys]
|
||||||
|
for input in self.optional_inputs:
|
||||||
|
input.setRemovable()
|
||||||
|
input.setDisabled()
|
||||||
|
return super().render(data, errors)
|
||||||
|
|
||||||
|
def parse(self, data):
|
||||||
|
data, errors = super().parse(data)
|
||||||
|
# filter out errors for optional fields
|
||||||
|
errors = [e for e in errors if e.getKey() not in self.optional or e.getKey() in data]
|
||||||
|
# remove optional keys if they have been removed from the form by setting them to None
|
||||||
|
for k in self.optional:
|
||||||
|
if k not in data:
|
||||||
|
data[k] = None
|
||||||
|
return data, errors
|
@ -16,7 +16,7 @@ from owrx.form.input import Input, TextInput, NumberInput, CheckboxInput, ModesI
|
|||||||
from owrx.form.input.converter import OptionalConverter
|
from owrx.form.input.converter import OptionalConverter
|
||||||
from owrx.form.input.device import GainInput, SchedulerInput, WaterfallLevelsInput
|
from owrx.form.input.device import GainInput, SchedulerInput, WaterfallLevelsInput
|
||||||
from owrx.form.input.validator import RequiredValidator
|
from owrx.form.input.validator import RequiredValidator
|
||||||
from owrx.controllers.settings import Section
|
from owrx.form.section import OptionalSection
|
||||||
from owrx.feature import FeatureDetector
|
from owrx.feature import FeatureDetector
|
||||||
from typing import List
|
from typing import List
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@ -447,90 +447,6 @@ class SdrDeviceDescriptionMissing(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OptionalSection(Section):
|
|
||||||
def __init__(self, title, inputs: List[Input], mandatory, optional):
|
|
||||||
super().__init__(title, *inputs)
|
|
||||||
self.mandatory = mandatory
|
|
||||||
self.optional = optional
|
|
||||||
self.optional_inputs = []
|
|
||||||
|
|
||||||
def classes(self):
|
|
||||||
classes = super().classes()
|
|
||||||
classes.append("optional-section")
|
|
||||||
return classes
|
|
||||||
|
|
||||||
def _is_optional(self, input):
|
|
||||||
return input.id in self.optional
|
|
||||||
|
|
||||||
def render_optional_select(self):
|
|
||||||
return """
|
|
||||||
<hr class="row" />
|
|
||||||
<div class="form-group row">
|
|
||||||
<label class="col-form-label col-form-label-sm col-3">
|
|
||||||
Additional optional settings
|
|
||||||
</label>
|
|
||||||
<div class="add-group col-9 p-0">
|
|
||||||
<div class="add-group-select">
|
|
||||||
<select class="form-control form-control-sm optional-select">
|
|
||||||
{options}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-sm btn-success option-add-button">Add</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
""".format(
|
|
||||||
options="".join(
|
|
||||||
"""
|
|
||||||
<option value="{value}">{name}</option>
|
|
||||||
""".format(
|
|
||||||
value=input.id,
|
|
||||||
name=input.getLabel(),
|
|
||||||
)
|
|
||||||
for input in self.optional_inputs
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def render_optional_inputs(self, data, errors):
|
|
||||||
return """
|
|
||||||
<div class="optional-inputs" style="display: none;">
|
|
||||||
{inputs}
|
|
||||||
</div>
|
|
||||||
""".format(
|
|
||||||
inputs="".join(self.render_input(input, data, errors) for input in self.optional_inputs)
|
|
||||||
)
|
|
||||||
|
|
||||||
def render_inputs(self, data, errors):
|
|
||||||
return (
|
|
||||||
super().render_inputs(data, errors)
|
|
||||||
+ self.render_optional_select()
|
|
||||||
+ self.render_optional_inputs(data, errors)
|
|
||||||
)
|
|
||||||
|
|
||||||
def render(self, data, errors):
|
|
||||||
indexed_inputs = {input.id: input for input in self.inputs}
|
|
||||||
visible_keys = set(self.mandatory + [k for k in self.optional if k in data])
|
|
||||||
optional_keys = set(k for k in self.optional if k not in data)
|
|
||||||
self.inputs = [input for k, input in indexed_inputs.items() if k in visible_keys]
|
|
||||||
for input in self.inputs:
|
|
||||||
if self._is_optional(input):
|
|
||||||
input.setRemovable()
|
|
||||||
self.optional_inputs = [input for k, input in indexed_inputs.items() if k in optional_keys]
|
|
||||||
for input in self.optional_inputs:
|
|
||||||
input.setRemovable()
|
|
||||||
input.setDisabled()
|
|
||||||
return super().render(data, errors)
|
|
||||||
|
|
||||||
def parse(self, data):
|
|
||||||
data, errors = super().parse(data)
|
|
||||||
# filter out errors for optional fields
|
|
||||||
errors = [e for e in errors if e.getKey() not in self.optional or e.getKey() in data]
|
|
||||||
# remove optional keys if they have been removed from the form by setting them to None
|
|
||||||
for k in self.optional:
|
|
||||||
if k not in data:
|
|
||||||
data[k] = None
|
|
||||||
return data, errors
|
|
||||||
|
|
||||||
|
|
||||||
class SdrDeviceDescription(object):
|
class SdrDeviceDescription(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getByType(sdr_type: str) -> "SdrDeviceDescription":
|
def getByType(sdr_type: str) -> "SdrDeviceDescription":
|
||||||
|
Loading…
Reference in New Issue
Block a user