feat: While importing ADIF, update MY fields
This commit is contained in:
@@ -25,6 +25,7 @@ import (
|
||||
"hamlog/internal/backup"
|
||||
"hamlog/internal/cat"
|
||||
"hamlog/internal/clublog"
|
||||
"hamlog/internal/cwdecode"
|
||||
"hamlog/internal/cluster"
|
||||
"hamlog/internal/db"
|
||||
"hamlog/internal/dxcc"
|
||||
@@ -378,8 +379,10 @@ type App struct {
|
||||
ubFollowStop chan struct{} // stops the "follow frequency" loop; nil when off
|
||||
audioMgr *audio.Manager
|
||||
qsoRec *audio.Recorder // continuous QSO recorder (rolling pre-roll)
|
||||
cwMu sync.Mutex // guards the CW decoder lifecycle
|
||||
cwStop chan struct{} // stops the CW decoder capture loop; nil when off
|
||||
cwMu sync.Mutex // guards the CW decoder lifecycle
|
||||
cwStop chan struct{} // stops the CW decoder capture loop; nil when off
|
||||
cwDecoder *cwdecode.Decoder // live decoder (for retargeting the pitch)
|
||||
cwPitchHz int // manual pitch override (0 = auto / follow Flex)
|
||||
dvkRecSlot int // slot currently being recorded (DVKStartRecord → DVKStopRecord)
|
||||
dvkPttKeyed bool // we keyed PTT for a voice message; unkey when it ends
|
||||
pttMu sync.Mutex
|
||||
@@ -1481,7 +1484,7 @@ func (a *App) AddQSO(q qso.QSO) (id int64, err error) {
|
||||
}
|
||||
}
|
||||
}()
|
||||
a.applyStationDefaults(&q)
|
||||
a.applyStationDefaults(&q, true)
|
||||
a.applyDXCCNumber(&q)
|
||||
a.applyClublogException(&q, false) // override entity for date-ranged DXpeditions
|
||||
a.refineDistrictZones(&q) // W6 → CQ3/ITU6 for zone-split countries
|
||||
@@ -1604,7 +1607,7 @@ func (a *App) refineDistrictZones(q *qso.QSO) {
|
||||
// currently-active profile's values. Multi-profile support means a user
|
||||
// can be /P with a different callsign + grid + SOTA ref than home — the
|
||||
// QSO carries whichever profile was selected at log time.
|
||||
func (a *App) applyStationDefaults(q *qso.QSO) {
|
||||
func (a *App) applyStationDefaults(q *qso.QSO, includeIdentity bool) {
|
||||
if a.profiles == nil {
|
||||
return
|
||||
}
|
||||
@@ -1612,15 +1615,17 @@ func (a *App) applyStationDefaults(q *qso.QSO) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if q.StationCallsign == "" {
|
||||
// STATION_CALLSIGN drives upload routing, so only stamp it on NEW QSOs — on
|
||||
// import backfill, stamping the active call onto a QSO that lacked one could
|
||||
// misroute it in a mixed-call log.
|
||||
if includeIdentity && q.StationCallsign == "" {
|
||||
q.StationCallsign = p.Callsign
|
||||
}
|
||||
// OPERATOR and OWNER_CALLSIGN are descriptive (not used for routing), so fill
|
||||
// them whenever empty — including on import.
|
||||
if q.Operator == "" {
|
||||
q.Operator = p.Operator
|
||||
}
|
||||
// OWNER_CALLSIGN is a valid ADIF field but not a promoted column, so it
|
||||
// lives in Extras (exported verbatim, round-trips, and is filterable via
|
||||
// json_extract). Stamp it from the active profile when set.
|
||||
if strings.TrimSpace(p.OwnerCallsign) != "" {
|
||||
if q.Extras == nil {
|
||||
q.Extras = map[string]string{}
|
||||
@@ -3477,17 +3482,19 @@ func (a *App) OpenADIFFile() (string, error) {
|
||||
// cty.dat for every record, overriding what the file carries — corrects the
|
||||
// wrong COUNTRY that contest software often exports (e.g. RG2Y as Asiatic
|
||||
// Russia). Everything else in the ADIF is still preserved verbatim.
|
||||
func (a *App) ImportADIF(path string, dupMode string, applyCty bool) (adif.ImportResult, error) {
|
||||
func (a *App) ImportADIF(path string, dupMode string, applyCty bool, applyStation bool) (adif.ImportResult, error) {
|
||||
if a.qso == nil {
|
||||
return adif.ImportResult{}, fmt.Errorf("db not initialized")
|
||||
}
|
||||
if path == "" {
|
||||
return adif.ImportResult{}, fmt.Errorf("empty path")
|
||||
}
|
||||
// Import preserves the ADIF verbatim — NO station / confirmation defaults
|
||||
// are applied. Defaults are for NEW QSOs only (manual entry + UDP auto-log);
|
||||
// stamping them on a historical import would, e.g., flag old QSOs as
|
||||
// "LoTW requested" and try to re-upload them.
|
||||
// Import preserves the ADIF verbatim by default — confirmation/sent-status
|
||||
// defaults are NEVER applied (they'd flag old QSOs "LoTW requested" and try to
|
||||
// re-upload). When applyStation is on, we DO backfill empty MY_* station
|
||||
// fields (grid/rig/antenna/QTH/address…) from the active profile — those are
|
||||
// descriptive metadata and safe to fill (identity fields are still left
|
||||
// alone, see applyStationDefaults).
|
||||
im := &adif.Importer{Repo: a.qso}
|
||||
switch dupMode {
|
||||
case "update":
|
||||
@@ -3508,11 +3515,18 @@ func (a *App) ImportADIF(path string, dupMode string, applyCty bool) (adif.Impor
|
||||
_ = a.clublog.EnsureLoaded()
|
||||
}
|
||||
clLoaded := a.clublog != nil && a.clublog.Loaded()
|
||||
if applyCty {
|
||||
if applyCty || applyStation {
|
||||
im.Enrich = func(q *qso.QSO) {
|
||||
a.enrichContactedFromCtyForce(q)
|
||||
if clLoaded {
|
||||
a.applyClublogException(q, true) // force: explicit import-time correction
|
||||
if applyCty {
|
||||
a.enrichContactedFromCtyForce(q)
|
||||
if clLoaded {
|
||||
a.applyClublogException(q, true) // force: explicit import-time correction
|
||||
}
|
||||
}
|
||||
if applyStation {
|
||||
// Backfill empty MY_* descriptive fields from the active profile
|
||||
// (identity fields left alone to keep mixed-call routing intact).
|
||||
a.applyStationDefaults(q, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6328,7 +6342,7 @@ func (a *App) LogUDPLoggedADIF(adifText string) (int64, error) {
|
||||
// (station callsign, grid, country, zones, and the profile's default
|
||||
// MY_RIG / MY_ANTENNA) from the active profile. Without this a UDP /
|
||||
// WSJT-X auto-logged QSO carried none of the operator's own data.
|
||||
a.applyStationDefaults(&q)
|
||||
a.applyStationDefaults(&q, true)
|
||||
|
||||
// ── DXCC# + QSL defaults ──
|
||||
// applyDXCCNumber stamps the contacted-station DXCC# from the
|
||||
@@ -6910,6 +6924,55 @@ func (a *App) FlexSetANFLevel(l int) error {
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetANFLevel(l) })
|
||||
}
|
||||
|
||||
func (a *App) FlexSetAPF(on bool) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetAPF(on) })
|
||||
}
|
||||
func (a *App) FlexSetAPFLevel(l int) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetAPFLevel(l) })
|
||||
}
|
||||
func (a *App) FlexSetCWSpeed(wpm int) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetCWSpeed(wpm) })
|
||||
}
|
||||
func (a *App) FlexSetCWPitch(hz int) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetCWPitch(hz) })
|
||||
}
|
||||
func (a *App) FlexSetCWBreakInDelay(ms int) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetCWBreakInDelay(ms) })
|
||||
}
|
||||
func (a *App) FlexSetCWSidetone(on bool) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetCWSidetone(on) })
|
||||
}
|
||||
func (a *App) FlexSetSidetoneLevel(l int) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetSidetoneLevel(l) })
|
||||
}
|
||||
func (a *App) FlexSetCWFilter(bw int) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetCWFilter(bw) })
|
||||
}
|
||||
|
||||
// SwitchCATRig hot-swaps the active OmniRig slot (Rig1 ↔ Rig2) without
|
||||
// requiring a trip through the full Settings panel. Persists the choice
|
||||
// so it survives restart.
|
||||
|
||||
Reference in New Issue
Block a user