feat(awards): settle each RDA disagreement on its own row
The comparison could show two hundred contacts where the log and the database name different districts, and leave the operator with nothing to do about any of them but open each QSO and edit it by hand. Now each row carries the two values as buttons: click the district you keep, then apply. There are shortcuts for keeping one source everywhere, and applying is a separate act — clicking through two hundred rows with each one written as it is clicked would make a slip permanent before the reading was finished. The choice is written as the contact's AWARD REFERENCE, not over the imported CNTY, and that distinction is the point. CNTY is what the downloaded log said; overwriting it would destroy the evidence the comparison runs on, and the next comparison would agree with itself and prove nothing. The override is the operator's decision, it is what the award engine counts, and it beats both sources. So the log keeps saying what it said, the database keeps saying what it says, and the contact counts for the district that was chosen. The buttons show the districts themselves rather than a tick box: someone arbitrating between RO-19 and KO-05 should be picking a value they can read, not remembering which source a checked box stood for.
This commit is contained in:
@@ -148,3 +148,72 @@ func isHamlogConfirmed(v string) bool {
|
||||
v = strings.ToUpper(strings.TrimSpace(v))
|
||||
return v != "" && v != "N" && v != "NO"
|
||||
}
|
||||
|
||||
// RDAChoice is one arbitrated contact: which district the operator decided is
|
||||
// right for that QSO.
|
||||
type RDAChoice struct {
|
||||
QSOID int64 `json:"qso_id"`
|
||||
District string `json:"district"`
|
||||
}
|
||||
|
||||
// RDAApplyResult reports what an arbitration did.
|
||||
type RDAApplyResult struct {
|
||||
Applied int `json:"applied"`
|
||||
Failed int `json:"failed"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ApplyRDAChoices writes the chosen district onto each contact.
|
||||
//
|
||||
// Written as the AWARD OVERRIDE rather than over the imported CNTY, and the
|
||||
// difference matters. CNTY is what the downloaded log said; it is a record of
|
||||
// what HAMLOG holds, and overwriting it would destroy the very evidence the
|
||||
// comparison was run on — the next comparison would then agree with itself and
|
||||
// prove nothing. The override is the operator's decision, it is what the award
|
||||
// engine counts, and it wins over both sources. So the log keeps saying what it
|
||||
// said, the database keeps saying what it says, and the contact counts for the
|
||||
// district the operator chose.
|
||||
//
|
||||
// A choice equal to what the database already answers is still written, on
|
||||
// purpose: it records that the disagreement was looked at and settled, which a
|
||||
// silent no-op would not.
|
||||
func (a *App) ApplyRDAChoices(choices []RDAChoice) (RDAApplyResult, error) {
|
||||
var res RDAApplyResult
|
||||
if a.qso == nil {
|
||||
return res, fmt.Errorf("db not initialized")
|
||||
}
|
||||
for _, c := range choices {
|
||||
district := strings.ToUpper(strings.TrimSpace(c.District))
|
||||
if !rdaDistrictRe.MatchString(district) {
|
||||
res.Failed++
|
||||
applog.Printf("rda apply: qso %d — %q is not a district reference", c.QSOID, c.District)
|
||||
continue
|
||||
}
|
||||
q, err := a.qso.GetByID(a.ctx, c.QSOID)
|
||||
if err != nil {
|
||||
res.Failed++
|
||||
applog.Printf("rda apply: qso %d could not be read: %v", c.QSOID, err)
|
||||
continue
|
||||
}
|
||||
if q.Extras == nil {
|
||||
q.Extras = map[string]string{}
|
||||
}
|
||||
q.Extras[award.ManualRefsKey] = setOverrideRef(q.Extras[award.ManualRefsKey], "RDA", district)
|
||||
if err := a.qso.Update(a.ctx, q); err != nil {
|
||||
res.Failed++
|
||||
applog.Printf("rda apply: qso %d update failed: %v", c.QSOID, err)
|
||||
continue
|
||||
}
|
||||
res.Applied++
|
||||
}
|
||||
if res.Applied > 0 {
|
||||
// The award totals were computed from the old answers.
|
||||
a.invalidateAwardStats()
|
||||
}
|
||||
applog.Printf("rda apply: %d contacts settled, %d failed", res.Applied, res.Failed)
|
||||
res.Message = fmt.Sprintf("%d settled", res.Applied)
|
||||
if res.Failed > 0 {
|
||||
res.Message += fmt.Sprintf(", %d failed (see the log)", res.Failed)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user