This commit is contained in:
Jakob Ketterl 2021-02-08 20:30:12 +01:00
parent 331e9627d6
commit 2d72055070
8 changed files with 188 additions and 182 deletions

View File

@ -12,14 +12,13 @@ from owrx.form import (
Option,
ServicesCheckboxInput,
Js8ProfileCheckboxInput,
ReceiverKeysConverter,
WfmTauValues,
MultiCheckboxInput,
OptionalConverter,
AprsBeaconSymbols,
AprsAntennaDirections,
Q65ModeMatrix,
)
from owrx.form.converter import OptionalConverter
from owrx.form.receiverid import ReceiverKeysConverter
from owrx.form.aprs import AprsBeaconSymbols, AprsAntennaDirections
from owrx.form.wfm import WfmTauValues
from owrx.form.wsjt import Q65ModeMatrix
from urllib.parse import quote
from owrx.wsjt import Fst4Profile, Fst4wProfile
import json

View File

@ -1,41 +1,10 @@
from abc import ABC, abstractmethod
from owrx.modes import Modes
from owrx.config import Config
from owrx.wsjt import Q65Mode, Q65Interval
from owrx.form.converter import Converter, NullConverter, IntConverter, FloatConverter, EnumConverter
from enum import Enum
class Converter(ABC):
@abstractmethod
def convert_to_form(self, value):
pass
@abstractmethod
def convert_from_form(self, value):
pass
class NullConverter(Converter):
def convert_to_form(self, value):
return value
def convert_from_form(self, value):
return value
class OptionalConverter(Converter):
"""
Maps None to an empty string, and reverse
useful for optional fields
"""
def convert_to_form(self, value):
return "" if value is None else value
def convert_from_form(self, value):
return value if value else None
class Input(ABC):
def __init__(self, id, label, infotext=None, converter: Converter = None):
self.id = id
@ -84,14 +53,6 @@ class TextInput(Input):
)
class IntConverter(Converter):
def convert_to_form(self, value):
return str(value)
def convert_from_form(self, value):
return int(value)
class NumberInput(Input):
def __init__(self, id, label, infotext=None, append="", converter: Converter = None):
super().__init__(id, label, infotext, converter=converter)
@ -128,14 +89,6 @@ class NumberInput(Input):
)
class FloatConverter(Converter):
def convert_to_form(self, value):
return str(value)
def convert_from_form(self, value):
return float(value)
class FloatInput(NumberInput):
def __init__(self, id, label, infotext=None, converter: Converter = None):
super().__init__(id, label, infotext, converter=converter)
@ -185,15 +138,6 @@ class TextAreaInput(Input):
)
class ReceiverKeysConverter(Converter):
def convert_to_form(self, value):
return "\n".join(value)
def convert_from_form(self, value):
# \r\n or \n? this should work with both.
return [v.strip("\r ") for v in value.split("\n")]
class CheckboxInput(Input):
def __init__(self, id, label, checkboxText, infotext=None):
super().__init__(id, label, infotext=infotext)
@ -317,125 +261,6 @@ class DropdownInput(Input):
return "".join(options)
class Q65ModeConverter(Converter):
def convert_to_form(self, value):
pass
def convert_from_form(self, value):
pass
class Q65ModeMatrix(Input):
def checkbox_id(self, mode, interval):
return "{0}-{1}-{2}".format(self.id, mode.value, interval.value)
def render_checkbox(self, mode: Q65Mode, interval: Q65Interval, value):
return """
<div class="{classes}">
<input class="form-check-input" type="checkbox" id="{id}" name="{id}" {checked} {disabled}>
<label class="form-check-label" for="{id}">
{checkboxText}
</label>
</div>
""".format(
classes=self.input_classes(),
id=self.checkbox_id(mode, interval),
checked="checked" if "{}{}".format(mode.name, interval.value) in value else "",
checkboxText="Mode {} interval {}s".format(mode.name, interval.value),
disabled="" if interval.is_available(mode) else "disabled",
)
def render_input(self, value):
checkboxes = "".join(
self.render_checkbox(mode, interval, value) for interval in Q65Interval for mode in Q65Mode
)
return """
<div class="matrix q65-matrix">
{checkboxes}
</div>
""".format(
checkboxes=checkboxes
)
def input_classes(self):
return " ".join(["form-check", "form-control-sm"])
def parse(self, data):
def in_response(mode, interval):
boxid = self.checkbox_id(mode, interval)
return boxid in data and data[boxid][0] == "on"
return {
self.id: [
"{}{}".format(mode.name, interval.value)
for interval in Q65Interval
for mode in Q65Mode
if in_response(mode, interval)
],
}
class DropdownEnum(Enum):
def toOption(self):
return Option(self.name, str(self))
class EnumConverter(Converter):
def __init__(self, enumCls):
self.enumCls = enumCls
def convert_to_form(self, value):
return None if value is None else self.enumCls(value).name
def convert_from_form(self, value):
return self.enumCls[value].value
class WfmTauValues(DropdownEnum):
TAU_50_MICRO = (50e-6, "most regions")
TAU_75_MICRO = (75e-6, "Americas and South Korea")
def __new__(cls, *args, **kwargs):
value, description = args
obj = object.__new__(cls)
obj._value_ = value
obj.description = description
return obj
def __str__(self):
return "{}µs ({})".format(int(self.value * 1e6), self.description)
class AprsBeaconSymbols(DropdownEnum):
BEACON_RECEIVE_ONLY = ("R&", "Receive only IGate")
BEACON_HF_GATEWAY = ("/&", "HF Gateway")
BEACON_IGATE_GENERIC = ("I&", "Igate Generic (please use more specific overlay)")
BEACON_PSKMAIL = ("P&", "PSKmail node")
BEACON_TX_1 = ("T&", "TX IGate with path set to 1 hop")
BEACON_WIRES_X = ("W&", "Wires-X")
BEACON_TX_2 = ("2&", "TX IGate with path set to 2 hops")
def __new__(cls, *args, **kwargs):
value, description = args
obj = object.__new__(cls)
obj._value_ = value
obj.description = description
return obj
def __str__(self):
return "{description} ({symbol})".format(description=self.description, symbol=self.value)
class AprsAntennaDirections(DropdownEnum):
DIRECTION_OMNI = None
DIRECTION_N = "N"
DIRECTION_NE = "NE"
DIRECTION_E = "E"
DIRECTION_SE = "SE"
DIRECTION_S = "S"
DIRECTION_SW = "SW"
DIRECTION_W = "W"
DIRECTION_NW = "NW"
def __str__(self):
return "omnidirectional" if self.value is None else self.value

36
owrx/form/aprs.py Normal file
View File

@ -0,0 +1,36 @@
from owrx.form import DropdownEnum
class AprsBeaconSymbols(DropdownEnum):
BEACON_RECEIVE_ONLY = ("R&", "Receive only IGate")
BEACON_HF_GATEWAY = ("/&", "HF Gateway")
BEACON_IGATE_GENERIC = ("I&", "Igate Generic (please use more specific overlay)")
BEACON_PSKMAIL = ("P&", "PSKmail node")
BEACON_TX_1 = ("T&", "TX IGate with path set to 1 hop")
BEACON_WIRES_X = ("W&", "Wires-X")
BEACON_TX_2 = ("2&", "TX IGate with path set to 2 hops")
def __new__(cls, *args, **kwargs):
value, description = args
obj = object.__new__(cls)
obj._value_ = value
obj.description = description
return obj
def __str__(self):
return "{description} ({symbol})".format(description=self.description, symbol=self.value)
class AprsAntennaDirections(DropdownEnum):
DIRECTION_OMNI = None
DIRECTION_N = "N"
DIRECTION_NE = "NE"
DIRECTION_E = "E"
DIRECTION_SE = "SE"
DIRECTION_S = "S"
DIRECTION_SW = "SW"
DIRECTION_W = "W"
DIRECTION_NW = "NW"
def __str__(self):
return "omnidirectional" if self.value is None else self.value

61
owrx/form/converter.py Normal file
View File

@ -0,0 +1,61 @@
from abc import ABC, abstractmethod
class Converter(ABC):
@abstractmethod
def convert_to_form(self, value):
pass
@abstractmethod
def convert_from_form(self, value):
pass
class NullConverter(Converter):
def convert_to_form(self, value):
return value
def convert_from_form(self, value):
return value
class OptionalConverter(Converter):
"""
Maps None to an empty string, and reverse
useful for optional fields
"""
def convert_to_form(self, value):
return "" if value is None else value
def convert_from_form(self, value):
return value if value else None
class IntConverter(Converter):
def convert_to_form(self, value):
return str(value)
def convert_from_form(self, value):
return int(value)
class FloatConverter(Converter):
def convert_to_form(self, value):
return str(value)
def convert_from_form(self, value):
return float(value)
class EnumConverter(Converter):
def __init__(self, enumCls):
self.enumCls = enumCls
def convert_to_form(self, value):
return None if value is None else self.enumCls(value).name
def convert_from_form(self, value):
return self.enumCls[value].value

7
owrx/form/gfx.py Normal file
View File

@ -0,0 +1,7 @@
from owrx.form import Input
class AvatarInput(Input):
def render_input(self, value):
pass

10
owrx/form/receiverid.py Normal file
View File

@ -0,0 +1,10 @@
from owrx.form.converter import Converter
class ReceiverKeysConverter(Converter):
def convert_to_form(self, value):
return "\n".join(value)
def convert_from_form(self, value):
# \r\n or \n? this should work with both.
return [v.strip("\r ") for v in value.split("\n")]

16
owrx/form/wfm.py Normal file
View File

@ -0,0 +1,16 @@
from owrx.form import DropdownEnum
class WfmTauValues(DropdownEnum):
TAU_50_MICRO = (50e-6, "most regions")
TAU_75_MICRO = (75e-6, "Americas and South Korea")
def __new__(cls, *args, **kwargs):
value, description = args
obj = object.__new__(cls)
obj._value_ = value
obj.description = description
return obj
def __str__(self):
return "{}µs ({})".format(int(self.value * 1e6), self.description)

52
owrx/form/wsjt.py Normal file
View File

@ -0,0 +1,52 @@
from owrx.form import Input
from owrx.wsjt import Q65Mode, Q65Interval
class Q65ModeMatrix(Input):
def checkbox_id(self, mode, interval):
return "{0}-{1}-{2}".format(self.id, mode.value, interval.value)
def render_checkbox(self, mode: Q65Mode, interval: Q65Interval, value):
return """
<div class="{classes}">
<input class="form-check-input" type="checkbox" id="{id}" name="{id}" {checked} {disabled}>
<label class="form-check-label" for="{id}">
{checkboxText}
</label>
</div>
""".format(
classes=self.input_classes(),
id=self.checkbox_id(mode, interval),
checked="checked" if "{}{}".format(mode.name, interval.value) in value else "",
checkboxText="Mode {} interval {}s".format(mode.name, interval.value),
disabled="" if interval.is_available(mode) else "disabled",
)
def render_input(self, value):
checkboxes = "".join(
self.render_checkbox(mode, interval, value) for interval in Q65Interval for mode in Q65Mode
)
return """
<div class="matrix q65-matrix">
{checkboxes}
</div>
""".format(
checkboxes=checkboxes
)
def input_classes(self):
return " ".join(["form-check", "form-control-sm"])
def parse(self, data):
def in_response(mode, interval):
boxid = self.checkbox_id(mode, interval)
return boxid in data and data[boxid][0] == "on"
return {
self.id: [
"{}{}".format(mode.name, interval.value)
for interval in Q65Interval
for mode in Q65Mode
if in_response(mode, interval)
],
}