fix: QRZ download used an option QRZ does not accept

The date window was sent as OPTION=AFTER:<date>. That was a guess, and QRZ
rejects the request outright — RESULT=FAIL with no reason — so the confirmation
download failed completely for everyone, whatever the account.

The documented option is MODSINCE, and it is the better window anyway: it selects
records MODIFIED since the date, so a QSO from years ago that was confirmed
yesterday comes back. A QSO-date window structurally cannot return those, and
confirmations are the whole point of this download — which is why the client-side
date filter is now skipped when the server did the windowing, instead of throwing
those same records away on arrival.

A refusal now falls back to ALL rather than leaving the operator with nothing.
This commit is contained in:
2026-07-28 15:43:47 +02:00
parent 71bde30e24
commit 305da583e1
2 changed files with 24 additions and 4 deletions
+22 -4
View File
@@ -9576,7 +9576,7 @@ func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalSe
sinceDate := resolveSince(keyExtQRZLastDownload)
emit(fmt.Sprintf("Window: since=%q → resolved date=%q (key %s%s)", since, sinceDate, a.profileScope(), keyExtQRZLastDownload))
if sinceDate != "" {
emit("Fetching QRZ.com logbook (will skip QSOs before " + sinceDate + ")…")
emit("Fetching QRZ.com logbook (records modified since " + sinceDate + ")…")
} else {
emit("Fetching QRZ.com logbook (full — no since date)…")
}
@@ -9585,11 +9585,26 @@ func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalSe
// truncates mid-record, so the parse died two thirds of the way through
// and everything past that point was never seen. AFTER: narrows it at the
// source; the client-side date filter below stays as the backstop.
fetchOpt := "ALL"
//
// The option is MODSINCE (QRZ FETCH documentation) — "AFTER:" was a guess
// and QRZ rejected the whole fetch, which broke the download outright.
// MODSINCE is also the RIGHT window for this job: it selects records
// MODIFIED since the date, so a ten-year-old QSO confirmed yesterday comes
// back — a QSO-date window would never return it, and confirmations are
// exactly what this download is for.
//
// It stays an optimisation, never a requirement: if QRZ refuses it, fall
// back to ALL rather than leaving the operator with no download at all.
fetchOpt, serverWindowed := "ALL", false
if sinceDate != "" {
fetchOpt = "AFTER:" + sinceDate
fetchOpt, serverWindowed = "MODSINCE:"+sinceDate, true
}
fr, err := extsvc.FetchQRZ(ctx, nil, cfg.QRZ.APIKey, fetchOpt)
if err != nil && serverWindowed {
emit("QRZ.com refused MODSINCE (" + err.Error() + ") — fetching the full logbook instead…")
fetchOpt, serverWindowed = "ALL", false
fr, err = extsvc.FetchQRZ(ctx, nil, cfg.QRZ.APIKey, fetchOpt)
}
if err != nil {
emit("Fetch failed: " + err.Error())
done(matched, total)
@@ -9649,7 +9664,10 @@ func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalSe
return nil
}
// Date window (client-side): skip QSOs older than the requested date.
if sinceDate != "" && !q.QSODate.IsZero() && q.QSODate.UTC().Format("2006-01-02") < sinceDate {
// ONLY when the server did not window the fetch itself — MODSINCE
// returns old QSOs whose CONFIRMATION is recent, and filtering those
// out by QSO date would throw away the very records asked for.
if !serverWindowed && sinceDate != "" && !q.QSODate.IsZero() && q.QSODate.UTC().Format("2006-01-02") < sinceDate {
return nil
}
// Skip a QSO logged under a DIFFERENT one of the operator's callsigns.