sig->activity and multiple activity changes for API v3. #143

This commit is contained in:
ian
2026-09-25 07:01:13 +01:00
committed by Ian Renton
parent 02d08c17cd
commit ecdcbe17e9
92 changed files with 1113 additions and 796 deletions
+31 -15
View File
@@ -5,7 +5,7 @@ from dataclasses import dataclass, field
from datetime import datetime, timedelta
import pytz
from pyhamtools.locator import locator_to_latlong, latlong_to_locator
from pyhamtools.locator import latlong_to_locator, locator_to_latlong
from core.activity_lookup_helper import populate_missing_activity_ref_info
from core.activity_utils import get_icon_for_activity
@@ -66,11 +66,13 @@ class Alert:
# Activity info
# Activity (e.g. outdoor activity programme such as POTA). Still named "sig" for API backwards compatibility.
sig: str | None = None
# Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. Still named
# "sig_refs" for API backwards compatibility.
sig_refs: list = field(default_factory=list)
# Activities (e.g. outdoor activity programmes such as POTA). An alert can be for several activities at once,
# e.g. a POTA and WWFF dual activation. This is a list so we can maintain the order items were added, but needs to
# be set-like to avoid dupes, and there's no Python class that handles that properly. So we use a list, but handle
# the uniqueness logic manually, so you must use add_activity() to add to it instead of adding directly.
activities: list = field(default_factory=list)
# Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO.
activity_refs: list = field(default_factory=list)
# Timing info
@@ -93,6 +95,11 @@ class Alert:
# Icon to use when displaying this alert in the web UI. Chosen from the Font Awesome set.
icon: str | None = None
def __post_init__(self):
"""Normalise the activities list, removing any duplicates while keeping the order."""
self.activities = list(dict.fromkeys(self.activities)) if self.activities else []
def infer_missing(self, credentials=None):
"""Infer missing parameters where possible"""
@@ -131,8 +138,8 @@ class Alert:
self.dx_flag = get_flag_for_dxcc(self.dx_dxcc_id)
# Fetch activity data, and set a real position if we can get one.
if self.sig_refs:
for activity_ref in self.sig_refs:
if self.activity_refs:
for activity_ref in self.activity_refs:
activity_ref = populate_missing_activity_ref_info(activity_ref)
# If the alert itself doesn't have location yet, but the activity ref does, extract it
if activity_ref.grid and not self.dx_grid:
@@ -146,10 +153,10 @@ class Alert:
self.dx_latitude = activity_ref.latitude
self.dx_longitude = activity_ref.longitude
# If the spot itself doesn't have an activity yet, but we have at least one activity reference, take that
# reference's activity and apply it to the whole spot.
if self.sig_refs and self.sig_refs[0] and not self.sig:
self.sig = self.sig_refs[0].sig
# Add the activities of any activity refs we have to the alert's list of activities.
for activity_ref in self.activity_refs:
if activity_ref and activity_ref.activity:
self.add_activity(activity_ref.activity)
# DX Grid to lat/lon and vice versa in case one is missing
if self.dx_grid and (not self.dx_latitude or not self.dx_longitude):
@@ -180,14 +187,23 @@ class Alert:
if self.dx_calls and not self.dx_names:
self.dx_names = [get_call_info(c, credentials).name for c in self.dx_calls]
# Icon for the alert should be the icon of its activity if known, otherwise a radio tower
# Icon for the alert should be the icon of its first activity that has one, otherwise a radio tower
self.icon = "fa-tower-cell"
if self.sig and (activity_icon := get_icon_for_activity(self.sig)):
self.icon = activity_icon
for activity in self.activities:
if activity_icon := get_icon_for_activity(activity):
self.icon = activity_icon
break
except Exception:
logger.exception("Exception while inferring missing data from spot")
def add_activity(self, activity):
"""Add an activity to the activities list, so long as it's not blank and not already there. The list is kept in
insertion order, so the first activity added is treated as the "primary" one."""
if activity and activity not in self.activities:
self.activities.append(activity)
def to_json(self):
"""JSON serialise"""