feat: Added a test tab in awards to test the matching

This commit is contained in:
2026-07-14 16:32:49 +02:00
parent 4fe0405432
commit 0c6f8e2d68
8 changed files with 574 additions and 13 deletions
+48
View File
@@ -2472,6 +2472,54 @@ func (a *App) mirrorAwardsToFolder(defs []award.Def) {
}
}
// AwardExplain is one QSO run through one award's matching rules, step by step.
// It answers the question the awards grid cannot: not "did it match" but WHY.
type AwardExplain struct {
QSO qso.QSO `json:"qso"`
Explanation award.Explanation `json:"explanation"`
}
// ExplainAward replays an award's rules against the QSOs of one callsign and
// reports every step. Written because three award bugs in a week all presented
// identically — an empty column and no way to see which rule looked where — and
// each took far longer to find than it should have.
//
// Returns the matching QSOs most recent first (a call worked several times can
// match on one QSO and not another, which is itself the answer sometimes).
func (a *App) ExplainAward(code, callsign string) ([]AwardExplain, error) {
if a.qso == nil {
return nil, fmt.Errorf("db not initialized")
}
call := strings.ToUpper(strings.TrimSpace(callsign))
if call == "" {
return nil, fmt.Errorf("enter a callsign to test against")
}
var def *award.Def
defs := a.awardDefs()
for i := range defs {
if strings.EqualFold(strings.TrimSpace(defs[i].Code), strings.TrimSpace(code)) {
def = &defs[i]
break
}
}
if def == nil {
return nil, fmt.Errorf("unknown award %q", code)
}
qsos, err := a.qso.List(a.ctx, qso.ListFilter{Callsign: call, Limit: 20})
if err != nil {
return nil, err
}
if len(qsos) == 0 {
return nil, fmt.Errorf("no QSO with %s in the log", call)
}
metas := a.awardRefMetas([]award.Def{*def})[strings.ToUpper(def.Code)]
out := make([]AwardExplain, 0, len(qsos))
for i := range qsos {
out = append(out, AwardExplain{QSO: qsos[i], Explanation: award.Explain(*def, metas, &qsos[i])})
}
return out, nil
}
// AwardUpdate is a shipped fix an operator has NOT received, because they edited
// the award and we refuse to overwrite their work. Rather than drop the fix
// silently, we offer it: applying is their call, and it costs them their changes.