././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788208997.8553312 notctyparser-26.8.31/0000755000076500000240000000000015245363546014001 5ustar00mbridakstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788206521.9543939 notctyparser-26.8.31/LICENSE0000644000076500000240000000207015245356672015007 0ustar00mbridakstaffMIT License Copyright (c) 2019-2022 classabbyamp, 0x5c Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788208997.8551736 notctyparser-26.8.31/PKG-INFO0000644000076500000240000000345415245363546015104 0ustar00mbridakstaffMetadata-Version: 2.4 Name: notctyparser Version: 26.8.31 Summary: Just ctyparser with added version check. Author-email: Michael Bridak Project-URL: Homepage, https://github.com/mbridak/ctyparser Project-URL: Bug Tracker, https://github.com/mbridak/ctyparser/issues Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Environment :: X11 Applications :: Qt Classifier: Operating System :: POSIX :: Linux Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: Topic :: Communications :: Ham Radio Requires-Python: >=3.10 Description-Content-Type: text/markdown License-File: LICENSE Requires-Dist: feedparser Requires-Dist: requests Requires-Dist: lxml Dynamic: license-file # notctyparser notctyparser is a fork and extension of [ctyparser](https://github.com/miaowware/ctyparser). I've probably screwed something up. Use the original. A CTY.DAT parser for modern amateur radio programs. [![PyPI](https://img.shields.io/pypi/v/notctyparser)](https://pypi.org/project/notctyparser/) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/notctyparser) ![PyPI - License](https://img.shields.io/pypi/l/notctyparser) ## Installation `notctyparser` requires Python 3.6 at minimum. ```none pip install notctyparser ``` ## Fork I just added a stupid check for update without doing the update. ```python def check_update(self) -> bool: ``` - Changed locale to en_US.utf8 - Used xpath to parse the download link. - Chose more narrow raised exceptions. - Specified utf-8 file encoding. - Made the linter happy. ## Copyright Copyright 2019-2022 classabbyamp, 0x5c Released under the MIT License. See `LICENSE` for the full license text. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788206521.9545088 notctyparser-26.8.31/README.md0000644000076500000240000000172215245356672015264 0ustar00mbridakstaff# notctyparser notctyparser is a fork and extension of [ctyparser](https://github.com/miaowware/ctyparser). I've probably screwed something up. Use the original. A CTY.DAT parser for modern amateur radio programs. [![PyPI](https://img.shields.io/pypi/v/notctyparser)](https://pypi.org/project/notctyparser/) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/notctyparser) ![PyPI - License](https://img.shields.io/pypi/l/notctyparser) ## Installation `notctyparser` requires Python 3.6 at minimum. ```none pip install notctyparser ``` ## Fork I just added a stupid check for update without doing the update. ```python def check_update(self) -> bool: ``` - Changed locale to en_US.utf8 - Used xpath to parse the download link. - Chose more narrow raised exceptions. - Specified utf-8 file encoding. - Made the linter happy. ## Copyright Copyright 2019-2022 classabbyamp, 0x5c Released under the MIT License. See `LICENSE` for the full license text. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1788208997.852706 notctyparser-26.8.31/docs/0000755000076500000240000000000015245363546014731 5ustar00mbridakstaff././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1788206521.955324 notctyparser-26.8.31/docs/conf.py0000644000076500000240000000445215245356672016237 0ustar00mbridakstaff# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys import miaowware_sphinx_theme sys.path.insert(0, os.path.abspath("..")) # -- Project information ----------------------------------------------------- project = "ctyparser" copyright = "2019-2022 classabbyamp, 0x5c" author = "classabbyamp, 0x5c" # The full version, including alpha/beta/rc tags release = "2.2.0" # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named "sphinx.ext.*") or your custom # ones. extensions = [ "sphinx.ext.autodoc" ] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "miaowware" html_theme_path = miaowware_sphinx_theme.get_html_theme_path() html_logo = "_static/miaowware_logo.png" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ["_static"] master_doc = "index" # -- Options for autodoc ----------------------------------------------------- autodoc_default_options = { "members": None, "member-order": "bysource", "ignore-module-all": None, } autodoc_typehints = "none" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788208997.8544893 notctyparser-26.8.31/notctyparser/0000755000076500000240000000000015245363546016536 5ustar00mbridakstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788206521.9558036 notctyparser-26.8.31/notctyparser/__info__.py0000644000076500000240000000056015245356672020642 0ustar00mbridakstaff"Package information file" # Format and usage strongly inspired by AGProjects/python-application. __project__ = "notctyparser" __summary__ = "A CTY.DAT parser for modern amateur radio programs." __webpage__ = "https://github.com/mbridak/ctyparser" __version__ = "23.6.2" __author__ = "classabbyamp, 0x5c" __email__ = "dev@kb6.ee, dev@0x5c.io" __license__ = "MIT" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788206521.9558985 notctyparser-26.8.31/notctyparser/__init__.py0000644000076500000240000000037315245356672020654 0ustar00mbridakstaff""" ctyparser --- A CTY.DAT parser for modern amateur radio programs. Copyright 2019-2022 classabbyamp, 0x5c Released under the terms of the MIT license. """ from .__info__ import __version__ # noqa: F401 from .bigcty import BigCty # noqa: F401 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788206521.9560158 notctyparser-26.8.31/notctyparser/__main__.py0000644000076500000240000000115115245356672020630 0ustar00mbridakstaff""" ctyparser commandline interface --- Copyright 2019-2022 classabbyamp, 0x5c Released under the terms of the MIT license. """ import os import pathlib import notctyparser file = pathlib.PurePath("./cty.json") try: cty = notctyparser.BigCty(file) except FileNotFoundError: cty = notctyparser.BigCty() print(f"LANG: {os.environ.get('LANG')}") print(f"LC_TIME: {os.environ.get('LC_TIME')}") print("Update:", cty.check_update()) print("Updated:", cty.update()) print("Datestamp:", cty.formatted_version) print("Version Entity:", cty.get("VERSION", "Not present, data possibly corrupted.")) cty.dump(file) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788207955.9281347 notctyparser-26.8.31/notctyparser/bigcty.py0000644000076500000240000002530715245361524020372 0ustar00mbridakstaff""" bigcty.py - part of miaowware/ctyparser --- Copyright 2019-2022 classabbyamp, 0x5c Released under the terms of the MIT license. """ import collections import copy import json import locale import os import pathlib import re import tempfile import zipfile from datetime import datetime import feedparser import requests from lxml import html DEFAULT_FEED = "http://www.country-files.com/category/big-cty/feed/" class BigCty(collections.abc.Mapping): """Class representing a BigCTY dataset. Can be initialised with data by passing the path to a valid ``cty.json`` file to the constructor. :param file_path: Location of the ``cty.json`` file to load. :type file_path: str or os.PathLike, optional :var version: the datestamp of the data, ``YYYYMMDD`` format. :vartype version: str """ regex_version_entry = re.compile(r"VER(\d{8})") regex_feed_date = re.compile(r"(\d{2}-\w+-\d{4})") regex_dat = re.compile( r"""=?(?P[a-zA-Z0-9/]+) (?:\((?P\d+)\))? (?:\[(?P\d+)\])? (?P <(?P[+-]?\d+(?:\.\d+)?) \/ (?P[+-]?\d+(?:.\d+)?)> )? (?:\{(?P\w+)\})? (?:~(?P[+-]?\d+(?:\.\d+)?)~)?""", re.VERBOSE, ) def __init__(self, file_path: str | os.PathLike | None = None): locale.setlocale(locale.LC_ALL, "en_US.utf8") self._data: dict = {} self._version = "" if file_path is not None: self.load(file_path) def load(self, cty_file: str | os.PathLike) -> None: """Loads a ``cty.json`` file into the instance. :param cty_file: Path to the file to load. :type cty_file: str or os.PathLike :return: None """ cty_file = pathlib.Path(cty_file) with cty_file.open("r", encoding="utf-8") as file: ctyjson = json.load(file) self._version = ctyjson.pop("version", None) self._data = ctyjson def dump(self, cty_file: str | os.PathLike) -> None: """Dumps the data of the instance to a ``cty.json`` file. :param cty_file: Path to the file to dump to. :type cty_file: str or os.PathLike :return: None """ cty_file = pathlib.Path(cty_file) datadump = self._data.copy() datadump["version"] = self._version with cty_file.open("w", encoding="utf-8") as file: json.dump(datadump, file) def import_dat(self, dat_file: str | os.PathLike) -> None: """Imports CTY data from a ``CTY.DAT`` file. :param dat_file: Path to the file to import. :type dat_file: str or os.PathLike :return: None """ dat_file = pathlib.Path(dat_file) with dat_file.open("r", encoding="utf-8") as file: cty_dict = {} # get the version from the file ver_match = re.search(self.regex_version_entry, file.read()) self._version = ver_match.group(1) if ver_match is not None else "" file.seek(0) # stores the previous prefix for the next iteration last = "" while True: line = ( file.readline().rstrip("\r").strip(":") ) # remove unnecessary carriage returns and colons if not line: break # check if the line introduces new DXCC if line != "" and line[0].isalpha(): # split line into fields at delimiters segments = [x.strip() for x in line.split(":")] # check if this entity is not a DXCC if segments[7][0] == "*": segments[7] = segments[7][1:] segments[0] += " (not DXCC)" cty_dict[segments[7]] = { "entity": segments[0], "cq": int(segments[1]), "itu": int(segments[2]), "continent": segments[3], "lat": float(segments[4]), "long": float(segments[5]), "tz": -1 * float(segments[6]), "len": len(segments[7]), "primary_pfx": segments[7], "exact_match": False, } # store the current prefix for the next iteration last = segments[7] # check if the line continues a DXCC elif line != "" and line[0].isspace(): overrides = line.strip().rstrip(";").rstrip(",").split(",") for item in overrides: if item not in cty_dict: # get the already stored data from primary prefix data = copy.deepcopy(cty_dict[last]) # apply regex to extract the prefix and overrides match = re.search(self.regex_dat, item) if match is None: continue if match.group("itu"): data["itu"] = int(match.group("itu")) if match.group("cq"): data["cq"] = int(match.group("cq")) if match.group("latlong"): data["lat"] = float(match.group("lat")) data["long"] = float(match.group("long")) if match.group("continent"): data["continent"] = match.group("continent") if match.group("tz"): data["tz"] = -1 * float(match.group("tz")) if item.startswith("="): data["exact_match"] = True prefix = match.group("prefix") cty_dict[prefix] = data self._data = cty_dict def check_update(self) -> bool: """Checks if an update exists. :raises AttributeError : If there is no date in the feed. :return: ``True`` if an update is available, otherwise ``False``. :rtype: bool """ with requests.Session() as session: feed = session.get(DEFAULT_FEED) parsed_feed = feedparser.parse(feed.content) update_url = parsed_feed.entries[0]["link"] date_match = re.search(self.regex_feed_date, update_url) if date_match is None: raise AttributeError("Error parsing feed: date missing") date_str = date_match.group(1).title() update_date = datetime.strftime( datetime.strptime(date_str, "%d-%B-%Y").astimezone(), "%Y%m%d" ) return self._version == update_date def update(self) -> bool: """Upates the instance's data from the feed. :raises AttributeError: If there is no date in the feed. :raises ResourceWarning: If unable to download bigcty. :return: ``True`` if an update was done, otherwise ``False``. :rtype: bool """ with requests.Session() as session: feed = session.get(DEFAULT_FEED) parsed_feed = feedparser.parse(feed.content) update_url = parsed_feed.entries[0]["link"] date_match = re.search(self.regex_feed_date, update_url) if date_match is None: raise AttributeError("Error parsing feed: date missing") date_str = date_match.group(1).title() update_date = datetime.strftime( datetime.strptime(date_str, "%d-%B-%Y").astimezone(), "%Y%m%d" ) if self._version == update_date: return False with tempfile.TemporaryDirectory() as temp: path = pathlib.PurePath(temp) page = session.get(update_url) tree = html.fromstring(page.content) urls = tree.xpath("//a[contains(@href,'zip')]/@href") if len(urls) == 0: raise ResourceWarning( f"Unable to find and download bigcty-{update_date}.zip" ) dl_url = urls[0] the_request = session.get(dl_url) if the_request.status_code == 404: dl_url = ( "http://www.country-files.com/bigcty/download/bigcty-" f"{update_date}.zip" ) the_request = session.get(dl_url) if the_request.status_code != 200: raise ResourceWarning( f"Unable to find and download bigcty-{update_date}.zip" ) with open(path / "cty.zip", "wb+") as file: file.write(the_request.content) zipfile.ZipFile(file).extract( "cty.dat", path=str(path) ) # Force cast as str because mypy self.import_dat(path / "cty.dat") return True @property def formatted_version(self) -> str: """Formatted representation of the version/date of the current BigCTY data. :getter: Returns version in ``YYYY-MM-DD`` format, or ``0000-00-00`` (if invalid date) :type: str """ try: return ( datetime.strptime(self._version, "%Y%m%d") .astimezone() .strftime("%Y-%m-%d") ) except ValueError: return "0000-00-00" @property def version(self) -> str: """The version/date of the current BigCTY data. :getter: Returns version in ``YYYYMMDD`` format :type: str """ return self._version # --- Wrappers to implement dict-like functionality --- def __len__(self): return len(self._data) def __getitem__(self, key: str): return self._data[key] def __iter__(self): return iter(self._data) # --- Standard methods we should all implement --- # str(): Simply return what it would be for the underlaying dict def __str__(self): return str(self._data) # repr(): Class name, instance ID, and last_updated def __repr__(self): return ( f"<{type(self).__module__}.{type(self).__qualname__} object" f"at {hex(id(self))}, last_updated={self.last_updated}>" ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788206521.9562678 notctyparser-26.8.31/notctyparser/py.typed0000644000076500000240000000000015245356672020225 0ustar00mbridakstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788208997.8549914 notctyparser-26.8.31/notctyparser.egg-info/0000755000076500000240000000000015245363546020230 5ustar00mbridakstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788208997.8475072 notctyparser-26.8.31/notctyparser.egg-info/PKG-INFO0000644000076500000240000000345415245363546021333 0ustar00mbridakstaffMetadata-Version: 2.4 Name: notctyparser Version: 26.8.31 Summary: Just ctyparser with added version check. Author-email: Michael Bridak Project-URL: Homepage, https://github.com/mbridak/ctyparser Project-URL: Bug Tracker, https://github.com/mbridak/ctyparser/issues Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Environment :: X11 Applications :: Qt Classifier: Operating System :: POSIX :: Linux Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: Topic :: Communications :: Ham Radio Requires-Python: >=3.10 Description-Content-Type: text/markdown License-File: LICENSE Requires-Dist: feedparser Requires-Dist: requests Requires-Dist: lxml Dynamic: license-file # notctyparser notctyparser is a fork and extension of [ctyparser](https://github.com/miaowware/ctyparser). I've probably screwed something up. Use the original. A CTY.DAT parser for modern amateur radio programs. [![PyPI](https://img.shields.io/pypi/v/notctyparser)](https://pypi.org/project/notctyparser/) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/notctyparser) ![PyPI - License](https://img.shields.io/pypi/l/notctyparser) ## Installation `notctyparser` requires Python 3.6 at minimum. ```none pip install notctyparser ``` ## Fork I just added a stupid check for update without doing the update. ```python def check_update(self) -> bool: ``` - Changed locale to en_US.utf8 - Used xpath to parse the download link. - Chose more narrow raised exceptions. - Specified utf-8 file encoding. - Made the linter happy. ## Copyright Copyright 2019-2022 classabbyamp, 0x5c Released under the MIT License. See `LICENSE` for the full license text. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788208997.8502285 notctyparser-26.8.31/notctyparser.egg-info/SOURCES.txt0000644000076500000240000000053015245363546022112 0ustar00mbridakstaffLICENSE README.md pyproject.toml docs/conf.py notctyparser/__info__.py notctyparser/__init__.py notctyparser/__main__.py notctyparser/bigcty.py notctyparser/py.typed notctyparser.egg-info/PKG-INFO notctyparser.egg-info/SOURCES.txt notctyparser.egg-info/dependency_links.txt notctyparser.egg-info/requires.txt notctyparser.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788208997.8477263 notctyparser-26.8.31/notctyparser.egg-info/dependency_links.txt0000644000076500000240000000000115245363546024276 0ustar00mbridakstaff ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1788208997.847836 notctyparser-26.8.31/notctyparser.egg-info/requires.txt0000644000076500000240000000003115245363546022622 0ustar00mbridakstafffeedparser requests lxml ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1788208997.847911 notctyparser-26.8.31/notctyparser.egg-info/top_level.txt0000644000076500000240000000002715245363546022761 0ustar00mbridakstaffdist docs notctyparser ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1788208720.5669012 notctyparser-26.8.31/pyproject.toml0000644000076500000240000000163215245363121016704 0ustar00mbridakstaff[build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta" [project] name = "notctyparser" version = "26.8.31" description = "Just ctyparser with added version check." readme = "README.md" requires-python = ">=3.10" authors = [ { name="Michael Bridak", email="michael.bridak@gmail.com" }, ] dependencies = [ "feedparser", "requests", "lxml", ] classifiers = [ "Programming Language :: Python :: 3", "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Environment :: X11 Applications :: Qt", "Operating System :: POSIX :: Linux", "Intended Audience :: Developers", "Natural Language :: English", "Topic :: Communications :: Ham Radio", ] [project.urls] "Homepage" = "https://github.com/mbridak/ctyparser" "Bug Tracker" = "https://github.com/mbridak/ctyparser/issues" [tool.setuptools.packages.find] where = ["."] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1788208997.855367 notctyparser-26.8.31/setup.cfg0000644000076500000240000000004615245363546015622 0ustar00mbridakstaff[egg_info] tag_build = tag_date = 0