fix: QRZ.com download marked unconfirmed QSOs as confirmed

The operator's own fetch settles it. One record, both fields:

  <app_qrzlog_status:1>N  <qrzcom_qso_download_status:1>Y

QRZ says the QSO is NOT confirmed and sets the download field anyway — it marks
what was handed back, not what was confirmed. OpsLog read it as a confirmation,
so eighteen QSOs came back "UPDATED" and green, none of them confirmed on QRZ.
The site's own logbook page shows them without a star.

Only app_qrzlog_status = C counts now. The test that asserted otherwise is
corrected and carries the real record.

And the download now takes back what it wrongly gave: when QRZ explicitly says
not-confirmed and our log says confirmed, the flag is cleared and reported on
its own line. These QSOs count towards award slots, so leaving them green until
someone noticed was not an option — and nothing else in the app would ever have
undone them. Only on an explicit no: a record with no app_qrzlog_status is QRZ
saying nothing, and silence is not a retraction.
This commit is contained in:
2026-07-31 14:40:50 +02:00
parent e4217da010
commit d90f953df4
4 changed files with 81 additions and 18 deletions
+24 -7
View File
@@ -6,12 +6,22 @@ import (
"hamlog/internal/adif"
)
// What counts as a QRZ.com confirmation.
// What counts as a QRZ.com confirmation: app_qrzlog_status = C, and nothing else.
//
// Reported 2026-07-29: after a confirmation download, every QSO turned to Y. The
// test accepted qsl_rcvd = Y — but that is the operator's own PAPER QSL flag,
// which they uploaded to QRZ themselves, so every QSO with a card came back
// looking QRZ-confirmed. On a log full of paper QSLs that is most of it.
// Narrowed twice, each time on evidence from an operator's own log.
//
// 2026-07-29 — qsl_rcvd = Y was accepted. That is the operator's own PAPER QSL
// flag, which they uploaded to QRZ themselves, so every QSO with a card came
// back looking QRZ-confirmed.
//
// 2026-07-31 — qrzcom_qso_download_status = Y was accepted, and this test said
// it should be. A fetch showed both fields on ONE record:
//
// <app_qrzlog_status:1>N <qrzcom_qso_download_status:1>Y
//
// QRZ says not confirmed and sets the download field anyway: it marks what was
// handed back, not what was confirmed. Eighteen QSOs, all "UPDATED", all green,
// none of them confirmed on QRZ.
//
// This status feeds the award slots, so a false Y is a QSO counted as confirmed
// when it is not — the reason the rule is narrow rather than generous.
@@ -21,8 +31,7 @@ func TestQRZRecordConfirmed(t *testing.T) {
rec adif.Record
want bool
}{
// QRZ's own statements.
{"QRZ download status Y", adif.Record{"qrzcom_qso_download_status": "Y"}, true},
// QRZ's own statement.
{"QRZ log status C", adif.Record{"app_qrzlog_status": "C"}, true},
{"lower case is still QRZ's answer", adif.Record{"app_qrzlog_status": "c"}, true},
{"padded", adif.Record{"app_qrzlog_status": " C "}, true},
@@ -33,6 +42,14 @@ func TestQRZRecordConfirmed(t *testing.T) {
{"LoTW confirmed, QRZ silent", adif.Record{"lotw_qsl_rcvd": "Y"}, false},
{"eQSL confirmed, QRZ silent", adif.Record{"eqsl_qsl_rcvd": "Y"}, false},
// The download flag is not an answer: QRZ sets it on everything it returns.
{"downloaded, not confirmed", adif.Record{"qrzcom_qso_download_status": "Y"}, false},
{"the record from the report", adif.Record{
"call": "IQ4J", "band": "17m", "mode": "FT8",
"qrzcom_qso_upload_status": "Y", "app_qrzlog_status": "N",
"qrzcom_qso_download_date": "20260731", "qrzcom_qso_download_status": "Y",
}, false},
// Explicit negatives and nothing at all.
{"QRZ says no", adif.Record{"qrzcom_qso_download_status": "N"}, false},
{"QRZ status not confirmed", adif.Record{"app_qrzlog_status": "N"}, false},