Skip to content

Commit

Permalink
Add fetools.pof toolset
Browse files Browse the repository at this point in the history
  • Loading branch information
cessnahat committed Aug 5, 2021
1 parent bc026b6 commit 4b1426e
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Changelog

#### `1.3.0` (2021-08-05)
- Add tools for working with POF files (`fetools.pof`)

#### `1.2.1-post` (2021-02-21)
- Rename `fetools.alias.AliasCommands.dumpxml` to `fetools.alias.AliasCommands._dumpxml`

Expand Down
178 changes: 178 additions & 0 deletions fetools/pof.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
""" Tools for reading, manipulating, and writing positions (POF stuff) """


import xml.etree.ElementTree as ET


__all__ = ["Positions", "load", "loads"]


class Positions:
"""
Store/manipulate POF positions
(See www.asrc.info/ASRC%20Documentation/Configuration.html)
"""

def __init__(self):
self._positions = {}

def __repr__(self):
return f"Positions({self._positions})"

def add(self, name, rname, freq, secid, artstag, callprefix, callsuffix,
line1, line2, lsquawk, hsquawk):
""" Add new position """
p = {}
p["rname"] = name if rname == "-" else rname
p["freq"] = str(freq).ljust(7, "0")
p["secid"] = secid
p["artstag"] = artstag
p["callprefix"] = callprefix
p["callsuffix"] = callsuffix
p["line1"] = line1
p["line2"] = line2
p["lsquawk"] = str(lsquawk)
p["hsquawk"] = str(hsquawk)
self._positions[name] = p

def set(self, sectName, **kwargs):
# Check if position even exists
if sectName not in self._positions:
raise KeyError(f'"{sectName}" is not an existing position')
""" Set/change a property of a position """
params = [
"rname", "freq", "secid", "artstag", "callprefix", "callsuffix",
"line1", "line2", "hsquawk", "lsquawk"
]
for k, v in kwargs.items():
# Check if k is a valid property name
if k not in params:
raise KeyError(f'"{k}" is not a recognized property name')
# Update each property
val = v
if k == "rname":
# Format rname
val = sectName if v == "-" else v
elif k == "freq":
# Pad frequency
val = str(v).ljust(7, "0")
elif k in ["hsquawk", "lsquawk"]:
val = str(v)
self._positions[sectName][k] = val

def rename(self, old, new):
# Check if position even exists
if old not in self._positions:
raise KeyError(f'"{old}" is not an existing position')
# Clone & remove
self._positions[new] = self._positions[old]
self.remove(old)

def get(self, name, attrib=None):
# Check if position even exists
if name not in self._positions:
raise KeyError(f'"{name}" is not an existing position')
# Return specific attrib if specified, else return the whole position
if attrib:
return self._positions[name][attrib]
return self._positions[name]

def remove(self, name):
# Check if position even exists
if name not in self._positions:
raise KeyError(f'"{name}" is not an existing position')
del self._positions[name]

def positions(self):
return list(self._positions.keys())

# def dump(self, file_obj, client):
# """ Writes Positions to passed-in file-like object """
# file_obj.write(self.dumps(client))

def dumps(self, client):
""" Returns a string in either the VRC txt or vSTARS/vERAM xml format """
if client.lower() == "vrc":
# Write to '.txt' format (VRC)
output = ""
for name, p in self._positions.items():
output += name + ":"
output += ("-" if p["rname"] == name else p["rname"]) + ":"
output += ":".join([str(item) for item in p.values()][1:]) + "\n"
return output
elif client.lower() in ["vstars", "veram"]:
# Write to '.xml' format (vSTARS & vERAM)
root = self._dumpxml()
# (ET.indent only works in Python 3.9+)
try:
ET.indent(root)
except:
pass
return ET.tostring(root, encoding="unicode")
else:
raise ValueError("unknown client " + repr(client))

def _dumpxml(self):
""" Returns an xml.etree.ElementTree Element object """
root = ET.Element("Positions")
for name, attribs in self._positions.items():
posXML = ET.SubElement(root, "PositionInfo")
posXML.attrib = {
"PositionType": "Other",
"SectorName": name,
"RadioName": attribs["rname"],
"Prefix": attribs["callprefix"],
"Suffix": attribs["callsuffix"],
"Frequency": None,
"SectorID": attribs["secid"],
"PositionSymbol": attribs["artstag"]
}
# Format frequency separately
freq = str(attribs["freq"]).replace(".", "")[1:]
posXML.attrib["Frequency"] = freq
return root


def load(file):
""" Makes Positions from a file """
# If file name, open file first
if type(file) is str:
with open(file) as f:
return loads(f.read())
# If already file obj, just use that
return loads(file.read())

def loads(text):
""" Makes Positions from a string """
if text.strip().startswith("<"):
# load xml
root = ET.fromstring(text)
pos = Positions()
for node in root.findall("PositionInfo"):
name = node.attrib["SectorName"]
rname = node.attrib["RadioName"]
prefix = node.attrib["Prefix"]
suffix = node.attrib["Suffix"]
secid = node.attrib["SectorID"]
artstag = node.attrib["PositionSymbol"]
# Format freq 120300
freq = "1" + node.attrib["Frequency"]
freq = freq[:3] + "." + freq[3:]
# Save position
pos.add(name, rname, freq, secid, artstag, prefix, suffix, "", "", "", "")
return pos
else:
# load txt (VRC)
pos = Positions()
for row in text.split("\n"):
line = row.strip()
# Skip comments and empty lines
if line.startswith(";") or line == "":
continue
# Remove same-line comments
if ";" in line:
line = line[:line.index(";")].strip()
# Parse line
name, *items = line.split(":")
pos.add(name, *items)
return pos
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

setup(
name="fetools",
version="1.2.1-post",
description="Python library for VATUSA FEs.",
version="1.3.0",
description="Makes some tasks easier for VATUSA FEs.",
long_description=long_desc,
long_description_content_type="text/markdown",
url="https://github.com/cessnahat/fetools",
Expand Down

0 comments on commit 4b1426e

Please sign in to comment.