diff --git a/README.md b/README.md index 09d4d51..f0364fe 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ a mapping exists. | `map-center-lon` | Numeric (decimal) | (auto) | `?map-center-lon=-0.1` | Sets the initial longitude of the map centre on the map page. If omitted, the map auto-fits to the loaded spots. | | `map-zoom` | Numeric (integer) | (auto) | `?map-zoom=6` | Sets the initial zoom level of the map on the map page. If omitted, the map auto-fits to the loaded spots. | -More will be added soon to allow customisation of filters and other display properties. +See the comment at the end of the next section regarding reliability and uptime of the "main" server. ## Writing your own client @@ -95,7 +95,13 @@ Various approaches exist to writing your own client, but in general: * Refer to the provided HTML/JS interface for a reference on different approaches. For example, the "map" and "bands" pages simply query the main spot API on a timer, whereas the main/spots page combines this approach with using the Server-Sent Events (SSE) endpoint to update live. -* Let me know if you get stuck, I'm happy to help! +* Let me know if you get stuck, I'm happy to help. + +Remember, here at Spothole Inc. we offer an industry-standard "five nines" uptime on our server, with our own unique +twist: we don't tell you which side of the decimal point the nines start! (Translation: This is a hobby project. +`spothole.app` runs on the same server as my blog and other stuff. It might go down without warning. By all means base +your own project on data from the main server if you like, but if you want any control over reliability and downtime, +please run your own copy instead.) ## Running your own copy diff --git a/spotproviders/gma.py b/spotproviders/gma.py index 558ca98..c367559 100644 --- a/spotproviders/gma.py +++ b/spotproviders/gma.py @@ -24,70 +24,74 @@ class GMA(HTTPSpotProvider): def _http_response_to_spots(self, http_response): new_spots = [] # Iterate through source data - for source_spot in http_response.json()["RCD"]: - # Convert to our spot format - spot = Spot(source=self.name, - dx_call=source_spot["ACTIVATOR"].upper(), - de_call=source_spot["SPOTTER"].upper(), - freq=float(source_spot["QRG"]) * 1000 if (source_spot["QRG"] != "") else None, - # Seen GMA spots with no frequency - mode=source_spot["MODE"].upper() if "<>" not in source_spot["MODE"] else None, - # Filter out some weird mode strings - comment=source_spot["TEXT"], - sig_refs=[SIGRef(id=source_spot["REF"], sig="", name=source_spot["NAME"])], - time=datetime.strptime(source_spot["DATE"] + source_spot["TIME"], "%Y%m%d%H%M").replace( - tzinfo=pytz.UTC).timestamp(), - dx_latitude=float(source_spot["LAT"]) if ( - source_spot["LAT"] and source_spot["LAT"] != "") else None, - # Seen GMA spots with no (or empty) lat/lon - dx_longitude=float(source_spot["LON"]) if ( - source_spot["LON"] and source_spot["LON"] != "") else None) + if "RCD" in http_response.json(): + for source_spot in http_response.json()["RCD"]: + # Convert to our spot format + spot = Spot(source=self.name, + dx_call=source_spot["ACTIVATOR"].upper(), + de_call=source_spot["SPOTTER"].upper(), + freq=float(source_spot["QRG"]) * 1000 if (source_spot["QRG"] != "") else None, + # Seen GMA spots with no frequency + mode=source_spot["MODE"].upper() if "<>" not in source_spot["MODE"] else None, + # Filter out some weird mode strings + comment=source_spot["TEXT"], + sig_refs=[SIGRef(id=source_spot["REF"], sig="", name=source_spot["NAME"])], + time=datetime.strptime(source_spot["DATE"] + source_spot["TIME"], "%Y%m%d%H%M").replace( + tzinfo=pytz.UTC).timestamp(), + dx_latitude=float(source_spot["LAT"]) if ( + source_spot["LAT"] and source_spot["LAT"] != "") else None, + # Seen GMA spots with no (or empty) lat/lon + dx_longitude=float(source_spot["LON"]) if ( + source_spot["LON"] and source_spot["LON"] != "") else None) - # GMA doesn't give what programme (SIG) the reference is for until we separately look it up. - if "REF" in source_spot: - try: - ref_response = SEMI_STATIC_URL_DATA_CACHE.get(self.REF_INFO_URL_ROOT + source_spot["REF"], - headers=HTTP_HEADERS) - # Sometimes this is blank, so handle that - if ref_response.text is not None and ref_response.text != "": - ref_info = ref_response.json() - # If this is POTA, SOTA or WWFF data we already have it through other means, so ignore. POTA and WWFF - # spots come through with reftype=POTA or reftype=WWFF. SOTA is harder to figure out because both SOTA - # and GMA summits come through with reftype=Summit, so we must check for the presence of a "sota" entry - # to determine if it's a SOTA summit. - if spot.sig_refs and "reftype" in ref_info and ref_info["reftype"] not in ["POTA", "WWFF"] and ( - ref_info["reftype"] != "Summit" or "sota" not in ref_info or ref_info["sota"] == ""): - match ref_info["reftype"]: - case "Summit": - spot.sig_refs[0].sig = "GMA" - spot.sig = "GMA" - case "IOTA Island": - spot.sig_refs[0].sig = "IOTA" - spot.sig = "IOTA" - case "Lighthouse (ILLW)": - spot.sig_refs[0].sig = "ILLW" - spot.sig = "ILLW" - case "Lighthouse (ARLHS)": - spot.sig_refs[0].sig = "ARLHS" - spot.sig = "ARLHS" - case "Castle": - spot.sig_refs[0].sig = "WCA" - spot.sig = "WCA" - case "Mill": - spot.sig_refs[0].sig = "MOTA" - spot.sig = "MOTA" - case _: - logging.warning("GMA spot found with ref type " + ref_info[ - "reftype"] + ", developer needs to add support for this!") - spot.sig_refs[0].sig = ref_info["reftype"] - spot.sig = ref_info["reftype"] + # GMA doesn't give what programme (SIG) the reference is for until we separately look it up. + if "REF" in source_spot: + try: + ref_response = SEMI_STATIC_URL_DATA_CACHE.get(self.REF_INFO_URL_ROOT + source_spot["REF"], + headers=HTTP_HEADERS) + # Sometimes this is blank, so handle that + if ref_response.text is not None and ref_response.text != "": + ref_info = ref_response.json() + # If this is POTA, SOTA or WWFF data we already have it through other means, so ignore. POTA and WWFF + # spots come through with reftype=POTA or reftype=WWFF. SOTA is harder to figure out because both SOTA + # and GMA summits come through with reftype=Summit, so we must check for the presence of a "sota" entry + # to determine if it's a SOTA summit. + if spot.sig_refs and "reftype" in ref_info and ref_info["reftype"] not in ["POTA", "WWFF"] and ( + ref_info["reftype"] != "Summit" or "sota" not in ref_info or ref_info["sota"] == ""): + match ref_info["reftype"]: + case "Summit": + spot.sig_refs[0].sig = "GMA" + spot.sig = "GMA" + case "IOTA Island": + spot.sig_refs[0].sig = "IOTA" + spot.sig = "IOTA" + case "Lighthouse (ILLW)": + spot.sig_refs[0].sig = "ILLW" + spot.sig = "ILLW" + case "Lighthouse (ARLHS)": + spot.sig_refs[0].sig = "ARLHS" + spot.sig = "ARLHS" + case "Castle": + spot.sig_refs[0].sig = "WCA" + spot.sig = "WCA" + case "Mill": + spot.sig_refs[0].sig = "MOTA" + spot.sig = "MOTA" + case _: + logging.warning("GMA spot found with ref type " + ref_info[ + "reftype"] + ", developer needs to add support for this!") + spot.sig_refs[0].sig = ref_info["reftype"] + spot.sig = ref_info["reftype"] + + # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do + # that for us. + new_spots.append(spot) + except: + logging.warning("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[ + "REF"] + ", ignoring this spot for now") + else: + logging.warning("The GMA API returned an unexpected response.") - # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do - # that for us. - new_spots.append(spot) - except: - logging.warning("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[ - "REF"] + ", ignoring this spot for now") return new_spots def can_submit_spot(self, sig): diff --git a/templates/add_spot.html b/templates/add_spot.html index da12fc5..ce91198 100644 --- a/templates/add_spot.html +++ b/templates/add_spot.html @@ -76,7 +76,7 @@ - + diff --git a/templates/alerts.html b/templates/alerts.html index f34e327..7c55794 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -75,7 +75,7 @@ - + diff --git a/templates/bands.html b/templates/bands.html index 7694ad9..bf6e0d7 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -77,8 +77,8 @@ - - + + diff --git a/templates/base.html b/templates/base.html index f456c08..385547c 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,6 @@ {% extends "skeleton.html" %} {% block head_extra %} - + @@ -10,10 +10,10 @@ - - - - + + + + {% end %} {% block body %}