fix(hamlog): read the site's own confirmation field, not a guessed one
A real record from their export settles it: <CALL:4>RL6M … <CNTY:5>RO-19 <APP_HAMLOG_R150COUNTRY:6>Russia <APP_HAMLOG_QSO_CFM:1>Y The confirmation lives in APP_HAMLOG_QSO_CFM. The four names guessed before a file was available — APP_HAMLOG_QSL and friends — were all wrong, which is the argument for reading one rather than reasoning about it. So that becomes the canonical key everywhere: the award source, the row colours, the grid column and the bulk editor. Importing a log downloaded from HAMLOG now carries its confirmations into OpsLog with nothing to rename. The older names, including the APP_OPSLOG_HAMLOG_QSL that OpsLog itself wrote in the meantime, are still honoured on read so nothing already stamped stops counting.
This commit is contained in:
+4
-2
@@ -12,7 +12,8 @@
|
|||||||
"Right-clicking QSOs offers HAMLOG.online among the upload targets, and the toast names each service properly instead of echoing its internal id.",
|
"Right-clicking QSOs offers HAMLOG.online among the upload targets, and the toast names each service properly instead of echoing its internal id.",
|
||||||
"Bulk edit can set HAMLOG.online sent and received, each with its status and its date — the same four fields every other service has there. A log uploaded to their site by hand had no way to be marked as sent, so OpsLog kept offering to upload every contact again.",
|
"Bulk edit can set HAMLOG.online sent and received, each with its status and its date — the same four fields every other service has there. A log uploaded to their site by hand had no way to be marked as sent, so OpsLog kept offering to upload every contact again.",
|
||||||
"HAMLOG.online joins the Confirmations defaults and the QSO grid columns — sent and received, each with its status and date, hidden by default like the other extras-backed columns.",
|
"HAMLOG.online joins the Confirmations defaults and the QSO grid columns — sent and received, each with its status and date, hidden by default like the other extras-backed columns.",
|
||||||
"Removed the explanatory paragraph from the Confirmations page."
|
"Removed the explanatory paragraph from the Confirmations page.",
|
||||||
|
"HAMLOG.online confirmations are read from the site's own ADIF field, APP_HAMLOG_QSO_CFM, taken from a real export. Import a log downloaded from HAMLOG and its confirmations count for awards straight away, with nothing to rename and no mapping to configure."
|
||||||
],
|
],
|
||||||
"fr": [
|
"fr": [
|
||||||
"Yaesu : une ligne ANT dans le panneau du poste sélectionne la prise d'antenne, et le choix est mémorisé pour la bande sur laquelle il a été fait — on change de bande, l'antenne suit. Aucune page à configurer : choisir l'antenne une fois sur une bande le dit très bien. Les postes à une seule prise n'affichent jamais la ligne.",
|
"Yaesu : une ligne ANT dans le panneau du poste sélectionne la prise d'antenne, et le choix est mémorisé pour la bande sur laquelle il a été fait — on change de bande, l'antenne suit. Aucune page à configurer : choisir l'antenne une fois sur une bande le dit très bien. Les postes à une seule prise n'affichent jamais la ligne.",
|
||||||
@@ -24,7 +25,8 @@
|
|||||||
"Le clic droit sur des QSO propose HAMLOG.online parmi les destinations d'envoi, et la notification nomme correctement chaque service au lieu d'afficher son identifiant interne.",
|
"Le clic droit sur des QSO propose HAMLOG.online parmi les destinations d'envoi, et la notification nomme correctement chaque service au lieu d'afficher son identifiant interne.",
|
||||||
"L'édition en masse peut renseigner HAMLOG.online envoyé et reçu, chacun avec son statut et sa date — les mêmes quatre champs que les autres services. Un journal téléversé à la main sur leur site n'avait aucun moyen d'être marqué comme envoyé, et OpsLog proposait donc de renvoyer tous les contacts.",
|
"L'édition en masse peut renseigner HAMLOG.online envoyé et reçu, chacun avec son statut et sa date — les mêmes quatre champs que les autres services. Un journal téléversé à la main sur leur site n'avait aucun moyen d'être marqué comme envoyé, et OpsLog proposait donc de renvoyer tous les contacts.",
|
||||||
"HAMLOG.online rejoint les valeurs par défaut des Confirmations et les colonnes de la grille des QSO — envoyé et reçu, chacun avec son statut et sa date, masquées par défaut comme les autres colonnes issues des extras.",
|
"HAMLOG.online rejoint les valeurs par défaut des Confirmations et les colonnes de la grille des QSO — envoyé et reçu, chacun avec son statut et sa date, masquées par défaut comme les autres colonnes issues des extras.",
|
||||||
"Suppression du paragraphe explicatif de la page Confirmations."
|
"Suppression du paragraphe explicatif de la page Confirmations.",
|
||||||
|
"Les confirmations HAMLOG.online sont lues dans le champ ADIF du site lui-même, APP_HAMLOG_QSO_CFM, relevé sur un export réel. Il suffit d'importer un journal téléchargé depuis HAMLOG pour que ses confirmations comptent aussitôt pour les diplômes, sans rien renommer ni configurer."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export const makeColCatalog = (t: TFn, myGrid?: string): ColEntry[] => [
|
|||||||
// service, shown to everyone, is how a grid becomes unreadable.
|
// service, shown to everyone, is how a grid becomes unreadable.
|
||||||
{ group: 'QSL', label: t('rqg.c.hamlog_sent'), colId: 'hamlog_sent', headerName: t('rqg.h.hamlog_sent'), width: 100, cellClass: qslStatusCellClass, valueGetter: (p) => { const e = (p.data as any)?.extras ?? {}; return e['APP_OPSLOG_HAMLOG_SENT'] || 'N'; }, defaultVisible: false },
|
{ group: 'QSL', label: t('rqg.c.hamlog_sent'), colId: 'hamlog_sent', headerName: t('rqg.h.hamlog_sent'), width: 100, cellClass: qslStatusCellClass, valueGetter: (p) => { const e = (p.data as any)?.extras ?? {}; return e['APP_OPSLOG_HAMLOG_SENT'] || 'N'; }, defaultVisible: false },
|
||||||
{ group: 'QSL', label: t('rqg.c.hamlog_sent_date'), colId: 'hamlog_sent_date', headerName: t('rqg.h.hamlog_sent_date'), width: 110, valueGetter: (p) => fmtDateOnly((p.data as any)?.extras?.['APP_OPSLOG_HAMLOG_SENT_DATE']), defaultVisible: false },
|
{ group: 'QSL', label: t('rqg.c.hamlog_sent_date'), colId: 'hamlog_sent_date', headerName: t('rqg.h.hamlog_sent_date'), width: 110, valueGetter: (p) => fmtDateOnly((p.data as any)?.extras?.['APP_OPSLOG_HAMLOG_SENT_DATE']), defaultVisible: false },
|
||||||
{ group: 'QSL', label: t('rqg.c.hamlog_rcvd'), colId: 'hamlog_rcvd', headerName: t('rqg.h.hamlog_rcvd'), width: 100, cellClass: qslStatusCellClass, valueGetter: (p) => { const e = (p.data as any)?.extras ?? {}; return e['APP_OPSLOG_HAMLOG_QSL'] || 'N'; }, defaultVisible: false },
|
{ group: 'QSL', label: t('rqg.c.hamlog_rcvd'), colId: 'hamlog_rcvd', headerName: t('rqg.h.hamlog_rcvd'), width: 100, cellClass: qslStatusCellClass, valueGetter: (p) => { const e = (p.data as any)?.extras ?? {}; return e['APP_HAMLOG_QSO_CFM'] || e['APP_OPSLOG_HAMLOG_QSL'] || 'N'; }, defaultVisible: false },
|
||||||
{ group: 'QSL', label: t('rqg.c.hamlog_rcvd_date'), colId: 'hamlog_rcvd_date', headerName: t('rqg.h.hamlog_rcvd_date'), width: 110, valueGetter: (p) => fmtDateOnly((p.data as any)?.extras?.['APP_OPSLOG_HAMLOG_QSL_DATE']), defaultVisible: false },
|
{ group: 'QSL', label: t('rqg.c.hamlog_rcvd_date'), colId: 'hamlog_rcvd_date', headerName: t('rqg.h.hamlog_rcvd_date'), width: 110, valueGetter: (p) => fmtDateOnly((p.data as any)?.extras?.['APP_OPSLOG_HAMLOG_QSL_DATE']), defaultVisible: false },
|
||||||
// App-specific: when the QSO's audio recording was e-mailed to the station.
|
// App-specific: when the QSO's audio recording was e-mailed to the station.
|
||||||
{ group: 'QSL', label: t('rqg.c.opslog_recording_sent'), colId: 'opslog_recording_sent', headerName: t('rqg.h.opslog_recording_sent'), width: 100, cellClass: qslStatusCellClass, valueGetter: (p) => { const e = (p.data as any)?.extras ?? {}; return e['APP_OPSLOG_RECORDING_SENT'] ? 'Y' : 'N'; }, defaultVisible: false },
|
{ group: 'QSL', label: t('rqg.c.opslog_recording_sent'), colId: 'opslog_recording_sent', headerName: t('rqg.h.opslog_recording_sent'), width: 100, cellClass: qslStatusCellClass, valueGetter: (p) => { const e = (p.data as any)?.extras ?? {}; return e['APP_OPSLOG_RECORDING_SENT'] ? 'Y' : 'N'; }, defaultVisible: false },
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const FIELDS: Record<string, { sent: string; rcvd: string }> = {
|
|||||||
// internal/award/award.go, which reads the same keys for award confirmations.
|
// internal/award/award.go, which reads the same keys for award confirmations.
|
||||||
// One vocabulary, two readers.
|
// One vocabulary, two readers.
|
||||||
const HAMLOG_SENT = 'APP_OPSLOG_HAMLOG_SENT';
|
const HAMLOG_SENT = 'APP_OPSLOG_HAMLOG_SENT';
|
||||||
const HAMLOG_RCVD = ['APP_OPSLOG_HAMLOG_QSL', 'APP_HAMLOG_QSL', 'APP_HAMLOGONLINE_QSL', 'HAMLOG_QSL_RCVD'];
|
const HAMLOG_RCVD = ['APP_HAMLOG_QSO_CFM', 'APP_OPSLOG_HAMLOG_QSL', 'APP_HAMLOG_QSL', 'APP_HAMLOGONLINE_QSL', 'HAMLOG_QSL_RCVD'];
|
||||||
|
|
||||||
// hamlogState reads a row's extras. Any value that is not an explicit "no"
|
// hamlogState reads a row's extras. Any value that is not an explicit "no"
|
||||||
// counts, because their export could carry a date or a match id rather than Y.
|
// counts, because their export could carry a date or a match id rather than Y.
|
||||||
|
|||||||
+16
-11
@@ -1502,18 +1502,23 @@ func confirmed(q *qso.QSO, sources []string, d *Def) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// HamlogQSLKey is where a HAMLOG.online confirmation is recorded, as an ADIF
|
// HamlogQSLKey is where a HAMLOG.online confirmation is recorded.
|
||||||
// extras key. Exported and re-imported like any other extra, so the state
|
|
||||||
// survives a move to another logger and back.
|
|
||||||
const HamlogQSLKey = "APP_OPSLOG_HAMLOG_QSL"
|
|
||||||
|
|
||||||
// hamlogAltKeys are the shapes an ADIF exported BY hamlog.online might use.
|
|
||||||
//
|
//
|
||||||
// Their site publishes no field name, so rather than demand that an operator
|
// THEIR field name, taken from a real export:
|
||||||
// rename a column by hand after every export, the ones an export could
|
//
|
||||||
// plausibly carry are accepted too. Costs three map lookups; saves a support
|
// <APP_HAMLOG_QSO_CFM:1>Y
|
||||||
// thread that would end in "edit your ADIF".
|
//
|
||||||
var hamlogAltKeys = []string{"APP_HAMLOG_QSL", "APP_HAMLOGONLINE_QSL", "HAMLOG_QSL_RCVD"}
|
// Using the site's own key rather than one of ours is what makes an import
|
||||||
|
// simply work: a log downloaded from HAMLOG carries its confirmations into
|
||||||
|
// OpsLog with nothing to rename and no mapping to configure. The guesses this
|
||||||
|
// replaced (APP_HAMLOG_QSL and friends) were all wrong, which is the argument
|
||||||
|
// for reading a real file before naming a field.
|
||||||
|
const HamlogQSLKey = "APP_HAMLOG_QSO_CFM"
|
||||||
|
|
||||||
|
// hamlogAltKeys are older or hand-written spellings still honoured on read.
|
||||||
|
// APP_OPSLOG_HAMLOG_QSL is the one OpsLog itself wrote before a real export was
|
||||||
|
// available; a log stamped with it keeps counting.
|
||||||
|
var hamlogAltKeys = []string{"APP_OPSLOG_HAMLOG_QSL", "APP_HAMLOG_QSL", "APP_HAMLOGONLINE_QSL", "HAMLOG_QSL_RCVD"}
|
||||||
|
|
||||||
// hamlogConfirmed reports whether a QSO carries a HAMLOG.online confirmation.
|
// hamlogConfirmed reports whether a QSO carries a HAMLOG.online confirmation.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -95,3 +95,30 @@ func TestHamlogConfirmationSource(t *testing.T) {
|
|||||||
t.Error("a hamlog confirmation counted for an award that only accepts LoTW")
|
t.Error("a hamlog confirmation counted for an award that only accepts LoTW")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The field name is theirs, taken from a real export:
|
||||||
|
//
|
||||||
|
// <APP_HAMLOG_QSO_CFM:1>Y
|
||||||
|
//
|
||||||
|
// Pinned as a test because it was GUESSED wrong first — an import that carries
|
||||||
|
// its confirmations into OpsLog with nothing to rename is the whole point, and
|
||||||
|
// a renamed constant would break it in silence.
|
||||||
|
func TestHamlogUsesTheSitesOwnFieldName(t *testing.T) {
|
||||||
|
if HamlogQSLKey != "APP_HAMLOG_QSO_CFM" {
|
||||||
|
t.Fatalf("HamlogQSLKey = %q — their export writes APP_HAMLOG_QSO_CFM", HamlogQSLKey)
|
||||||
|
}
|
||||||
|
def := Def{Confirm: []string{"hamlog"}}
|
||||||
|
// The record as HAMLOG exports it.
|
||||||
|
q := &qso.QSO{Extras: map[string]string{
|
||||||
|
"APP_HAMLOG_R150COUNTRY": "Russia",
|
||||||
|
"APP_HAMLOG_QSO_CFM": "Y",
|
||||||
|
}}
|
||||||
|
if !Confirmed(q, def, def.Confirm) {
|
||||||
|
t.Error("a real HAMLOG export did not confirm")
|
||||||
|
}
|
||||||
|
// Anything OpsLog stamped before that export existed keeps counting.
|
||||||
|
old := &qso.QSO{Extras: map[string]string{"APP_OPSLOG_HAMLOG_QSL": "Y"}}
|
||||||
|
if !Confirmed(old, def, def.Confirm) {
|
||||||
|
t.Error("a QSO stamped with the older key stopped counting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ func TestHamlogFieldsAreBulkEditable(t *testing.T) {
|
|||||||
for field, want := range map[string]string{
|
for field, want := range map[string]string{
|
||||||
"hamlog_sent": "APP_OPSLOG_HAMLOG_SENT",
|
"hamlog_sent": "APP_OPSLOG_HAMLOG_SENT",
|
||||||
"hamlog_sent_date": "APP_OPSLOG_HAMLOG_SENT_DATE",
|
"hamlog_sent_date": "APP_OPSLOG_HAMLOG_SENT_DATE",
|
||||||
"hamlog_rcvd": "APP_OPSLOG_HAMLOG_QSL",
|
// Their field name — see award.HamlogQSLKey. Editing it by hand and
|
||||||
|
// importing their ADIF must land in the SAME place.
|
||||||
|
"hamlog_rcvd": "APP_HAMLOG_QSO_CFM",
|
||||||
"hamlog_rcvd_date": "APP_OPSLOG_HAMLOG_QSL_DATE",
|
"hamlog_rcvd_date": "APP_OPSLOG_HAMLOG_QSL_DATE",
|
||||||
} {
|
} {
|
||||||
if got := BulkExtraKey(field); got != want {
|
if got := BulkExtraKey(field); got != want {
|
||||||
|
|||||||
+1
-1
@@ -920,7 +920,7 @@ var bulkEditableExtras = map[string]string{
|
|||||||
// operator asked of it.
|
// operator asked of it.
|
||||||
"hamlog_sent": "APP_OPSLOG_HAMLOG_SENT",
|
"hamlog_sent": "APP_OPSLOG_HAMLOG_SENT",
|
||||||
"hamlog_sent_date": "APP_OPSLOG_HAMLOG_SENT_DATE",
|
"hamlog_sent_date": "APP_OPSLOG_HAMLOG_SENT_DATE",
|
||||||
"hamlog_rcvd": "APP_OPSLOG_HAMLOG_QSL",
|
"hamlog_rcvd": "APP_HAMLOG_QSO_CFM",
|
||||||
"hamlog_rcvd_date": "APP_OPSLOG_HAMLOG_QSL_DATE",
|
"hamlog_rcvd_date": "APP_OPSLOG_HAMLOG_QSL_DATE",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user