chore: release v0.26.2
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -142,6 +143,7 @@ func parseHamQTHSearch(body []byte) (Result, error) {
|
||||
QSLVia: s.QSLVia,
|
||||
Web: s.Web,
|
||||
ImageURL: s.Picture,
|
||||
RDA: rdaRef(s.Oblast, s.District),
|
||||
}
|
||||
r.Lat, _ = strconv.ParseFloat(s.Latitude, 64)
|
||||
r.Lon, _ = strconv.ParseFloat(s.Longitude, 64)
|
||||
@@ -151,6 +153,27 @@ func parseHamQTHSearch(body []byte) (Result, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// rdaRefRe matches a Russian district reference: two letters, a hyphen, two
|
||||
// digits. Searched for rather than anchored, because the field is filled by
|
||||
// hand and arrives as "KE-29", "KE-29 Kemerovskaya obl." or with stray spaces
|
||||
// depending on which editor the operator used.
|
||||
var rdaRefRe = regexp.MustCompile(`\b([A-Z]{2}-[0-9]{2})\b`)
|
||||
|
||||
// rdaRef extracts a district reference from HamQTH's oblast/district fields.
|
||||
//
|
||||
// Strict about the SHAPE and silent otherwise: a value that is not a district
|
||||
// reference — an oblast name, a postal code, free text — must yield nothing.
|
||||
// Writing it into the award anyway would create a reference no list contains,
|
||||
// which counts for zero and has to be found and removed by hand later.
|
||||
func rdaRef(fields ...string) string {
|
||||
for _, f := range fields {
|
||||
if m := rdaRefRe.FindStringSubmatch(strings.ToUpper(strings.TrimSpace(f))); m != nil {
|
||||
return m[1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *HamQTH) get(ctx context.Context, u string) ([]byte, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", u, nil)
|
||||
if err != nil {
|
||||
@@ -191,15 +214,19 @@ type hamqthSearch struct {
|
||||
AdrCountry string `xml:"adr_country"`
|
||||
USState string `xml:"us_state"`
|
||||
USCounty string `xml:"us_county"`
|
||||
Grid string `xml:"grid"`
|
||||
Latitude string `xml:"latitude"`
|
||||
Longitude string `xml:"longitude"`
|
||||
DXCC string `xml:"adif"` // HamQTH exposes the ADIF/DXCC number under <adif>
|
||||
CQ string `xml:"cq"`
|
||||
ITU string `xml:"itu"`
|
||||
Continent string `xml:"continent"`
|
||||
Email string `xml:"email"`
|
||||
QSLVia string `xml:"qsl_via"`
|
||||
// Oblast is HamQTH's Russian district field; District is its generic one.
|
||||
// Both are read because operators fill whichever their editor showed them.
|
||||
Oblast string `xml:"oblast"`
|
||||
District string `xml:"district"`
|
||||
Grid string `xml:"grid"`
|
||||
Latitude string `xml:"latitude"`
|
||||
Longitude string `xml:"longitude"`
|
||||
DXCC string `xml:"adif"` // HamQTH exposes the ADIF/DXCC number under <adif>
|
||||
CQ string `xml:"cq"`
|
||||
ITU string `xml:"itu"`
|
||||
Continent string `xml:"continent"`
|
||||
Email string `xml:"email"`
|
||||
QSLVia string `xml:"qsl_via"`
|
||||
// AdrName is the full postal name. Many records carry it and no <name> at
|
||||
// all, so building the name from <nick> + <name> silently kept the first
|
||||
// name and dropped the rest.
|
||||
|
||||
@@ -45,6 +45,17 @@ type Result struct {
|
||||
// and nothing used to read it — and since no live activation feed exists for
|
||||
// IOTA the way it does for POTA, the callbook record is the practical source.
|
||||
IOTA string `json:"iota,omitempty"`
|
||||
// RDA is the Russian District Award reference (KE-29). HamQTH publishes it
|
||||
// as <oblast>, described in their own documentation as "something like
|
||||
// district (Russian stations)"; QRZ.com has no equivalent — its county and
|
||||
// state fields are USA-only.
|
||||
//
|
||||
// It is the CURRENT district, which is why it may only ever be applied to a
|
||||
// contact being made now. A Russian station can move, and stamping today's
|
||||
// district on a three-year-old QSO rewrites a correct contact into a wrong
|
||||
// one. DXLab solves the general case with dated activity records; we solve
|
||||
// the case that matters by never dating anything but the present.
|
||||
RDA string `json:"rda,omitempty"`
|
||||
// Zip is the postal code. HamQTH and QRZ both send one.
|
||||
Zip string `json:"zip,omitempty"`
|
||||
ImageURL string `json:"image_url,omitempty"` // profile picture URL
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package lookup
|
||||
|
||||
import "testing"
|
||||
|
||||
// The district only ever reaches the log if it has the RIGHT SHAPE. HamQTH's
|
||||
// oblast field is filled by hand, so it arrives in whatever form the operator's
|
||||
// editor encouraged — and anything that is not a district reference must yield
|
||||
// nothing at all rather than a reference no list contains.
|
||||
func TestRDARef(t *testing.T) {
|
||||
for _, c := range []struct{ in, want string }{
|
||||
{"KE-29", "KE-29"},
|
||||
{"ke-29", "KE-29"},
|
||||
{" KE-29 ", "KE-29"},
|
||||
{"KE-29 Kemerovskaya obl.", "KE-29"},
|
||||
{"Kemerovskaya obl. (KE-29)", "KE-29"},
|
||||
{"", ""},
|
||||
{"Kemerovskaya oblast", ""}, // a name, not a reference
|
||||
{"KE", ""}, // the oblast alone carries no district
|
||||
{"KE-1", ""}, // one digit is not an RDA
|
||||
{"KE-291", ""}, // three digits is not one either
|
||||
{"650000", ""}, // a postal code
|
||||
{"EU-064", ""}, // an IOTA reference in the wrong field
|
||||
} {
|
||||
if got := rdaRef(c.in); got != c.want {
|
||||
t.Errorf("rdaRef(%q) = %q, want %q", c.in, got, c.want)
|
||||
}
|
||||
}
|
||||
// oblast wins over district; an empty first field falls through to the next.
|
||||
if got := rdaRef("", "MO-19"); got != "MO-19" {
|
||||
t.Errorf("fallback to district = %q, want MO-19", got)
|
||||
}
|
||||
if got := rdaRef("KE-29", "MO-19"); got != "KE-29" {
|
||||
t.Errorf("oblast must win, got %q", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user