Add NG3K DXpedition calendar #17

This commit is contained in:
Ian Renton
2025-10-09 17:03:42 +01:00
parent 277743dac7
commit b27d0f826c
6 changed files with 103 additions and 16 deletions

73
alertproviders/ng3k.py Normal file
View File

@@ -0,0 +1,73 @@
from datetime import datetime
import pytz
from rss_parser import RSSParser
from alertproviders.http_alert_provider import HTTPAlertProvider
from data.alert import Alert
# Alert provider NG3K DXpedition list
class NG3K(HTTPAlertProvider):
POLL_INTERVAL_SEC = 3600
ALERTS_URL = "https://www.ng3k.com/adxo.xml"
def __init__(self, provider_config):
super().__init__(provider_config, self.ALERTS_URL, self.POLL_INTERVAL_SEC)
def http_response_to_alerts(self, http_response):
new_alerts = []
rss = RSSParser.parse(http_response.content.decode())
# Iterate through source data
for source_alert in rss.channel.items:
# Deal with "the format"...
parts = source_alert.description.split(" --\n")
start_string = parts[0].split("-")[0]
end_string = parts[0].split("-")[1]
end_year = end_string.split(", ")[1].strip()
if ", " in start_string:
start_year = start_string.split(", ")[1].strip()
start_mon = start_string.split(", ")[0][0:3].strip()
start_day = start_string.split(", ")[0][4:].strip()
else:
start_year = end_year
start_mon = start_string[0:3].strip()
start_day = start_string[4:].strip()
if " " in end_string.split(", ")[0]:
end_mon = end_string.split(", ")[0].split(" ")[0].strip()
end_day = end_string.split(", ")[0].split(" ")[1].strip()
else:
end_day = end_string.split(", ")[0].strip()
end_mon = start_mon
start_timestamp = datetime.strptime(start_year + " " + start_mon + " " + start_day, "%Y %b %d").replace(
tzinfo=pytz.UTC).timestamp()
end_timestamp = datetime.strptime(end_year + " " + end_mon + " " + end_day + " 23:59", "%Y %b %d %H:%M").replace(
tzinfo=pytz.UTC).timestamp()
dx_country = parts[1]
dx_call = parts[2]
qsl_info = parts[3]
extra_parts = parts[5].split("; ")
by = extra_parts[0]
bands = extra_parts[1]
modes = extra_parts[2] if len(extra_parts) > 3 else ""
comment = extra_parts[-1]
# Convert to our alert format
alert = Alert(source=self.name,
dx_call=dx_call.upper(),
dx_country=dx_country,
freqs_modes=bands + (("; " + modes) if modes != "" else ""),
comment=by + "; " + comment + "; " + qsl_info,
icon="globe-africa",
start_time=start_timestamp,
end_time=end_timestamp)
# Add to our list.
new_alerts.append(alert)
return new_alerts

View File

@@ -84,6 +84,10 @@ alert-providers:
class: "WWFF"
name: "WWFF"
enabled: true
-
class: "NG3K"
name: "NG3K"
enabled: true
# Port to open the local web server on
web-server-port: 8080

View File

@@ -9,3 +9,4 @@ aprslib~=0.7.2
diskcache~=5.6.3
psutil~=7.1.0
requests-sse~=0.5.2
rss-parser~=2.1.1

View File

@@ -10,7 +10,7 @@ info:
license:
name: The Unlicense
url: https://unlicense.org/#the-unlicense
version: 1
version: v1
servers:
- url: https://spothole.app/api/v1
paths:

View File

@@ -27,7 +27,7 @@ function buildQueryString() {
});
str = str + "limit=" + $("#alerts-to-fetch option:selected").val();
var maxDur = $("#max-duration option:selected").val();
if (maxDur != "") {
if (maxDur != "9999999999") {
str = str + "&max_duration=" + maxDur;
}
return str;
@@ -89,7 +89,8 @@ function addAlertRowsToTable(tbody, alerts) {
var useLocalTime = $("#useLocalTime")[0].checked;
// Get times for the alert, and convert to local time if necessary.
var start_time = moment.unix(a["start_time"]).utc();
var start_time_unix = moment.unix(a["start_time"]);
var start_time = start_time_unix.utc();
if (useLocalTime) {
start_time = start_time.local();
}
@@ -103,21 +104,27 @@ function addAlertRowsToTable(tbody, alerts) {
// different year to the current year, in which case the year is inserted between month and hour.
// If the time is set to local not UTC, and the date in local time is "today", we display that instead.
// End time is displayed the same as above, except if the end date is the same as the start date, in which case
// just e.g. 23:45 is used. Finally, if there is no end date set, "---" is displayed.
var start_time_formatted = start_time.format("D MMM HH:mm");
// just e.g. 23:45 is used.
// Overriding all of that, if the start time is 00:00 and the end time is 23:59 when considered in UTC, the
// hours and minutes are stripped out from the display, as we assume the server is just giving us full days.
// Finally, if there is no end date set, "---" is displayed.
var whole_days = start_time_unix.utc().format("HH:mm") == "00:00" &&
(end_time_unix != null || end_time_unix > 0 || end_time_unix.utc().format("HH:mm") == "23:59");
var hours_minutes_format = whole_days ? "" : " HH:mm";
var start_time_formatted = start_time.format("D MMM" + hours_minutes_format);
if (start_time.format("YYYY") != moment().format("YYYY")) {
start_time_formatted = start_time.format("D MMM YYYY HH:mm");
start_time_formatted = start_time.format("D MMM YYYY" + hours_minutes_format);
} else if (useLocalTime && start_time.format("D MMM YYYY") == moment().format("D MMM YYYY")) {
start_time_formatted = start_time.format("[Today] HH:mm");
start_time_formatted = start_time.format("[Today]" + hours_minutes_format);
}
var end_time_formatted = "---";
if (end_time_unix != null && end_time_unix > 0 && end_time != null) {
var end_time_formatted = end_time.format("HH:mm");
var end_time_formatted = whole_days ? start_time_formatted : end_time.format("HH:mm");
if (end_time.format("D MMM") != start_time.format("D MMM")) {
if (end_time.format("YYYY") != moment().format("YYYY")) {
end_time_formatted = end_time.format("D MMM YYYY HH:mm");
end_time_formatted = end_time.format("D MMM YYYY" + hours_minutes_format);
} else {
end_time_formatted = end_time.format("D MMM HH:mm");
end_time_formatted = end_time.format("D MMM" + hours_minutes_format);
}
}
}
@@ -212,7 +219,7 @@ function generateMaxDurationDropdownFilterCard(band_options) {
<option value="86400" selected>24 hours</option>
<option value="604800">1 week</option>
<option value="2419200">4 weeks</option>
<option value="">No limit</option>
<option value="9999999999">No limit</option>
</select>`);
$p.append(" <i class='fa-solid fa-circle-question' title='Some users create long-duration alerts for the period they will be generally in and around xOTA references, not just the times they are specifically on the air. Use this control to restrict the maximum duration of spots that the software will display, and exclude any with a long duration.'></i>");
// Compile HTML elements to return

View File

@@ -108,10 +108,10 @@ function saveSettings() {
// Find all storeable UI elements, store a key of "element id:property name" mapped to the value of that
// property. For a checkbox, that's the "checked" property.
$(".storeable-checkbox").each(function() {
localStorage.setItem($(this)[0].id + ":checked", $(this)[0].checked);
localStorage.setItem("#" + $(this)[0].id + ":checked", $(this)[0].checked);
});
$(".storeable-select").each(function() {
localStorage.setItem($(this)[0].id + ":value", $(this)[0].value);
localStorage.setItem("#" + $(this)[0].id + ":value", $(this)[0].value);
});
}
@@ -119,8 +119,10 @@ function saveSettings() {
function loadSettings() {
// Find all local storage entries and push their data to the corresponding UI element
Object.keys(localStorage).forEach(function(key) {
// Split the key back into an element ID and a property
var split = key.split(":");
$("#" + split[0]).prop(split[1], JSON.parse(localStorage.getItem(key)));
if (key.startsWith("#") && key.includes(":")) {
// Split the key back into an element ID and a property
var split = key.split(":");
$(split[0]).prop(split[1], JSON.parse(localStorage.getItem(key)));
}
});
}