feat: New badges in cluster view for new Counties and new POTA
This commit is contained in:
@@ -318,7 +318,7 @@ func Migrate(defs []Def) ([]Def, bool) {
|
||||
func Fields() []string {
|
||||
return []string{
|
||||
"dxcc", "cqz", "ituz", "prefix", "callsign",
|
||||
"state", "cont", "country", "grid", "grid4",
|
||||
"state", "us_county", "cont", "country", "grid", "grid4",
|
||||
"iota", "sota_ref", "pota_ref", "wwff",
|
||||
"name", "qth", "address", "comment", "note",
|
||||
}
|
||||
@@ -693,7 +693,11 @@ func USCountyKey(state, cnty string) string {
|
||||
if len(st) != 2 || co == "" {
|
||||
return ""
|
||||
}
|
||||
return st + "," + co
|
||||
// Separator is "/", NOT ",": the QSOFIELDS matcher splits a field value on
|
||||
// commas/semicolons (n-fer POTA "US-1,US-2"), which would shatter "AL,AUTAUGA"
|
||||
// into two non-matching tokens. The stored ADIF cnty keeps its comma; only
|
||||
// this internal match key uses "/".
|
||||
return st + "/" + co
|
||||
}
|
||||
|
||||
// labelRef fills a worked reference's name/group from the reference list (or the
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"def": {
|
||||
"code": "USCOUNTIES",
|
||||
"name": "US Counties (USA-CA)",
|
||||
"description": "Worked US counties (CQ USA-CA). Matches the QSO's county, tolerating LoTW \"ST,County\" and bare county-name shapes.",
|
||||
"code": "USA-CA",
|
||||
"name": "USA-CA (US Counties)",
|
||||
"description": "CQ United States of America Counties Award. Matches the QSO's county, tolerating LoTW \"ST,County\" and bare county-name shapes. Independent cities of Virginia/Nevada and DC are excluded per the award rules.",
|
||||
"valid": true,
|
||||
"protected": true,
|
||||
"type": "QSOFIELDS",
|
||||
@@ -11,6 +11,7 @@
|
||||
"exact_match": true,
|
||||
"pattern": "",
|
||||
"ref_display": "name",
|
||||
"url": "https://cq-amateur-radio.com/cq_awards/cq_usa_ca_awards/cq_usa_ca_awards.html",
|
||||
"dxcc_filter": [
|
||||
291,
|
||||
110,
|
||||
@@ -6,15 +6,15 @@ func TestUSCountyKey(t *testing.T) {
|
||||
cases := []struct {
|
||||
state, cnty, want string
|
||||
}{
|
||||
{"MA", "MA,MIDDLESEX", "MA,MIDDLESEX"}, // LoTW "ST,County" shape
|
||||
{"NJ", "Middlesex", "NJ,MIDDLESEX"}, // bare name + state column
|
||||
{"TX", "Montgomery", "TX,MONTGOMERY"}, // title case
|
||||
{"FL", "Saint Lucie", "FL,STLUCIE"}, // Saint→St, space dropped
|
||||
{"FL", "St. Lucie", "FL,STLUCIE"}, // FIPS abbreviation, period dropped
|
||||
{"AK", "Matanuska-Susitna", "AK,MATANUSKASUSITNA"}, // hyphen→space→dropped
|
||||
{"IL", "De Kalb", "IL,DEKALB"}, // split name
|
||||
{"IL", "DeKalb County", "IL,DEKALB"}, // suffix + one word
|
||||
{"LA", "Acadia Parish", "LA,ACADIA"}, // parish suffix
|
||||
{"MA", "MA,MIDDLESEX", "MA/MIDDLESEX"}, // LoTW "ST,County" shape
|
||||
{"NJ", "Middlesex", "NJ/MIDDLESEX"}, // bare name + state column
|
||||
{"TX", "Montgomery", "TX/MONTGOMERY"}, // title case
|
||||
{"FL", "Saint Lucie", "FL/STLUCIE"}, // Saint→St, space dropped
|
||||
{"FL", "St. Lucie", "FL/STLUCIE"}, // FIPS abbreviation, period dropped
|
||||
{"AK", "Matanuska-Susitna", "AK/MATANUSKASUSITNA"}, // hyphen→space→dropped
|
||||
{"IL", "De Kalb", "IL/DEKALB"}, // split name
|
||||
{"IL", "DeKalb County", "IL/DEKALB"}, // suffix + one word
|
||||
{"LA", "Acadia Parish", "LA/ACADIA"}, // parish suffix
|
||||
{"", "Honolulu", ""}, // no state → no match
|
||||
{"HI", "0", ""}, // garbage
|
||||
{"HI", "", ""}, // empty
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// - WAC → continent code ("EU", "NA", …)
|
||||
// - WAS → ADIF STATE code ("AL", …)
|
||||
// - DDFM → "D06" (the award pattern captures the leading D)
|
||||
// - USCOUNTIES → canonical "STATE,COUNTY" key (see award.usCountyKey)
|
||||
// - USA-CA → canonical "STATE/COUNTY" key (see award.USCountyKey)
|
||||
func BuiltinRefs(code string) ([]Ref, bool) {
|
||||
switch code {
|
||||
case "DXCC":
|
||||
@@ -30,7 +30,7 @@ func BuiltinRefs(code string) ([]Ref, bool) {
|
||||
return usStates().Refs, true
|
||||
case "DDFM":
|
||||
return frenchDepartments(), true
|
||||
case "USCOUNTIES":
|
||||
case "USA-CA":
|
||||
return usCounties(), true
|
||||
}
|
||||
return nil, false
|
||||
|
||||
+3102
-3143
File diff suppressed because it is too large
Load Diff
+54
-2
@@ -1605,7 +1605,7 @@ func (r *Repo) IterateAll(ctx context.Context, fn func(QSO) error) error {
|
||||
// column to this list AND populate it in scanAwardQSO below, or that award will
|
||||
// silently see an empty value during stats/computation.
|
||||
const awardCols = `id, callsign, qso_date, band, freq_hz, mode, ` +
|
||||
`grid, vucc_grids, country, state, cont, cqz, ituz, dxcc, iota, sota_ref, pota_ref, ` +
|
||||
`grid, vucc_grids, country, state, cnty, cont, cqz, ituz, dxcc, iota, sota_ref, pota_ref, ` +
|
||||
`name, qth, address, comment, notes, ` +
|
||||
`qsl_rcvd, lotw_rcvd, eqsl_rcvd, extras_json`
|
||||
|
||||
@@ -1640,6 +1640,7 @@ func scanAwardQSO(s scanner) (QSO, error) {
|
||||
qsoDateStr string
|
||||
freqHz sql.NullInt64
|
||||
grid, vucc, country, state sql.NullString
|
||||
cnty sql.NullString
|
||||
cont, iotaRef, sota, pota sql.NullString
|
||||
dxcc, cqz, ituz sql.NullInt64
|
||||
name, qth, address sql.NullString
|
||||
@@ -1649,7 +1650,7 @@ func scanAwardQSO(s scanner) (QSO, error) {
|
||||
)
|
||||
if err := s.Scan(
|
||||
&q.ID, &q.Callsign, &qsoDateStr, &q.Band, &freqHz, &q.Mode,
|
||||
&grid, &vucc, &country, &state, &cont, &cqz, &ituz, &dxcc, &iotaRef, &sota, &pota,
|
||||
&grid, &vucc, &country, &state, &cnty, &cont, &cqz, &ituz, &dxcc, &iotaRef, &sota, &pota,
|
||||
&name, &qth, &address, &comment, ¬es,
|
||||
&qslRcvd, &lotwRcvd, &eqslRcvd, &extrasJSON,
|
||||
); err != nil {
|
||||
@@ -1664,6 +1665,7 @@ func scanAwardQSO(s scanner) (QSO, error) {
|
||||
q.VUCCGrids = vucc.String
|
||||
q.Country = country.String
|
||||
q.State = state.String
|
||||
q.County = cnty.String
|
||||
q.Continent = cont.String
|
||||
if cqz.Valid {
|
||||
v := int(cqz.Int64)
|
||||
@@ -1782,6 +1784,56 @@ func (r *Repo) WorkedCallsigns(ctx context.Context) (map[string]struct{}, error)
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// WorkedCountyKeys returns the set of counties already worked, keyed by the
|
||||
// caller-supplied normaliser (award.USCountyKey — passed in to avoid importing
|
||||
// the award package here). Only US-entity QSOs (DXCC 291/110/6) with a county
|
||||
// are considered. Empty keys (unresolvable state/county) are skipped.
|
||||
func (r *Repo) WorkedCountyKeys(ctx context.Context, keyFn func(state, cnty string) string) (map[string]struct{}, error) {
|
||||
rows, err := r.db.QueryContext(ctx,
|
||||
`SELECT DISTINCT COALESCE(state,''), COALESCE(cnty,'') FROM qso
|
||||
WHERE dxcc IN (291,110,6) AND cnty IS NOT NULL AND cnty != ''`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := make(map[string]struct{}, 1024)
|
||||
for rows.Next() {
|
||||
var state, cnty string
|
||||
if err := rows.Scan(&state, &cnty); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if k := keyFn(state, cnty); k != "" {
|
||||
out[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// WorkedPOTARefs returns the set of POTA park references already worked
|
||||
// (upper-cased). A QSO's pota_ref may hold several comma-separated parks
|
||||
// (an n-fer); each is added separately.
|
||||
func (r *Repo) WorkedPOTARefs(ctx context.Context) (map[string]struct{}, error) {
|
||||
rows, err := r.db.QueryContext(ctx,
|
||||
`SELECT DISTINCT pota_ref FROM qso WHERE pota_ref IS NOT NULL AND pota_ref != ''`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := make(map[string]struct{}, 256)
|
||||
for rows.Next() {
|
||||
var ref string
|
||||
if err := rows.Scan(&ref); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range strings.Split(ref, ",") {
|
||||
if p = strings.ToUpper(strings.TrimSpace(p)); p != "" {
|
||||
out[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// Count returns the total number of QSOs in the database.
|
||||
func (r *Repo) Count(ctx context.Context) (int64, error) {
|
||||
var n int64
|
||||
|
||||
Reference in New Issue
Block a user