feat(counties): right-click to re-derive a US county from the ULS database

The automatic pass fills a blank county only, and deliberately: a value the
operator or QRZ supplied usually beats one derived from a ZIP code. That leaves
no way at all to correct a county already stored — and the Connecticut planning
regions are exactly that case. Every CT contact logged before that correction
holds a county the state abolished in 2022, and filling blanks never reaches
one of them.

So the new entry OVERWRITES, because it is asked for by hand on a chosen set of
rows precisely because the stored value is believed wrong. Only US entities are
touched, only callsigns the database holds, and only when the answer actually
differs — so re-running it on a mixed selection is safe and the count reported
is the number of counties that really changed. Award refs are re-materialised:
the county IS the reference for CQ USA-CA and the state for WAS.

The entry appears only once the database has been downloaded, and appears
without a restart when one finishes — an action that can only ever answer "no
database" is not worth a line in a menu this long.
This commit is contained in:
2026-08-16 10:45:21 +02:00
parent 5770c40d89
commit 3ed2d957e5
10 changed files with 123 additions and 15 deletions
+60
View File
@@ -10549,6 +10549,66 @@ func (a *App) applyULSCounty(q *qso.QSO) {
}
}
// UpdateQSOsCountyFromULS re-derives the county of the selected US contacts from
// the offline ULS database, and OVERWRITES what is there.
//
// That is the difference from applyULSCounty beside it, and it is deliberate.
// The automatic pass fills blanks only, because a value the operator or QRZ
// supplied is usually better than one derived from a ZIP code. This one is asked
// for by hand, on a chosen set of rows, precisely BECAUSE the stored county is
// believed wrong — the Connecticut planning regions are the case that prompted
// it: every CT contact logged before that correction holds a county the state
// abolished in 2022, and no amount of filling blanks reaches them.
//
// Only US entities are touched, only callsigns the database knows, and only when
// the answer actually differs — so re-running it on a mixed selection is safe
// and the count reported is the number of counties that really changed.
func (a *App) UpdateQSOsCountyFromULS(ids []int64) (int, error) {
if a.qso == nil {
return 0, fmt.Errorf("db not initialized")
}
if a.uls == nil || a.uls.Count() == 0 {
return 0, fmt.Errorf("the US county database has not been downloaded yet — Settings Awards US counties")
}
changed := 0
for _, id := range ids {
q, err := a.qso.GetByID(a.ctx, id)
if err != nil || q.DXCC == nil {
continue
}
switch *q.DXCC {
case 291, 110, 6: // United States, Hawaii, Alaska
default:
continue
}
loc, ok := a.uls.Resolve(q.Callsign)
if !ok {
continue
}
cnty := loc.CNTY()
if cnty == "" {
continue
}
if q.County == cnty && (loc.State == "" || q.State == loc.State) {
continue
}
q.County = cnty
if loc.State != "" {
q.State = loc.State
}
if a.qso.Update(a.ctx, q) == nil {
changed++
}
}
if changed > 0 {
// The county IS the reference for CQ USA-CA and the state for WAS, so the
// stored award refs are stale the moment it changes.
a.invalidateAwardStats()
a.materializeAwardRefsForIDs(ids)
}
return changed, nil
}
// ULSStatusResult reports whether the county database is loaded, and how fresh.
type ULSStatusResult struct {
Count int `json:"count"`