docs: say that the import option also fills the confirmation statuses
Asked for as a missing feature. It is not missing — the "fill my station fields" option has stamped the default QSL / LoTW / eQSL / Club Log / HRDLog / QRZ.com statuses on empty fields since v0.14. What was missing is any mention of it: the option's description talked only about MY_* fields, so the behaviour was undiscoverable except by comparing a log before and after. The description now says it, in both languages, and names why it matters — a WSJT-X log carries almost no QSL fields. The rule is also pinned by a test now, which meant splitting the fill from the settings lookup: blanks are filled, existing values are never touched. Overwriting them would erase confirmations the operator has actually received, and that is the half a future edit is most likely to get wrong.
This commit is contained in:
@@ -8734,36 +8734,33 @@ func (a *App) applyQSLDefaults(q *qso.QSO) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if q.QSLSent == "" {
|
||||
q.QSLSent = d.QSLSent
|
||||
}
|
||||
if q.QSLRcvd == "" {
|
||||
q.QSLRcvd = d.QSLRcvd
|
||||
}
|
||||
if q.LOTWSent == "" {
|
||||
q.LOTWSent = d.LOTWSent
|
||||
}
|
||||
if q.LOTWRcvd == "" {
|
||||
q.LOTWRcvd = d.LOTWRcvd
|
||||
}
|
||||
if q.EQSLSent == "" {
|
||||
q.EQSLSent = d.EQSLSent
|
||||
}
|
||||
if q.EQSLRcvd == "" {
|
||||
q.EQSLRcvd = d.EQSLRcvd
|
||||
}
|
||||
if q.ClublogUploadStatus == "" {
|
||||
q.ClublogUploadStatus = d.ClublogStatus
|
||||
}
|
||||
if q.HRDLogUploadStatus == "" {
|
||||
q.HRDLogUploadStatus = d.HRDLogStatus
|
||||
}
|
||||
if q.QRZComUploadStatus == "" {
|
||||
q.QRZComUploadStatus = d.QRZComStatus
|
||||
}
|
||||
if q.QRZComDownloadStatus == "" {
|
||||
q.QRZComDownloadStatus = d.QRZComCfm
|
||||
applyQSLDefaultsTo(q, d)
|
||||
}
|
||||
|
||||
// applyQSLDefaultsTo stamps the operator's default confirmation statuses on the
|
||||
// fields a QSO leaves EMPTY. Split from the settings lookup so the rule can be
|
||||
// tested: fill the blanks, never touch a value that is already there.
|
||||
//
|
||||
// The distinction matters on import. A WSJT-X log carries almost no QSL fields,
|
||||
// so without this every imported QSO would sit with blank statuses; a log
|
||||
// exported from another program carries real ones, and overwriting those would
|
||||
// erase confirmations the operator has actually received.
|
||||
func applyQSLDefaultsTo(q *qso.QSO, d QSLDefaults) {
|
||||
fill := func(dst *string, def string) {
|
||||
if *dst == "" {
|
||||
*dst = def
|
||||
}
|
||||
}
|
||||
fill(&q.QSLSent, d.QSLSent)
|
||||
fill(&q.QSLRcvd, d.QSLRcvd)
|
||||
fill(&q.LOTWSent, d.LOTWSent)
|
||||
fill(&q.LOTWRcvd, d.LOTWRcvd)
|
||||
fill(&q.EQSLSent, d.EQSLSent)
|
||||
fill(&q.EQSLRcvd, d.EQSLRcvd)
|
||||
fill(&q.ClublogUploadStatus, d.ClublogStatus)
|
||||
fill(&q.HRDLogUploadStatus, d.HRDLogStatus)
|
||||
fill(&q.QRZComUploadStatus, d.QRZComStatus)
|
||||
fill(&q.QRZComDownloadStatus, d.QRZComCfm)
|
||||
}
|
||||
|
||||
// ── External services (logbook upload) ─────────────────────────────────
|
||||
|
||||
+4
-2
@@ -7,14 +7,16 @@
|
||||
"Split showed the opposite of the radio on some Yaesus. Where the rig reports which VFO transmits rather than a split flag, that has to be compared with the VFO you are listening to — so it read backwards for an operator working on SUB and correctly for one on MAIN. Setting split had the same fault.",
|
||||
"A shared MySQL logbook could fail to build its schema, leaving OpsLog on the local SQLite database with a message that only said the connection failed. Column declarations that are quoted, or share a line with others, escaped the SQLite-to-MySQL type conversion — so MySQL refused an index and a default value. A database already left in that state is now repaired at startup instead of failing at every launch.",
|
||||
"Yaesu: moving both RX and TX to the SUB receiver showed the MAIN frequency. The receive VFO is now read from the command that actually reports it, which also puts split right — split means the transmit VFO differs from the receive one.",
|
||||
"Clicking a spot in DXHunter can now tune the radio: a remote-call UDP packet carrying <FREQ> and <MODE> sets the frequency and mode on whichever CAT backend is active, as well as filling the callsign. A packet without them still just fills the callsign."
|
||||
"Clicking a spot in DXHunter can now tune the radio: a remote-call UDP packet carrying <FREQ> and <MODE> sets the frequency and mode on whichever CAT backend is active, as well as filling the callsign. A packet without them still just fills the callsign.",
|
||||
"The ADIF import option that fills your station fields from your profile also stamps your default confirmation statuses (paper QSL, LoTW, eQSL, Club Log, HRDLog, QRZ.com) on the ones the file leaves empty — it always did, but nothing said so, and a WSJT-X log carries almost none. Existing confirmations are never overwritten."
|
||||
],
|
||||
"fr": [
|
||||
"Cliquer sur un spot du cluster en écoutant sur le VFO SUB ne ramène plus la radio sur le VFO principal. La commande utilisée pour s'accorder agit par définition sur le VFO principal ; sur SUB, c'est désormais le VFO secondaire qui est écrit, sans toucher à la sélection.",
|
||||
"Le split affichait l'inverse de la radio sur certains Yaesu. Quand la radio indique quel VFO émet plutôt qu'un indicateur de split, il faut le comparer au VFO écouté — l'affichage était donc inversé pour un opérateur travaillant sur SUB et correct pour un autre sur MAIN. Le réglage du split souffrait du même défaut.",
|
||||
"Un carnet MySQL partagé pouvait échouer à créer son schéma, laissant OpsLog sur la base SQLite locale avec un message indiquant seulement l'échec de la connexion. Les déclarations de colonnes entre guillemets, ou partageant une ligne avec d'autres, échappaient à la conversion de types SQLite vers MySQL — d'où le refus d'un index et d'une valeur par défaut. Une base déjà dans cet état est désormais réparée au démarrage au lieu d'échouer à chaque lancement.",
|
||||
"Yaesu : passer RX et TX sur le récepteur SUB affichait la fréquence du MAIN. Le VFO de réception est désormais lu sur la commande qui l'indique réellement, ce qui corrige aussi le split — être en split, c'est émettre sur un VFO différent de celui qu'on écoute.",
|
||||
"Cliquer un spot dans DXHunter peut désormais accorder la radio : un paquet UDP remote_call contenant <FREQ> et <MODE> règle la fréquence et le mode sur le backend CAT actif, en plus de renseigner l'indicatif. Un paquet sans ces balises se contente, comme avant, de renseigner l'indicatif."
|
||||
"Cliquer un spot dans DXHunter peut désormais accorder la radio : un paquet UDP remote_call contenant <FREQ> et <MODE> règle la fréquence et le mode sur le backend CAT actif, en plus de renseigner l'indicatif. Un paquet sans ces balises se contente, comme avant, de renseigner l'indicatif.",
|
||||
"L'option d'import ADIF qui remplit vos champs station depuis votre profil applique aussi vos statuts de confirmation par défaut (QSL papier, LoTW, eQSL, Club Log, HRDLog, QRZ.com) sur ceux que le fichier laisse vides — c'était déjà le cas, mais rien ne le disait, et un log WSJT-X n'en contient pratiquement aucun. Les confirmations existantes ne sont jamais écrasées."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -134,7 +134,7 @@ const en: Dict = {
|
||||
'imp.ctyTitle': 'Fix country & zones (cty.dat + ClubLog)',
|
||||
'imp.ctyDesc': "Recompute Country, DXCC & CQ/ITU zones from cty.dat, overriding the file — corrects what contest software exports wrong (e.g. RG2Y as Asiatic instead of European Russia). ClubLog's DXpedition overrides are applied on top per QSO date (e.g. TO974REF → Reunion, TO2A 2012 → French Guiana) whenever the ClubLog data is downloaded. Everything else in the ADIF is kept as-is. Tip: use Update duplicates to re-fix QSOs already in your log.",
|
||||
'imp.stationTitle': 'Fill my station fields from my profile',
|
||||
'imp.stationDesc': "Backfill empty MY_* fields (my grid, rig, antenna, address, city, state, county, SOTA/POTA ref, TX power…) plus Operator and Owner callsign from your active profile. Existing values are kept. Only STATION_CALLSIGN is left untouched so a mixed-call log isn't re-routed. Enable when importing your own log.",
|
||||
'imp.stationDesc': "Backfill empty MY_* fields (my grid, rig, antenna, address, city, state, county, SOTA/POTA ref, TX power…) plus Operator and Owner callsign from your active profile. Existing values are kept. Only STATION_CALLSIGN is left untouched so a mixed-call log isn't re-routed. It ALSO stamps your default confirmation statuses (paper QSL, LoTW, eQSL, Club Log, HRDLog, QRZ.com sent and received) on the ones the file leaves empty — a WSJT-X log carries almost none. Enable when importing your own log.",
|
||||
'imp.cancel': 'Cancel', 'imp.import': 'Import',
|
||||
'imp.progressTitle': 'Importing ADIF…',
|
||||
'imp.progressCount': '{done} / {tot} records · {pct}%', 'imp.progressCountOnly': '{done} records…',
|
||||
@@ -545,7 +545,7 @@ const fr: Dict = {
|
||||
'imp.ctyTitle': 'Corriger pays & zones (cty.dat + ClubLog)',
|
||||
'imp.ctyDesc': "Recalcule Pays, DXCC & zones CQ/ITU depuis cty.dat, en écrasant le fichier — corrige ce que les logiciels de contest exportent mal (ex. RG2Y en Russie asiatique au lieu d'européenne). Les exceptions DXpédition de ClubLog s'appliquent par-dessus selon la date du QSO (ex. TO974REF → Réunion, TO2A 2012 → Guyane française) dès que les données ClubLog sont téléchargées. Tout le reste de l'ADIF est conservé tel quel. Astuce : utilise Mettre à jour les doublons pour re-corriger des QSO déjà dans le log.",
|
||||
'imp.stationTitle': 'Remplir mes champs station depuis mon profil',
|
||||
'imp.stationDesc': "Complète les champs MY_* vides (locator, rig, antenne, adresse, ville, état, comté, réf. SOTA/POTA, puissance TX…) plus Opérateur et Indicatif propriétaire depuis le profil actif. Les valeurs existantes sont conservées. Seul STATION_CALLSIGN n'est jamais touché pour ne pas re-router un log multi-indicatifs. À activer quand tu importes ton propre log.",
|
||||
'imp.stationDesc': "Complète les champs MY_* vides (locator, rig, antenne, adresse, ville, état, comté, réf. SOTA/POTA, puissance TX…) plus Opérateur et Indicatif propriétaire depuis le profil actif. Les valeurs existantes sont conservées. Seul STATION_CALLSIGN n'est jamais touché pour ne pas re-router un log multi-indicatifs. Elle applique AUSSI tes statuts de confirmation par défaut (QSL papier, LoTW, eQSL, Club Log, HRDLog, QRZ.com envoyé et reçu) sur ceux que le fichier laisse vides — un log WSJT-X n'en contient pratiquement aucun. À activer quand tu importes ton propre log.",
|
||||
'imp.cancel': 'Annuler', 'imp.import': 'Importer',
|
||||
'imp.progressTitle': 'Import ADIF en cours…',
|
||||
'imp.progressCount': '{done} / {tot} enregistrements · {pct}%', 'imp.progressCountOnly': '{done} enregistrements…',
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"hamlog/internal/qso"
|
||||
)
|
||||
|
||||
// The confirmation statuses an import fills in, and the ones it must not touch.
|
||||
//
|
||||
// An operator asked for this feature believing it missing — it has been there
|
||||
// since v0.14, but the option's description never mentioned it, so nobody could
|
||||
// know. The behaviour is pinned here so a future edit cannot quietly drop what
|
||||
// the description now promises.
|
||||
//
|
||||
// The rule is fill-if-empty: a WSJT-X log carries almost no QSL fields, while a
|
||||
// log exported from another program carries real ones that must survive.
|
||||
func TestQSLDefaultsFillOnlyEmptyFields(t *testing.T) {
|
||||
defaults := QSLDefaults{
|
||||
QSLSent: "N", QSLRcvd: "N",
|
||||
LOTWSent: "N", LOTWRcvd: "N",
|
||||
EQSLSent: "N", EQSLRcvd: "N",
|
||||
ClublogStatus: "N", HRDLogStatus: "N",
|
||||
QRZComStatus: "N", QRZComCfm: "N",
|
||||
}
|
||||
|
||||
// A record as WSJT-X writes it: no confirmation fields at all.
|
||||
bare := &qso.QSO{Callsign: "F4XYZ"}
|
||||
applyQSLDefaultsTo(bare, defaults)
|
||||
for name, got := range map[string]string{
|
||||
"qsl_sent": bare.QSLSent, "qsl_rcvd": bare.QSLRcvd,
|
||||
"lotw_sent": bare.LOTWSent, "lotw_rcvd": bare.LOTWRcvd,
|
||||
"eqsl_sent": bare.EQSLSent, "eqsl_rcvd": bare.EQSLRcvd,
|
||||
"clublog": bare.ClublogUploadStatus, "hrdlog": bare.HRDLogUploadStatus,
|
||||
"qrz_sent": bare.QRZComUploadStatus, "qrz_rcvd": bare.QRZComDownloadStatus,
|
||||
} {
|
||||
if got != "N" {
|
||||
t.Errorf("%s = %q on an empty record, want the default \"N\"", name, got)
|
||||
}
|
||||
}
|
||||
|
||||
// A record that already carries confirmations — from a real logbook export.
|
||||
// Overwriting these would erase QSLs the operator has actually received.
|
||||
kept := &qso.QSO{
|
||||
Callsign: "F4XYZ",
|
||||
QSLRcvd: "Y", LOTWRcvd: "Y", EQSLSent: "Y",
|
||||
ClublogUploadStatus: "Y", QRZComDownloadStatus: "Y",
|
||||
}
|
||||
applyQSLDefaultsTo(kept, defaults)
|
||||
for name, got := range map[string]string{
|
||||
"qsl_rcvd": kept.QSLRcvd, "lotw_rcvd": kept.LOTWRcvd, "eqsl_sent": kept.EQSLSent,
|
||||
"clublog": kept.ClublogUploadStatus, "qrz_rcvd": kept.QRZComDownloadStatus,
|
||||
} {
|
||||
if got != "Y" {
|
||||
t.Errorf("%s = %q — an existing confirmation was overwritten", name, got)
|
||||
}
|
||||
}
|
||||
// …while its empty ones still get the default.
|
||||
if kept.QSLSent != "N" || kept.LOTWSent != "N" {
|
||||
t.Errorf("empty fields on a partly-filled record were not defaulted: qsl_sent=%q lotw_sent=%q",
|
||||
kept.QSLSent, kept.LOTWSent)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user