feat(hamlog): mark what has been sent, and a QSL Manager backlog

An upload now leaves a trace on the QSO: APP_OPSLOG_HAMLOG_SENT holds the day
it went. The date rather than a Y, so a log says WHEN — and so one shape serves
three readers: eligibility (a stamped QSO is not sent twice), the QSL Manager's
backlog, and the appearance rules' sent channel.

HAMLOG joins the QSL Manager's service list. It has no status column to select
on, so its backlog is the ABSENCE of that extras key — a LIKE over the extras
JSON, which is a full scan and is the right trade here: it answers a button an
operator presses, and the alternative is a promoted column for a field no other
program would ever read. The match is on the quoted key, so a QSO whose comment
merely mentions the text is not taken for one already sent — which is what the
test pins.
This commit is contained in:
2026-08-23 12:40:09 +02:00
parent 73806cbd70
commit 036dc53fe8
5 changed files with 104 additions and 3 deletions
+29 -1
View File
@@ -10999,6 +10999,13 @@ func (a *App) FindQSOsForUpload(service, sentStatus string) ([]qso.QSO, error) {
if a.qso == nil {
return nil, fmt.Errorf("db not initialized")
}
// HAMLOG.online keeps its state in an ADIF extra rather than a column, so
// its backlog is "the extra is absent" instead of "the column is blank".
// The sent-status filter has nothing to select on — a QSO has gone or it has
// not — so it is ignored rather than pretended to work.
if extsvc.Service(service) == extsvc.ServiceHamlog {
return a.qso.ListMissingExtra(a.ctx, hamlogSentKey)
}
col := uploadColumnFor(service)
if col == "" {
return nil, fmt.Errorf("unknown service %q", service)
@@ -11014,7 +11021,7 @@ func (a *App) UploadQSOsManual(service string, ids []int64) error {
return fmt.Errorf("db not initialized")
}
svc := extsvc.Service(service)
if uploadColumnFor(service) == "" {
if uploadColumnFor(service) == "" && svc != extsvc.ServiceHamlog {
return fmt.Errorf("unknown service %q", service)
}
cfg := a.loadExternalServices()
@@ -12820,6 +12827,15 @@ func (a *App) extShouldUpload(svc extsvc.Service, id int64) bool {
// and every QSO is eligible. The cost is that a permanently failed upload
// is not remembered — hence no on-close mode and no manual backlog.
return true
case extsvc.ServiceHamlog:
// The stamp is an extra, not a column — see markExtUploaded. Present means
// it has gone, which is what stops an on-demand re-upload of a whole log
// from sending every contact twice.
if q.Extras != nil && strings.TrimSpace(q.Extras[hamlogSentKey]) != "" {
applog.Printf("extsvc: QSO %d not eligible for hamlog — already sent on %s", id, q.Extras[hamlogSentKey])
return false
}
return true
case extsvc.ServiceLoTW:
for _, f := range a.loadExternalServices().LoTW.UploadFlags {
if strings.EqualFold(q.LOTWSent, f) {
@@ -12833,6 +12849,11 @@ func (a *App) extShouldUpload(svc extsvc.Service, id int64) bool {
// markExtUploaded stamps the per-service upload status on the QSO row and
// tells the frontend to refresh that row's confirmation columns.
// hamlogSentKey records the day a QSO went to HAMLOG.online. Shared with the
// row colours (lib/rowColors.ts) and the QSL Manager, so the three agree on
// what "already sent" means.
const hamlogSentKey = "APP_OPSLOG_HAMLOG_SENT"
func (a *App) markExtUploaded(svc extsvc.Service, id int64, logID string) {
date := time.Now().UTC().Format("20060102")
// Use a fresh background context, NOT a.ctx: this stamp often runs during
@@ -12869,6 +12890,13 @@ func (a *App) markExtUploaded(svc extsvc.Service, id int64, logID string) {
case extsvc.ServiceCloudlog:
// Nothing to stamp — see extShouldUpload. Still logged and announced so
// the upload is visible in the diagnostic log and the UI.
case extsvc.ServiceHamlog:
// An ADIF EXTRA, not a column: the standard names a field for hamlog.EU
// and none for hamlog.ONLINE, and borrowing the other site's would write
// a falsehood into every export. The date rather than a Y, so a log tells
// WHEN a contact went — and so the same shape serves the QSL Manager's
// backlog query, which asks "is this key present at all".
err = a.qso.SetExtra(ctx, id, hamlogSentKey, date)
}
if err != nil {
applog.Printf("extsvc: mark %s uploaded %d failed: %v", svc, id, err)