This commit is contained in:
2026-06-07 01:11:37 +02:00
parent 17f7a00bd7
commit 16c04fc12b
13 changed files with 418 additions and 52 deletions
+43 -1
View File
@@ -152,7 +152,7 @@ func Migrate(defs []Def) ([]Def, bool) {
func Fields() []string {
return []string{
"dxcc", "cqz", "ituz", "prefix", "callsign",
"state", "cont", "country", "grid",
"state", "cont", "country", "grid", "grid4",
"iota", "sota_ref", "pota_ref", "wwff",
"name", "qth", "address", "comment", "note",
}
@@ -421,6 +421,12 @@ func MatchQSO(d Def, metas []RefMeta, q *qso.QSO) []string {
// sources (lotw|qsl|eqsl). Exported for the statistics view.
func Confirmed(q *qso.QSO, sources []string) bool { return confirmed(q, sources) }
// InScope reports whether a QSO falls within an award's scope (DXCC entity,
// bands, modes, emission, dates) — independent of whether a reference was
// found. Used to surface "in scope but no reference" gaps (e.g. a French QSO
// missing its department for DDFM).
func InScope(d Def, q *qso.QSO) bool { return inScope(&d, q) }
// EmissionOf maps an ADIF mode to its broad category (CW|PHONE|DIGITAL).
func EmissionOf(mode string) string { return emissionOf(mode) }
@@ -709,6 +715,11 @@ func fieldRaw(field string, q *qso.QSO) string {
return q.Country
case "grid":
return q.Grid
case "grid4":
// VUCC: distinct 4-character grid squares. A QSO on a grid line carries
// several in VUCC_GRIDS; otherwise the 4-char prefix of GRIDSQUARE. The
// comma-joined result is split into one reference per square downstream.
return grid4Refs(q)
case "iota":
return q.IOTA
case "sota_ref":
@@ -738,6 +749,37 @@ func fieldRaw(field string, q *qso.QSO) string {
return ""
}
// grid4Refs returns the distinct 4-character grid squares a QSO contributes —
// from VUCC_GRIDS (a comma list when the contact straddles grid lines) if set,
// else the 4-char prefix of GRIDSQUARE. Joined with commas so the matcher
// counts each square separately (VUCC awards).
func grid4Refs(q *qso.QSO) string {
src := strings.TrimSpace(q.VUCCGrids)
if src == "" {
src = strings.TrimSpace(q.Grid)
}
if src == "" {
return ""
}
seen := map[string]struct{}{}
var out []string
for _, tok := range strings.FieldsFunc(src, func(r rune) bool { return r == ',' || r == ';' || r == ' ' }) {
g := strings.ToUpper(strings.TrimSpace(tok))
if len(g) > 4 {
g = g[:4]
}
if g == "" {
continue
}
if _, ok := seen[g]; ok {
continue
}
seen[g] = struct{}{}
out = append(out, g)
}
return strings.Join(out, ",")
}
func dxccAllowed(dxcc *int, filter []int) bool {
if dxcc == nil {
return false