fix(entity): show the QSO's own entity, and date the ClubLog exception
Two faults behind one screenshot: 3Y0K, recorded in the log as Bouvet Island, showing the Antarctica matrix. SELECTING A QSO now shows what that QSO records. The panel asked WorkedBefore with no DXCC hint, and a hint of zero makes the backend resolve the entity from cty.dat and the ClubLog exceptions AS THEY ARE TODAY — right for a live contact, wrong for one being looked at in the log. The repo's own "infer from past QSOs" path never ran, because the hint was no longer zero by the time it got there. Hence Antarctica, and "5 QSOs with this entity" against "11 with this call": two different entities, one of them nobody had asked about. The selected row's dxcc now travels with it and is passed as the hint. Browsing the log shows what the log says, even where the log is wrong. Correcting an entity stays a deliberate act — right-click, Update from ClubLog. BACK-ENTERING A QSO now resolves the exception at the CONTACT'S date. An exception carries a validity window and a DXpedition's window closes: 3Y0K typed months later, with the activation's own date in the form, was resolved against today, matched nothing, and fell back to cty.dat. Both callers pass the date they already hold — the entry strip's, and in the editor the record's own. The date is trusted to move the resolution BACKWARDS only. A half-typed "2026-0" must not send the lookup to the year 20, and a mistyped future date must not resolve against a window that has not opened; both fall back to now. Midday rather than midnight, because a window given in whole days is inclusive of its end date and 00:00 sits exactly on the boundary.
This commit is contained in:
@@ -7127,8 +7127,14 @@ func (a *App) SaveCabrilloFile() (string, error) {
|
||||
// LookupCallsign returns the cached or freshly-fetched info for a callsign.
|
||||
// Errors are returned as-is to the frontend; ErrNotFound surfaces as
|
||||
// "callsign not found".
|
||||
func (a *App) LookupCallsign(callsign string) (lookup.Result, error) {
|
||||
return a.lookupCallsign(callsign, false)
|
||||
// qsoDate is the entry form's date as "YYYY-MM-DD" (or empty for now). It is
|
||||
// what the ClubLog exception is resolved AGAINST: an exception has a validity
|
||||
// window, and a DXpedition's window closes. Entering 3Y0K by hand months later,
|
||||
// with the activation's own date in the form, resolved the exception at TODAY's
|
||||
// date, found none, and fell back to cty.dat — Antarctica instead of Bouvet
|
||||
// Island. A backdated QSO must be enriched as of when it happened.
|
||||
func (a *App) LookupCallsign(callsign string, qsoDate string) (lookup.Result, error) {
|
||||
return a.lookupCallsign(callsign, false, qsoDate)
|
||||
}
|
||||
|
||||
// LookupCallsignFresh is the same, but SKIPS the cache and refreshes it.
|
||||
@@ -7139,11 +7145,11 @@ func (a *App) LookupCallsign(callsign string) (lookup.Result, error) {
|
||||
// subscription went on getting the thin free-account record, and deleting the
|
||||
// cached row by hand was the only way out. A deliberate click must reach the
|
||||
// provider and overwrite what was stored.
|
||||
func (a *App) LookupCallsignFresh(callsign string) (lookup.Result, error) {
|
||||
return a.lookupCallsign(callsign, true)
|
||||
func (a *App) LookupCallsignFresh(callsign string, qsoDate string) (lookup.Result, error) {
|
||||
return a.lookupCallsign(callsign, true, qsoDate)
|
||||
}
|
||||
|
||||
func (a *App) lookupCallsign(callsign string, force bool) (lookup.Result, error) {
|
||||
func (a *App) lookupCallsign(callsign string, force bool, qsoDate string) (lookup.Result, error) {
|
||||
if a.lookup == nil {
|
||||
return lookup.Result{}, fmt.Errorf("lookup not initialized")
|
||||
}
|
||||
@@ -7188,10 +7194,15 @@ func (a *App) lookupCallsign(callsign string, force bool) (lookup.Result, error)
|
||||
r.ImageURL = ""
|
||||
}
|
||||
}
|
||||
// ClubLog exception override (live entry → today's date): for an active
|
||||
// DXpedition the entered call gets the right entity/zones immediately.
|
||||
// ClubLog exception override, resolved AT THE QSO'S DATE.
|
||||
//
|
||||
// An exception carries a validity window and a DXpedition's window closes.
|
||||
// Resolving at today's date is right for a contact happening now and wrong
|
||||
// for one being entered afterwards: 3Y0K typed months later, with the
|
||||
// activation's date in the form, found no live exception and fell back to
|
||||
// cty.dat — Antarctica, where the log says Bouvet Island.
|
||||
if a.clublogCtyEnabled() && a.clublog != nil {
|
||||
if e, ok := a.clublog.Resolve(callsign, time.Now().UTC()); ok {
|
||||
if e, ok := a.clublog.Resolve(callsign, lookupWhen(qsoDate)); ok {
|
||||
r.Country = titleEntity(e.Entity)
|
||||
if e.Cont != "" {
|
||||
r.Continent = e.Cont
|
||||
@@ -18512,3 +18523,31 @@ func wsjtLoggedQSO(q qso.QSO) udp.LoggedQSO {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// lookupWhen turns the entry form's date into the instant a ClubLog exception
|
||||
// should be resolved at.
|
||||
//
|
||||
// Empty, unparseable, or in the future → now. A date is only trusted to move
|
||||
// the resolution BACKWARDS: a half-typed "2026-0" must not send the lookup to
|
||||
// the year 20, and a mistyped future date must not resolve against an exception
|
||||
// window that has not opened.
|
||||
//
|
||||
// Midday UTC rather than midnight: an exception window given in whole days is
|
||||
// inclusive of its end date, and resolving at 00:00 of that day sits on the
|
||||
// boundary where an off-by-one in either direction changes the answer.
|
||||
func lookupWhen(qsoDate string) time.Time {
|
||||
now := time.Now().UTC()
|
||||
s := strings.TrimSpace(qsoDate)
|
||||
if s == "" {
|
||||
return now
|
||||
}
|
||||
t, err := time.Parse("2006-01-02", s)
|
||||
if err != nil {
|
||||
return now
|
||||
}
|
||||
t = t.Add(12 * time.Hour)
|
||||
if t.After(now) {
|
||||
return now
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user