feat(bulk): mode, submode and RST are repair fields, so allow them
They were excluded as "per-QSO", alongside callsign and date. That rule confused two different things: bulk edit is not for describing QSOs, it is for REPAIRING a batch of them — an import that mapped every contact to SSB, an ADIF that carried no MODE at all. Refusing because a hundred rows should not normally share a value left the operator editing a hundred rows by hand. Setting the mode CLEARS the submode. A submode belongs to the mode it was recorded under; left behind it contradicts the new one, and "FT8 with a submode of USB" is not a thing — worse, the submode is what most ADIF readers believe. Band is still refused on its own, and that half of the rule stands. It travels with the frequency through BulkSetFrequency, which writes the pair: a band contradicting its own frequency is invalid ADIF, and every export would carry the contradiction out into the world. Callsign and date stay out too — they identify the contact rather than describe it.
This commit is contained in:
+6
-2
@@ -2,8 +2,12 @@
|
||||
{
|
||||
"version": "0.24.6",
|
||||
"date": "",
|
||||
"en": [],
|
||||
"fr": []
|
||||
"en": [
|
||||
"Bulk edit can now set mode, submode and RST. They were excluded as per-QSO fields, which missed the point: bulk edit is for repairing a batch — an import that mapped every contact to SSB, an ADIF with no mode at all — and refusing meant editing a hundred rows one at a time. Setting the mode clears the submode, since one left over from the old mode contradicts the new one. Band stays with frequency, which already sets the two together."
|
||||
],
|
||||
"fr": [
|
||||
"L édition groupée sait enfin régler le mode, le sous-mode et le RST. Ils étaient exclus comme champs propres à chaque QSO, ce qui manquait l essentiel : l édition groupée sert à RÉPARER un lot — un import qui a tout mis en SSB, un ADIF sans aucun mode — et refuser obligeait à corriger cent lignes une par une. Régler le mode efface le sous-mode, celui de l ancien mode contredisant le nouveau. La bande reste avec la fréquence, qui pose déjà les deux ensemble."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.24.5",
|
||||
|
||||
@@ -91,6 +91,17 @@ const FIELDS: FieldDef[] = [
|
||||
{ id: 'iota', label: 'bulk.fIota', group: 'Contacted station', kind: 'text', upper: true },
|
||||
{ id: 'sig', label: 'bulk.fSig', group: 'Contacted station', kind: 'text' },
|
||||
{ id: 'sig_info', label: 'bulk.fSigInfo', group: 'Contacted station', kind: 'text' },
|
||||
// The contact itself — repair fields, not description fields. An import that
|
||||
// mapped every QSO to SSB, or an ADIF with no MODE at all, is fixed here
|
||||
// instead of one row at a time.
|
||||
//
|
||||
// Band is deliberately absent: it travels with the frequency below, because a
|
||||
// band contradicting its own frequency is invalid ADIF and every export would
|
||||
// carry the contradiction.
|
||||
{ id: 'mode', label: 'bulk.fMode', group: 'The contact', kind: 'text', upper: true },
|
||||
{ id: 'submode', label: 'bulk.fSubmode', group: 'The contact', kind: 'text', upper: true },
|
||||
{ id: 'rst_sent', label: 'bulk.fRstSent', group: 'The contact', kind: 'text' },
|
||||
{ id: 'rst_rcvd', label: 'bulk.fRstRcvd', group: 'The contact', kind: 'text' },
|
||||
// Misc
|
||||
// Frequency (MHz) — sets freq_hz AND recomputes band. Main use: fixing a batch
|
||||
// logged on a stale/default frequency after CAT dropped.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
|
||||
package qso
|
||||
|
||||
import "testing"
|
||||
|
||||
// Mode, submode and RST are repair fields: an import that mapped every contact
|
||||
// to SSB, or an ADIF that carried no MODE, is fixed in one pass instead of one
|
||||
// row at a time. They were excluded as "per-QSO", which confused describing a
|
||||
// QSO with repairing a batch of them.
|
||||
func TestModeAndRSTAreBulkEditable(t *testing.T) {
|
||||
for _, col := range []string{"mode", "submode", "rst_sent", "rst_rcvd"} {
|
||||
if !bulkEditableCols[col] {
|
||||
t.Errorf("%s should be bulk-editable", col)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Band must NOT be bulk-editable on its own: it travels with the frequency
|
||||
// through BulkSetFrequency. A band contradicting its own frequency is invalid
|
||||
// ADIF, and every export would carry the contradiction out into the world.
|
||||
func TestBandIsNotBulkEditableAlone(t *testing.T) {
|
||||
for _, col := range []string{"band", "freq_hz", "callsign", "qso_date"} {
|
||||
if bulkEditableCols[col] {
|
||||
t.Errorf("%s must not be bulk-editable on its own", col)
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
-8
@@ -734,13 +734,26 @@ func (r *Repo) MarkEQSLSent(ctx context.Context, id int64, date string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bulkEditableCols whitelists the columns BulkSetField may write. Limited to
|
||||
// TEXT fields where setting one value across many QSOs is meaningful: the
|
||||
// per-service QSL/upload status fields, plus "my station"/operator fields that
|
||||
// are naturally constant across a run (grid, antenna, rig, address, …). It
|
||||
// deliberately excludes per-QSO fields (callsign, band, mode, date, RST, the
|
||||
// contacted station's details) and numeric columns (power, zones, lat/lon),
|
||||
// which would be corrupted or meaningless if bulk-set to a single value.
|
||||
// bulkEditableCols whitelists the columns BulkSetField may write.
|
||||
//
|
||||
// Mostly TEXT fields where one value across many QSOs is meaningful: the
|
||||
// per-service QSL/upload status fields, plus "my station"/operator fields
|
||||
// naturally constant across a run (grid, antenna, rig, address, …).
|
||||
//
|
||||
// Mode, submode and RST are here too, which the original rule excluded as
|
||||
// "per-QSO". That rule confused two different things. Bulk edit is not for
|
||||
// describing QSOs, it is for REPAIRING a batch — an import that mapped every
|
||||
// contact to SSB, an ADIF with no MODE at all — and refusing to fix a hundred
|
||||
// rows because a hundred rows should not normally share a value leaves the
|
||||
// operator editing them one at a time.
|
||||
//
|
||||
// Band is NOT here, and frequency is not either: both go through
|
||||
// BulkSetFrequency, which writes the pair together. A band that contradicts its
|
||||
// own frequency is invalid ADIF, and every export would carry the contradiction.
|
||||
//
|
||||
// Still excluded, and this part of the rule stands: callsign and date, which
|
||||
// identify the contact rather than describe it, and the numeric columns (power,
|
||||
// zones, lat/lon) that are meaningless shared.
|
||||
var bulkEditableCols = map[string]bool{
|
||||
// QSL / upload status
|
||||
"lotw_sent": true,
|
||||
@@ -820,6 +833,14 @@ var bulkEditableCols = map[string]bool{
|
||||
"iota": true,
|
||||
"sig": true,
|
||||
"sig_info": true,
|
||||
// The contact itself. Repair fields: an import that mapped everything to SSB,
|
||||
// or an ADIF that carried no MODE. Setting mode CLEARS submode (see
|
||||
// BulkSetField) — a submode left over from the old mode contradicts the new
|
||||
// one, and "FT8 / USB" is not a thing.
|
||||
"mode": true,
|
||||
"submode": true,
|
||||
"rst_sent": true,
|
||||
"rst_rcvd": true,
|
||||
// Misc text
|
||||
"comment": true,
|
||||
"notes": true,
|
||||
@@ -843,8 +864,16 @@ func (r *Repo) BulkSetField(ctx context.Context, ids []int64, column, value stri
|
||||
ph[i] = "?"
|
||||
args = append(args, id)
|
||||
}
|
||||
set := column + " = ?, updated_at = ?"
|
||||
if column == "mode" {
|
||||
// A submode belongs to the mode it was recorded under. Left behind, it
|
||||
// contradicts the new one — "FT8" with a submode of "USB" is not a thing,
|
||||
// and it is the submode that most ADIF readers believe. Clearing it is the
|
||||
// only outcome that leaves the row meaning what the operator asked for.
|
||||
set += ", submode = ''"
|
||||
}
|
||||
res, err := r.db.ExecContext(ctx,
|
||||
`UPDATE qso SET `+column+` = ?, updated_at = ? WHERE id IN (`+strings.Join(ph, ",")+`)`,
|
||||
`UPDATE qso SET `+set+` WHERE id IN (`+strings.Join(ph, ",")+`)`,
|
||||
args...)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("bulk set %s: %w", column, err)
|
||||
|
||||
Reference in New Issue
Block a user