mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2025-10-27 08:49:27 +00:00
Starting to implement alerts #17
This commit is contained in:
111
data/alert.py
Normal file
111
data/alert.py
Normal file
@@ -0,0 +1,111 @@
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
import pytz
|
||||
|
||||
from core.constants import DXCC_FLAGS
|
||||
from core.utils import infer_continent_from_callsign, \
|
||||
infer_country_from_callsign, infer_cq_zone_from_callsign, infer_itu_zone_from_callsign, infer_dxcc_id_from_callsign, \
|
||||
infer_name_from_callsign
|
||||
|
||||
|
||||
# Data class that defines an alert.
|
||||
@dataclass
|
||||
class Alert:
|
||||
# Unique identifier for the alert
|
||||
id: int = None
|
||||
# Callsign of the operator that has been alertted
|
||||
dx_call: str = None
|
||||
# Name of the operator that has been alertted
|
||||
dx_name: str = None
|
||||
# Country of the DX operator
|
||||
dx_country: str = None
|
||||
# Country flag of the DX operator
|
||||
dx_flag: str = None
|
||||
# Continent of the DX operator
|
||||
dx_continent: str = None
|
||||
# DXCC ID of the DX operator
|
||||
dx_dxcc_id: int = None
|
||||
# CQ zone of the DX operator
|
||||
dx_cq_zone: int = None
|
||||
# ITU zone of the DX operator
|
||||
dx_itu_zone: int = None
|
||||
# Intended frequencies & modes of operation. Essentially just a different kind of comment field.
|
||||
freqs_modes: str = None
|
||||
# Start time of the activation, UTC seconds since UNIX epoch
|
||||
start_time: float = None
|
||||
# Start time of the activation of the alert, ISO 8601
|
||||
start_time_iso: str = None
|
||||
# End time of the activation, UTC seconds since UNIX epoch. Optional
|
||||
end_time: float = None
|
||||
# End time of the activation of the alert, ISO 8601
|
||||
end_time_iso: str = None
|
||||
# Time that this software received the alert, UTC seconds since UNIX epoch. This is used with the "since_received"
|
||||
# call to our API to receive all data that is new to us, even if by a quirk of the API it might be older than the
|
||||
# list time the client polled the API.
|
||||
received_time: float = None
|
||||
# Time that this software received the alert, ISO 8601
|
||||
received_time_iso: str = None
|
||||
# Comment made by the alerter, if any
|
||||
comment: str = None
|
||||
# Special Interest Group (SIG), e.g. outdoor activity programme such as POTA
|
||||
sig: str = None
|
||||
# SIG references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO
|
||||
sig_refs: list = None
|
||||
# SIG reference names
|
||||
sig_refs_names: list = None
|
||||
# Activation score. SOTA only
|
||||
activation_score: int = None
|
||||
# Icon, from the Font Awesome set. This is fairly opinionated but is here to help the alerthole web UI and Field alertter. Does not include the "fa-" prefix.
|
||||
icon: str = "question"
|
||||
# Where we got the alert from, e.g. "POTA", "SOTA"...
|
||||
source: str = None
|
||||
# The ID the source gave it, if any.
|
||||
source_id: str = None
|
||||
|
||||
# Infer missing parameters where possible
|
||||
def infer_missing(self):
|
||||
# If we somehow don't have a start time, set it to zero so it sorts off the bottom of any list but
|
||||
# clients can still reliably parse it as a number.
|
||||
if not self.start_time:
|
||||
self.start_time = 0
|
||||
|
||||
# If we don't have a received time, this has just been received so set that to "now"
|
||||
if not self.received_time:
|
||||
self.received_time = datetime.now(pytz.UTC).timestamp()
|
||||
|
||||
# Fill in ISO versions of times, in case the client prefers that
|
||||
if self.start_time and not self.start_time_iso:
|
||||
self.start_time_iso = datetime.fromtimestamp(self.start_time, pytz.UTC).isoformat()
|
||||
if self.end_time and not self.end_time_iso:
|
||||
self.end_time_iso = datetime.fromtimestamp(self.end_time, pytz.UTC).isoformat()
|
||||
if self.received_time and not self.received_time_iso:
|
||||
self.received_time_iso = datetime.fromtimestamp(self.received_time, pytz.UTC).isoformat()
|
||||
|
||||
# DX country, continent, zones etc. from callsign
|
||||
if self.dx_call and not self.dx_country:
|
||||
self.dx_country = infer_country_from_callsign(self.dx_call)
|
||||
if self.dx_call and not self.dx_continent:
|
||||
self.dx_continent = infer_continent_from_callsign(self.dx_call)
|
||||
if self.dx_call and not self.dx_cq_zone:
|
||||
self.dx_cq_zone = infer_cq_zone_from_callsign(self.dx_call)
|
||||
if self.dx_call and not self.dx_itu_zone:
|
||||
self.dx_itu_zone = infer_itu_zone_from_callsign(self.dx_call)
|
||||
if self.dx_call and not self.dx_dxcc_id:
|
||||
self.dx_dxcc_id = infer_dxcc_id_from_callsign(self.dx_call)
|
||||
if self.dx_dxcc_id and not self.dx_flag:
|
||||
self.dx_flag = DXCC_FLAGS[self.dx_dxcc_id]
|
||||
|
||||
# DX operator details lookup, using QRZ.com. This should be the last resort compared to taking the data from
|
||||
# the actual alertting service, e.g. we don't want to accidentally use a user's QRZ.com home lat/lon instead of
|
||||
# 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)
|
||||
|
||||
# Always create an ID based on a hashcode
|
||||
self.id = hash(str(self))
|
||||
|
||||
# JSON serialise
|
||||
def to_json(self):
|
||||
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)
|
||||
11
data/spot.py
11
data/spot.py
@@ -1,5 +1,4 @@
|
||||
import json
|
||||
import uuid
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
@@ -16,8 +15,8 @@ from core.utils import infer_mode_type_from_mode, infer_band_from_freq, infer_co
|
||||
# Data class that defines a spot.
|
||||
@dataclass
|
||||
class Spot:
|
||||
# Globally unique identifier for the spot
|
||||
guid: str = None
|
||||
# Unique identifier for the spot
|
||||
id: int = None
|
||||
# Callsign of the operator that has been spotted
|
||||
dx_call: str = None
|
||||
# Callsign of the operator that has spotted them
|
||||
@@ -97,9 +96,6 @@ class Spot:
|
||||
|
||||
# Infer missing parameters where possible
|
||||
def infer_missing(self):
|
||||
# Always create a GUID
|
||||
self.guid = str(uuid.uuid4())
|
||||
|
||||
# If we somehow don't have a spot time, set it to zero so it sorts off the bottom of any list but
|
||||
# clients can still reliably parse it as a number.
|
||||
if not self.time:
|
||||
@@ -211,6 +207,9 @@ class Spot:
|
||||
# is likely at home.
|
||||
self.location_good = self.location_source == "SPOT" or (self.location_source == "QRZ" and not "/" in self.dx_call)
|
||||
|
||||
# Always create an ID based on a hashcode
|
||||
self.id = hash(str(self))
|
||||
|
||||
# JSON serialise
|
||||
def to_json(self):
|
||||
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)
|
||||
Reference in New Issue
Block a user