mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-03-15 12:24:29 +00:00
Compare commits
4 Commits
1.1
...
abdf8d3065
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abdf8d3065 | ||
|
|
67b9c3bc50 | ||
|
|
9b3536d740 | ||
|
|
897901e105 |
@@ -20,27 +20,29 @@ class BOTA(HTTPAlertProvider):
|
||||
new_alerts = []
|
||||
# Find the table of upcoming alerts
|
||||
bs = BeautifulSoup(http_response.content.decode(), features="lxml")
|
||||
tbody = bs.body.find('div', attrs={'class': 'view-activations-public'}).find('table', attrs={'class': 'views-table'}).find('tbody')
|
||||
for row in tbody.find_all('tr'):
|
||||
cells = row.find_all('td')
|
||||
first_cell_text = str(cells[0].find('a').contents[0]).strip()
|
||||
ref_name = first_cell_text.split(" by ")[0]
|
||||
dx_call = str(cells[1].find('a').contents[0]).strip().upper()
|
||||
forthcoming_activations_div = bs.body.find('div', attrs={'class': 'view-activations-public'})
|
||||
if forthcoming_activations_div:
|
||||
tbody = forthcoming_activations_div.find('table', attrs={'class': 'views-table'}).find('tbody')
|
||||
for row in tbody.find_all('tr'):
|
||||
cells = row.find_all('td')
|
||||
first_cell_text = str(cells[0].find('a').contents[0]).strip()
|
||||
ref_name = first_cell_text.split(" by ")[0]
|
||||
dx_call = str(cells[1].find('a').contents[0]).strip().upper()
|
||||
|
||||
# Get the date, dealing with the fact we get no year so have to figure out if it's last year or next year
|
||||
date_text = str(cells[2].find('span').contents[0]).strip()
|
||||
date_time = datetime.strptime(date_text,"%d %b - %H:%M UTC").replace(tzinfo=pytz.UTC)
|
||||
date_time = date_time.replace(year=datetime.now(pytz.UTC).year)
|
||||
# If this was more than a day ago, activation is actually next year
|
||||
if date_time < datetime.now(pytz.UTC) - timedelta(days=1):
|
||||
date_time = date_time.replace(year=datetime.now(pytz.UTC).year + 1)
|
||||
# Get the date, dealing with the fact we get no year so have to figure out if it's last year or next year
|
||||
date_text = str(cells[2].find('span').contents[0]).strip()
|
||||
date_time = datetime.strptime(date_text,"%d %b - %H:%M UTC").replace(tzinfo=pytz.UTC)
|
||||
date_time = date_time.replace(year=datetime.now(pytz.UTC).year)
|
||||
# If this was more than a day ago, activation is actually next year
|
||||
if date_time < datetime.now(pytz.UTC) - timedelta(days=1):
|
||||
date_time = date_time.replace(year=datetime.now(pytz.UTC).year + 1)
|
||||
|
||||
# Convert to our alert format
|
||||
alert = Alert(source=self.name,
|
||||
dx_calls=[dx_call],
|
||||
sig_refs=[SIGRef(id=ref_name, sig="BOTA")],
|
||||
start_time=date_time.timestamp(),
|
||||
is_dxpedition=False)
|
||||
# Convert to our alert format
|
||||
alert = Alert(source=self.name,
|
||||
dx_calls=[dx_call],
|
||||
sig_refs=[SIGRef(id=ref_name, sig="BOTA")],
|
||||
start_time=date_time.timestamp(),
|
||||
is_dxpedition=False)
|
||||
|
||||
new_alerts.append(alert)
|
||||
new_alerts.append(alert)
|
||||
return new_alerts
|
||||
|
||||
@@ -4,7 +4,7 @@ from data.sig import SIG
|
||||
|
||||
# General software
|
||||
SOFTWARE_NAME = "Spothole by M0TRT"
|
||||
SOFTWARE_VERSION = "1.1"
|
||||
SOFTWARE_VERSION = "1.2-pre"
|
||||
|
||||
# HTTP headers used for spot providers that use HTTP
|
||||
HTTP_HEADERS = {"User-Agent": SOFTWARE_NAME + ", v" + SOFTWARE_VERSION + " (operated by " + SERVER_OWNER_CALLSIGN + ")"}
|
||||
@@ -36,10 +36,25 @@ SIGS = [
|
||||
# Modes. Note "DIGI" and "DIGITAL" are also supported but are normalised into "DATA".
|
||||
CW_MODES = ["CW"]
|
||||
PHONE_MODES = ["PHONE", "SSB", "USB", "LSB", "AM", "FM", "DV", "DMR", "DSTAR", "C4FM", "M17"]
|
||||
DATA_MODES = ["DATA", "FT8", "FT4", "RTTY", "SSTV", "JS8", "HELL", "BPSK", "PSK", "PSK31", "BPSK31", "OLIVIA", "MFSK", "MFSK32", "PKT", "MSK144"]
|
||||
DATA_MODES = ["DATA", "FT8", "FT4", "RTTY", "SSTV", "JS8", "HELL", "PSK", "OLIVIA", "PKT", "MSK144"]
|
||||
ALL_MODES = CW_MODES + PHONE_MODES + DATA_MODES
|
||||
MODE_TYPES = ["CW", "PHONE", "DATA"]
|
||||
|
||||
# Mode aliases. Sometimes we get spots with a mode described in a different way that is effectively the same as a mode
|
||||
# we already know, or we want to normalise things for consistency. The lookup table for this is here. Incoming spots
|
||||
# that match a key in this table will be converted to the corresponding value, so only the modes above will actually be
|
||||
# present in the spots.
|
||||
MODE_ALIASES = {
|
||||
"RTT": "RTTY",
|
||||
"BPSK": "PSK",
|
||||
"PSK31": "PSK",
|
||||
"BPSK31": "PSK",
|
||||
"MFSK": "FSK",
|
||||
"MFSK32": "FSK",
|
||||
"DIGI": "DATA",
|
||||
"DIGITAL": "DATA"
|
||||
}
|
||||
|
||||
# Band definitions
|
||||
BANDS = [
|
||||
Band(name="2200m", start_freq=135700, end_freq=137800),
|
||||
|
||||
@@ -16,7 +16,7 @@ from requests_cache import CachedSession
|
||||
from core.cache_utils import SEMI_STATIC_URL_DATA_CACHE
|
||||
from core.config import config
|
||||
from core.constants import BANDS, UNKNOWN_BAND, CW_MODES, PHONE_MODES, DATA_MODES, ALL_MODES, \
|
||||
HTTP_HEADERS, HAMQTH_PRG
|
||||
HTTP_HEADERS, HAMQTH_PRG, MODE_ALIASES
|
||||
|
||||
|
||||
# Singleton class that provides lookup functionality.
|
||||
@@ -160,6 +160,9 @@ class LookupHelper:
|
||||
for mode in ALL_MODES:
|
||||
if mode in comment.upper():
|
||||
return mode
|
||||
for mode in MODE_ALIASES.keys():
|
||||
if mode in comment.upper():
|
||||
return MODE_ALIASES[mode]
|
||||
return None
|
||||
|
||||
# Infer a "mode family" from a mode.
|
||||
|
||||
@@ -10,6 +10,7 @@ import pytz
|
||||
from pyhamtools.locator import locator_to_latlong, latlong_to_locator
|
||||
|
||||
from core.config import MAX_SPOT_AGE
|
||||
from core.constants import MODE_ALIASES
|
||||
from core.lookup_helper import lookup_helper
|
||||
from core.sig_utils import populate_sig_ref_info, ANY_SIG_REGEX, get_ref_regex_for_sig
|
||||
from data.sig_ref import SIGRef
|
||||
@@ -213,10 +214,9 @@ class Spot:
|
||||
self.mode = lookup_helper.infer_mode_from_frequency(self.freq)
|
||||
self.mode_source = "BANDPLAN"
|
||||
|
||||
# Normalise "generic digital" modes. "DIGITAL", "DIGI" and "DATA" are just the same thing with no extra
|
||||
# information, so standardise on "DATA"
|
||||
if self.mode == "DIGI" or self.mode == "DIGITAL":
|
||||
self.mode = "DATA"
|
||||
# Normalise mode if necessary.
|
||||
if self.mode in MODE_ALIASES:
|
||||
self.mode = MODE_ALIASES[self.mode]
|
||||
|
||||
# Mode type from mode
|
||||
if self.mode and not self.mode_type:
|
||||
|
||||
@@ -46,7 +46,7 @@ class SOTA(HTTPSpotProvider):
|
||||
comment=source_spot["comments"],
|
||||
sig="SOTA",
|
||||
sig_refs=[SIGRef(id=source_spot["summitCode"], sig="SOTA", name=source_spot["summitName"], activation_score=source_spot["points"])],
|
||||
time=datetime.fromisoformat(source_spot["timeStamp"]).timestamp())
|
||||
time=datetime.fromisoformat(source_spot["timeStamp"].replace("Z", "+00:00")).timestamp())
|
||||
|
||||
# Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do
|
||||
# that for us.
|
||||
|
||||
@@ -30,7 +30,7 @@ class WWBOTA(SSESpotProvider):
|
||||
comment=source_spot["comment"],
|
||||
sig="WWBOTA",
|
||||
sig_refs=refs,
|
||||
time=datetime.fromisoformat(source_spot["time"]).timestamp(),
|
||||
time=datetime.fromisoformat(source_spot["time"].replace("Z", "+00:00")).timestamp(),
|
||||
# WWBOTA spots can contain multiple references for bunkers being activated simultaneously. For
|
||||
# now, we will just pick the first one to use as our grid, latitude and longitude.
|
||||
dx_grid=source_spot["references"][0]["locator"],
|
||||
|
||||
@@ -35,7 +35,7 @@ class ZLOTA(HTTPSpotProvider):
|
||||
comment=source_spot["comments"],
|
||||
sig="ZLOTA",
|
||||
sig_refs=[SIGRef(id=source_spot["reference"], sig="ZLOTA", name=source_spot["name"])],
|
||||
time=datetime.fromisoformat(source_spot["referenced_time"]).astimezone(pytz.UTC).timestamp())
|
||||
time=datetime.fromisoformat(source_spot["referenced_time"].replace("Z", "+00:00")).astimezone(pytz.UTC).timestamp())
|
||||
|
||||
new_spots.append(spot)
|
||||
return new_spots
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
<p class="d-inline-flex gap-1">
|
||||
<span class="btn-group" role="group">
|
||||
<input type="radio" class="btn-check" name="runPause" id="runButton" autocomplete="off" checked>
|
||||
<label class="btn btn-outline-primary" for="runButton"><i class="fa-solid fa-play"></i> Run</label>
|
||||
<label class="btn btn-outline-primary" for="runButton"><i class="fa-solid fa-play"></i><span class="hideonmobile"> Run</span></label>
|
||||
|
||||
<input type="radio" class="btn-check" name="runPause" id="pauseButton" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="pauseButton"><i class="fa-solid fa-pause"></i> Pause</label>
|
||||
<label class="btn btn-outline-primary" for="pauseButton"><i class="fa-solid fa-pause"></i><span class="hideonmobile"> Pause</span></label>
|
||||
</span>
|
||||
<span class="hideonmobile" style="position: relative;">
|
||||
<span style="position: relative;">
|
||||
<i id="searchicon" class="fa-solid fa-magnifying-glass"></i>
|
||||
<input id="search" type="search" class="form-control" oninput="filtersUpdated();" placeholder="Search">
|
||||
</span>
|
||||
<button id="filters-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleFiltersPanel();"><i class="fa-solid fa-filter"></i> Filters</button>
|
||||
<button id="display-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleDisplayPanel();"><i class="fa-solid fa-desktop"></i> Display</button>
|
||||
<button id="filters-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleFiltersPanel();"><i class="fa-solid fa-filter"></i><span class="hideonmobile"> Filters</span></button>
|
||||
<button id="display-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleDisplayPanel();"><i class="fa-solid fa-desktop"></i><span class="hideonmobile"> Display</span></button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -872,12 +872,9 @@ components:
|
||||
- SSTV
|
||||
- JS8
|
||||
- HELL
|
||||
- BPSK
|
||||
- PSK
|
||||
- BPSK31
|
||||
- OLIVIA
|
||||
- MFSK
|
||||
- MFSK32
|
||||
- PSK
|
||||
- FSK
|
||||
- PKT
|
||||
- MSK144
|
||||
example: SSB
|
||||
|
||||
@@ -349,6 +349,9 @@ div.band-spot:hover span.band-spot-info {
|
||||
max-height: 26em;
|
||||
overflow: scroll;
|
||||
}
|
||||
input#search {
|
||||
max-width: 7em;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
|
||||
Reference in New Issue
Block a user