feat: ADIF export field picker — choose exactly which fields to write
Global "Export to ADIF" gains a third option "Choose fields…" next to the standard-only and full-OpsLog choices, and right-clicking selected QSOs adds "Export selected — choose fields…". The picker separates official ADIF 3.1.7 fields (grouped by category) from OpsLog/non-standard tags actually present in the log (discovered via DistinctExtraKeys over extras_json), with All/None per group and reset-to-defaults; the selection is persisted per user. Backend: adif.Exporter gains a Fields allow-set that gates every promoted and extras field in writeRecord; app.go ExportADIF/ExportADIFFiltered/ ExportADIFSelected take a fields []string param, plus new ADIFExtraFields.
This commit is contained in:
@@ -5465,37 +5465,55 @@ func (a *App) ADIFFields() []adif.FieldDef { return adif.Fields }
|
||||
// ADIFVersion returns the ADIF spec version OpsLog conforms to (e.g. "3.1.7").
|
||||
func (a *App) ADIFVersion() string { return adif.ADIFVersion() }
|
||||
|
||||
// adifFieldSet turns a list of ADIF tag names into an allow-set (uppercased),
|
||||
// or nil when the list is empty — nil means "all fields" (standard/full mode),
|
||||
// so the two-mode export path is unchanged when no explicit field list is given.
|
||||
func adifFieldSet(fields []string) map[string]bool {
|
||||
if len(fields) == 0 {
|
||||
return nil
|
||||
}
|
||||
set := make(map[string]bool, len(fields))
|
||||
for _, f := range fields {
|
||||
if t := strings.ToUpper(strings.TrimSpace(f)); t != "" {
|
||||
set[t] = true
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// ExportADIF writes every QSO to the given file path in ADIF 3.1 format.
|
||||
// Streams from DB so memory stays flat even with 100k+ records.
|
||||
// includeAppFields=false → portable standard ADIF (for other loggers);
|
||||
// true → full export keeping OpsLog/app-specific APP_* fields (round-trip).
|
||||
func (a *App) ExportADIF(path string, includeAppFields bool) (adif.ExportResult, error) {
|
||||
// fields (optional): when non-empty, export EXACTLY those ADIF tags — the
|
||||
// "choose which fields to export" mode (overrides includeAppFields).
|
||||
func (a *App) ExportADIF(path string, includeAppFields bool, fields []string) (adif.ExportResult, error) {
|
||||
if a.qso == nil {
|
||||
return adif.ExportResult{}, fmt.Errorf("db not initialized")
|
||||
}
|
||||
if path == "" {
|
||||
return adif.ExportResult{}, fmt.Errorf("empty path")
|
||||
}
|
||||
ex := &adif.Exporter{Repo: a.qso, AppName: "OpsLog", AppVersion: "0.1", IncludeAppFields: includeAppFields}
|
||||
ex := &adif.Exporter{Repo: a.qso, AppName: "OpsLog", AppVersion: "0.1", IncludeAppFields: includeAppFields, Fields: adifFieldSet(fields)}
|
||||
return ex.ExportFile(a.ctx, path)
|
||||
}
|
||||
|
||||
// ExportADIFFiltered writes the QSOs matching the current filter to path, with
|
||||
// NO row limit (the on-screen list is capped by the threshold; this is not).
|
||||
func (a *App) ExportADIFFiltered(path string, includeAppFields bool, f qso.QueryFilter) (adif.ExportResult, error) {
|
||||
func (a *App) ExportADIFFiltered(path string, includeAppFields bool, f qso.QueryFilter, fields []string) (adif.ExportResult, error) {
|
||||
if a.qso == nil {
|
||||
return adif.ExportResult{}, fmt.Errorf("db not initialized")
|
||||
}
|
||||
if path == "" {
|
||||
return adif.ExportResult{}, fmt.Errorf("empty path")
|
||||
}
|
||||
ex := &adif.Exporter{Repo: a.qso, AppName: "OpsLog", AppVersion: "0.1", IncludeAppFields: includeAppFields}
|
||||
ex := &adif.Exporter{Repo: a.qso, AppName: "OpsLog", AppVersion: "0.1", IncludeAppFields: includeAppFields, Fields: adifFieldSet(fields)}
|
||||
return ex.ExportFileFiltered(a.ctx, path, f)
|
||||
}
|
||||
|
||||
// ExportADIFSelected writes only the QSOs whose ids are given (the rows the
|
||||
// operator highlighted in the grid).
|
||||
func (a *App) ExportADIFSelected(path string, includeAppFields bool, ids []int64) (adif.ExportResult, error) {
|
||||
func (a *App) ExportADIFSelected(path string, includeAppFields bool, ids []int64, fields []string) (adif.ExportResult, error) {
|
||||
if a.qso == nil {
|
||||
return adif.ExportResult{}, fmt.Errorf("db not initialized")
|
||||
}
|
||||
@@ -5505,10 +5523,32 @@ func (a *App) ExportADIFSelected(path string, includeAppFields bool, ids []int64
|
||||
if len(ids) == 0 {
|
||||
return adif.ExportResult{}, fmt.Errorf("no QSOs selected")
|
||||
}
|
||||
ex := &adif.Exporter{Repo: a.qso, AppName: "OpsLog", AppVersion: "0.1", IncludeAppFields: includeAppFields}
|
||||
ex := &adif.Exporter{Repo: a.qso, AppName: "OpsLog", AppVersion: "0.1", IncludeAppFields: includeAppFields, Fields: adifFieldSet(fields)}
|
||||
return ex.ExportFileByIDs(a.ctx, path, ids)
|
||||
}
|
||||
|
||||
// ADIFExtraFields returns the distinct NON-standard ADIF tags actually present
|
||||
// in the logbook's stored extras (APP_* and vendor tags) — the "OpsLog / extra
|
||||
// fields" group in the export field picker, alongside the static official-ADIF
|
||||
// list from ADIFFields(). Sampled + capped so it stays fast on a big remote log.
|
||||
func (a *App) ADIFExtraFields() []string {
|
||||
if a.qso == nil {
|
||||
return nil
|
||||
}
|
||||
keys, err := a.qso.DistinctExtraKeys(a.ctx, 5000)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
if t := strings.ToUpper(strings.TrimSpace(k)); t != "" && !adif.IsStandardField(t) {
|
||||
out = append(out, t)
|
||||
}
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// CabrilloResult reports how many QSOs a Cabrillo export wrote and where.
|
||||
type CabrilloResult struct {
|
||||
Count int `json:"count"`
|
||||
|
||||
Reference in New Issue
Block a user