mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2025-10-27 08:49:27 +00:00
Add QRZ lookup
This commit is contained in:
@@ -10,3 +10,7 @@ web-server-port: 8080
|
|||||||
|
|
||||||
# Maximum spot age to keep in the system before deleting it
|
# Maximum spot age to keep in the system before deleting it
|
||||||
max-spot-age-sec: 3600
|
max-spot-age-sec: 3600
|
||||||
|
|
||||||
|
# Login for QRZ.com to look up information. Optional.
|
||||||
|
qrz-username: "N0CALL"
|
||||||
|
qrz-password: ""
|
||||||
@@ -3,12 +3,17 @@ from datetime import datetime
|
|||||||
|
|
||||||
from pyhamtools import LookupLib, Callinfo
|
from pyhamtools import LookupLib, Callinfo
|
||||||
|
|
||||||
|
from core.config import config
|
||||||
from core.constants import BANDS, UNKNOWN_BAND, CW_MODES, PHONE_MODES, DATA_MODES, ALL_MODES
|
from core.constants import BANDS, UNKNOWN_BAND, CW_MODES, PHONE_MODES, DATA_MODES, ALL_MODES
|
||||||
|
|
||||||
# Static lookup helpers from pyhamtools
|
# Lookup helpers from pyhamtools
|
||||||
# todo in future add QRZ as a second lookup option in case it provides more data?
|
LOOKUP_LIB_BASIC = LookupLib(lookuptype="countryfile")
|
||||||
lookuplib = LookupLib(lookuptype="countryfile")
|
CALL_INFO_BASIC = Callinfo(LOOKUP_LIB_BASIC)
|
||||||
callinfo = Callinfo(lookuplib)
|
QRZ_AVAILABLE = config["qrz-password"] != ""
|
||||||
|
if QRZ_AVAILABLE:
|
||||||
|
LOOKUP_LIB_QRZ = LookupLib(lookuptype="qrz", username=config["qrz-username"], pwd=config["qrz-password"])
|
||||||
|
# Cache of QRZ.com callsign lookups, so we don't repeatedly call the API for stuff we already know
|
||||||
|
QRZ_CALLSIGN_DATA_CACHE = {}
|
||||||
|
|
||||||
# Infer a mode from the comment
|
# Infer a mode from the comment
|
||||||
def infer_mode_from_comment(comment):
|
def infer_mode_from_comment(comment):
|
||||||
@@ -40,38 +45,82 @@ def infer_band_from_freq(freq):
|
|||||||
# Infer a country name from a callsign
|
# Infer a country name from a callsign
|
||||||
def infer_country_from_callsign(call):
|
def infer_country_from_callsign(call):
|
||||||
try:
|
try:
|
||||||
return callinfo.get_country_name(call)
|
return CALL_INFO_BASIC.get_country_name(call)
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Infer a DXCC ID from a callsign
|
# Infer a DXCC ID from a callsign
|
||||||
def infer_dxcc_id_from_callsign(call):
|
def infer_dxcc_id_from_callsign(call):
|
||||||
try:
|
try:
|
||||||
return callinfo.get_adif_id(call)
|
return CALL_INFO_BASIC.get_adif_id(call)
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Infer a continent shortcode from a callsign
|
# Infer a continent shortcode from a callsign
|
||||||
def infer_continent_from_callsign(call):
|
def infer_continent_from_callsign(call):
|
||||||
try:
|
try:
|
||||||
return callinfo.get_continent(call)
|
return CALL_INFO_BASIC.get_continent(call)
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Infer a CQ zone from a callsign
|
# Infer a CQ zone from a callsign
|
||||||
def infer_cq_zone_from_callsign(call):
|
def infer_cq_zone_from_callsign(call):
|
||||||
try:
|
try:
|
||||||
return callinfo.get_cqz(call)
|
return CALL_INFO_BASIC.get_cqz(call)
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Infer a ITU zone from a callsign
|
# Infer a ITU zone from a callsign
|
||||||
def infer_itu_zone_from_callsign(call):
|
def infer_itu_zone_from_callsign(call):
|
||||||
try:
|
try:
|
||||||
return callinfo.get_ituz(call)
|
return CALL_INFO_BASIC.get_ituz(call)
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Utility method to get QRZ.com data from cache if possible, if not get it from the API and cache it
|
||||||
|
def get_qrz_data_for_callsign(call):
|
||||||
|
# Fetch from cache if we can, otherwise fetch from the API and cache it
|
||||||
|
if call in QRZ_CALLSIGN_DATA_CACHE:
|
||||||
|
return QRZ_CALLSIGN_DATA_CACHE[call]
|
||||||
|
elif QRZ_AVAILABLE:
|
||||||
|
try:
|
||||||
|
data = LOOKUP_LIB_QRZ.lookup_callsign(callsign=call)
|
||||||
|
QRZ_CALLSIGN_DATA_CACHE[call] = data
|
||||||
|
return data
|
||||||
|
except KeyError:
|
||||||
|
# QRZ had no info for the call, that's OK
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Infer an operator name from a callsign (requires QRZ.com)
|
||||||
|
def infer_name_from_callsign(call):
|
||||||
|
data = get_qrz_data_for_callsign(call)
|
||||||
|
if data and "fname" in data:
|
||||||
|
name = data["fname"]
|
||||||
|
if "name" in data:
|
||||||
|
name = name + " " + data["name"]
|
||||||
|
return name
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Infer a latitude and longitude from a callsign (requires QRZ.com)
|
||||||
|
def infer_latlon_from_callsign(call):
|
||||||
|
data = get_qrz_data_for_callsign(call)
|
||||||
|
if data and "latitude" in data and "longitude" in data:
|
||||||
|
return [data["longitude"], data["longitude"]]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Infer a grid locator from a callsign (requires QRZ.com)
|
||||||
|
def infer_grid_from_callsign(call):
|
||||||
|
data = get_qrz_data_for_callsign(call)
|
||||||
|
if data and "locator" in data:
|
||||||
|
return data["locator"]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# Convert objects to serialisable things. Used by JSON serialiser as a default when it encounters unserializable things.
|
# Convert objects to serialisable things. Used by JSON serialiser as a default when it encounters unserializable things.
|
||||||
# Converts datetimes to ISO.
|
# Converts datetimes to ISO.
|
||||||
# Anything else it tries to convert to a dict.
|
# Anything else it tries to convert to a dict.
|
||||||
|
|||||||
17
data/spot.py
17
data/spot.py
@@ -8,7 +8,7 @@ from pyhamtools.locator import locator_to_latlong, latlong_to_locator
|
|||||||
from core.constants import DXCC_FLAGS
|
from core.constants import DXCC_FLAGS
|
||||||
from core.utils import infer_mode_family_from_mode, infer_band_from_freq, infer_continent_from_callsign, \
|
from core.utils import infer_mode_family_from_mode, infer_band_from_freq, infer_continent_from_callsign, \
|
||||||
infer_country_from_callsign, infer_cq_zone_from_callsign, infer_itu_zone_from_callsign, infer_dxcc_id_from_callsign, \
|
infer_country_from_callsign, infer_cq_zone_from_callsign, infer_itu_zone_from_callsign, infer_dxcc_id_from_callsign, \
|
||||||
infer_mode_from_comment
|
infer_mode_from_comment, infer_name_from_callsign, infer_latlon_from_callsign, infer_grid_from_callsign
|
||||||
|
|
||||||
|
|
||||||
# Data class that defines a spot.
|
# Data class that defines a spot.
|
||||||
@@ -130,9 +130,18 @@ class Spot:
|
|||||||
if self.comment and not self.qrt:
|
if self.comment and not self.qrt:
|
||||||
self.qrt = "QRT" in self.comment.upper()
|
self.qrt = "QRT" in self.comment.upper()
|
||||||
|
|
||||||
# TODO use QRZ/HamQTH provider to get grids, lat Lon, when missing; and DX name
|
# DX operator details lookup, using QRZ.com. This should be the last resort compared to taking the data from
|
||||||
# credentials in config file which is .gitignored; sample provided
|
# the actual spotting service, e.g. we don't want to accidentally use a user's QRZ.com home lat/lon instead of
|
||||||
# TODO lat/lon from DXCC centre as last resort?
|
# the one from the park reference they're at.
|
||||||
|
if self.dx_call and not self.dx_name:
|
||||||
|
self.dx_name = infer_name_from_callsign(self.dx_call)
|
||||||
|
if self.dx_call and not self.latitude:
|
||||||
|
latlon = infer_latlon_from_callsign(self.dx_call)
|
||||||
|
if latlon:
|
||||||
|
self.latitude = latlon[0]
|
||||||
|
self.longitude = latlon[1]
|
||||||
|
if self.dx_call and not self.grid:
|
||||||
|
self.grid = infer_grid_from_callsign(self.dx_call)
|
||||||
|
|
||||||
# JSON serialise
|
# JSON serialise
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
|
|||||||
19
main.py
19
main.py
@@ -33,11 +33,6 @@ if __name__ == '__main__':
|
|||||||
# Set up logging
|
# Set up logging
|
||||||
root = logging.getLogger()
|
root = logging.getLogger()
|
||||||
root.setLevel(logging.INFO)
|
root.setLevel(logging.INFO)
|
||||||
handler = logging.StreamHandler(sys.stdout)
|
|
||||||
handler.setLevel(logging.INFO)
|
|
||||||
formatter = logging.Formatter("%(message)s")
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
root.addHandler(handler)
|
|
||||||
logging.info("Starting...")
|
logging.info("Starting...")
|
||||||
|
|
||||||
# Shut down gracefully on SIGINT
|
# Shut down gracefully on SIGINT
|
||||||
@@ -45,13 +40,13 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Create providers
|
# Create providers
|
||||||
providers = [
|
providers = [
|
||||||
POTA(),
|
# POTA(),
|
||||||
SOTA(),
|
# SOTA(),
|
||||||
WWFF(),
|
# WWFF(),
|
||||||
WWBOTA(),
|
# WWBOTA(),
|
||||||
GMA(),
|
# GMA(),
|
||||||
HEMA(),
|
# HEMA(),
|
||||||
ParksNPeaks(),
|
# ParksNPeaks(),
|
||||||
DXCluster("hrd.wa9pie.net", 8000),
|
DXCluster("hrd.wa9pie.net", 8000),
|
||||||
# DXCluster("dxc.w3lpl.net", 22)
|
# DXCluster("dxc.w3lpl.net", 22)
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user