diff --git a/owrx/controllers/settings/__init__.py b/owrx/controllers/settings/__init__.py index 539af53..8ad8a4e 100644 --- a/owrx/controllers/settings/__init__.py +++ b/owrx/controllers/settings/__init__.py @@ -1,7 +1,6 @@ from owrx.config import Config from owrx.controllers.admin import AuthorizationMixin from owrx.controllers.template import WebpageController -from owrx.form.error import FormError from owrx.breadcrumb import Breadcrumb, BreadcrumbItem, BreadcrumbMixin from abc import ABCMeta, abstractmethod from urllib.parse import parse_qs @@ -11,45 +10,6 @@ import logging 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 """ -
-

- {title} -

- {inputs} -
- """.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): def indexAction(self): self.serve_template("settings.html", **self.template_variables()) diff --git a/owrx/controllers/settings/backgrounddecoding.py b/owrx/controllers/settings/backgrounddecoding.py index c2dd332..902c37f 100644 --- a/owrx/controllers/settings/backgrounddecoding.py +++ b/owrx/controllers/settings/backgrounddecoding.py @@ -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.breadcrumb import Breadcrumb, BreadcrumbItem from owrx.controllers.settings import SettingsBreadcrumb diff --git a/owrx/controllers/settings/decoding.py b/owrx/controllers/settings/decoding.py index be035ab..7e72b8e 100644 --- a/owrx/controllers/settings/decoding.py +++ b/owrx/controllers/settings/decoding.py @@ -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.wfm import WfmTauValues from owrx.form.input.wsjt import Q65ModeMatrix, WsjtDecodingDepthsInput diff --git a/owrx/controllers/settings/general.py b/owrx/controllers/settings/general.py index 3dc17c1..da9c631 100644 --- a/owrx/controllers/settings/general.py +++ b/owrx/controllers/settings/general.py @@ -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.form.input import ( TextInput, diff --git a/owrx/controllers/settings/reporting.py b/owrx/controllers/settings/reporting.py index 1db2974..09ee775 100644 --- a/owrx/controllers/settings/reporting.py +++ b/owrx/controllers/settings/reporting.py @@ -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.aprs import AprsBeaconSymbols, AprsAntennaDirections from owrx.form.input import TextInput, CheckboxInput, DropdownInput, NumberInput diff --git a/owrx/controllers/settings/sdr.py b/owrx/controllers/settings/sdr.py index 68f591d..bba1465 100644 --- a/owrx/controllers/settings/sdr.py +++ b/owrx/controllers/settings/sdr.py @@ -4,7 +4,8 @@ from owrx.controllers.settings import SettingsFormController from owrx.source import SdrDeviceDescription, SdrDeviceDescriptionMissing, SdrClientClass from owrx.config import Config 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 owrx.sdr import SdrService from owrx.form.input import TextInput, DropdownInput, Option diff --git a/owrx/form/section.py b/owrx/form/section.py new file mode 100644 index 0000000..3c53cf2 --- /dev/null +++ b/owrx/form/section.py @@ -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 """ +
+

+ {title} +

+ {inputs} +
+ """.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 """ +
+
+ +
+
+ +
+ +
+
+ """.format( + options="".join( + """ + + """.format( + value=input.id, + name=input.getLabel(), + ) + for input in self.optional_inputs + ) + ) + + def render_optional_inputs(self, data, errors): + return """ + + """.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 diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index e5801be..7f5e0a4 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -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.device import GainInput, SchedulerInput, WaterfallLevelsInput 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 typing import List from enum import Enum @@ -447,90 +447,6 @@ class SdrDeviceDescriptionMissing(Exception): 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 """ -
-
- -
-
- -
- -
-
- """.format( - options="".join( - """ - - """.format( - value=input.id, - name=input.getLabel(), - ) - for input in self.optional_inputs - ) - ) - - def render_optional_inputs(self, data, errors): - return """ - - """.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): @staticmethod def getByType(sdr_type: str) -> "SdrDeviceDescription":