fix(lotw): keep another station's confirmations out, and show a suspect report

Unscoped, the report carries every station on the account — including the calls
belonging to another profile's logbook. Those match nothing here, and with 'add
the ones not found' ticked they would pour a second log into this one. The
station callsigns this logbook actually holds are now the filter: a portable
worked here is kept, an expedition call never used here is skipped and counted.

A near-empty report is also the failure that reads as success — 'matched 1 of 1'
where the account holds twelve thousand. Under 4 KB, what LoTW answered is shown
verbatim instead.
This commit is contained in:
2026-08-27 23:12:44 +02:00
parent 4366083152
commit cff590fe8a
3 changed files with 69 additions and 2 deletions
+40
View File
@@ -12190,6 +12190,19 @@ func (a *App) runDownloadConfirmations(ctx context.Context, svc extsvc.Service,
ownCall = ""
}
callLabel := ownCall
// Unscoped, the report carries every station on the account — including
// the ones belonging to ANOTHER profile's logbook (a Vietnam expedition,
// say). Those confirmations have nothing to match here, and with "add the
// ones not found" ticked they would pour a second log into this one. So
// the station callsigns this logbook actually holds become the filter:
// F4BPO/P is kept because it was worked here, XV9Q is skipped because it
// never was.
var ownStations map[string]bool
if ownCall == "" {
if st, e := a.qso.StationCallsigns(ctx); e == nil && len(st) > 0 {
ownStations = st
}
}
if callLabel == "" {
callLabel = "all callsigns"
}
@@ -12206,6 +12219,19 @@ func (a *App) runDownloadConfirmations(ctx context.Context, svc extsvc.Service,
return
}
emit(fmt.Sprintf("LoTW returned %d KB of ADIF", len(adifText)/1024))
// A report far smaller than the account justifies is the one failure that
// looks like success: LoTW answers 200 with a near-empty ADIF when it
// disagrees with the query (an unknown callsign in qso_owncall, a login
// that half-worked). Show its own header rather than leaving "matched 1 of
// 1" to be read as "you have one confirmation".
if len(adifText) < 4096 {
head := strings.TrimSpace(adifText)
if len(head) > 400 {
head = head[:400]
}
emit("The report is unexpectedly small — what LoTW actually sent:")
emit(" " + strings.Join(strings.Fields(head), " "))
}
keyIDs, kerr := a.qso.DedupeKeyIDs(ctx)
if kerr != nil {
emit("Error reading local log: " + kerr.Error())
@@ -12223,6 +12249,7 @@ func (a *App) runDownloadConfirmations(ctx context.Context, svc extsvc.Service,
sets, _ := a.qso.ConfirmedSlots(ctx, []string{"lotw_rcvd", "qsl_rcvd"})
var items []ConfirmationItem
var unmatched []string
skippedOtherStation := 0
perr := adif.Parse(strings.NewReader(adifText), func(rec adif.Record) error {
if ctx.Err() != nil {
return ctx.Err() // window closed / superseded
@@ -12231,6 +12258,16 @@ func (a *App) runDownloadConfirmations(ctx context.Context, svc extsvc.Service,
if !ok {
return nil
}
// Another station's confirmation — see ownStations above. Counted so
// the report says how many were left alone rather than silently
// dropping a third of the file.
if ownStations != nil {
st := strings.ToUpper(strings.TrimSpace(rec["station_callsign"]))
if st != "" && !ownStations[st] {
skippedOtherStation++
return nil
}
}
total++
date := rec["qslrdate"]
if date == "" {
@@ -12311,6 +12348,9 @@ func (a *App) runDownloadConfirmations(ctx context.Context, svc extsvc.Service,
} else {
emit(fmt.Sprintf("Matched %d of %d confirmed QSO(s)", matched, total))
}
if skippedOtherStation > 0 {
emit(fmt.Sprintf(" (%d confirmation(s) skipped — made under a callsign this logbook has never used)", skippedOtherStation))
}
if byClass > 0 {
// Said out loud rather than folded silently into the total: these
// matched on the mode CLASS, not the mode. LoTW hands back "DATA" for