up
This commit is contained in:
+21
-18
@@ -1241,44 +1241,47 @@ type EntitySlot struct {
|
||||
Slots map[string]map[string]struct{} // band → modes worked
|
||||
}
|
||||
|
||||
// EntitySlotMap returns slot data for every QSO, grouping by entity.
|
||||
// EntitySlotMap returns slot data for every QSO, grouped by DXCC entity NUMBER.
|
||||
//
|
||||
// `resolveEntity` maps a callsign to its canonical entity name (we use
|
||||
// cty.dat for this). When non-nil, the resolved name wins over the
|
||||
// stored `country` column — that's important because QRZ's "Turkey"
|
||||
// disagrees with cty.dat's "Asiatic Turkey" and the cluster status
|
||||
// comparison would otherwise miss past QSOs. When nil, we fall back to
|
||||
// the stored country (useful for tests).
|
||||
// keyFor maps a QSO (its callsign + stored DXCC + stored country) to a DXCC
|
||||
// entity number. Keying by NUMBER — not name — is what makes the cluster
|
||||
// "new / new-band / new-slot" check robust: QRZ's "Turkey" and cty.dat's
|
||||
// "Asiatic Turkey" are the same entity (390), and a logged Lord Howe Island
|
||||
// QSO (stored DXCC 147) matches a VJ2L spot even though cty.dat resolves the
|
||||
// logged callsign "VK2/SP9FIH" to Australia by prefix. The caller decides the
|
||||
// precedence (stored DXCC → stored country → cty.dat prefix). keyFor returning
|
||||
// 0 (unresolvable) skips the QSO.
|
||||
//
|
||||
// One DB scan regardless of input size. Cheap to call per cluster batch.
|
||||
func (r *Repo) EntitySlotMap(ctx context.Context, resolveEntity func(callsign string) string) (map[string]*EntitySlot, error) {
|
||||
func (r *Repo) EntitySlotMap(ctx context.Context, keyFor func(call string, storedDXCC int, country string) int) (map[int]*EntitySlot, error) {
|
||||
rows, err := r.db.QueryContext(ctx,
|
||||
`SELECT callsign, lower(coalesce(country,'')), lower(band), upper(mode) FROM qso
|
||||
`SELECT callsign, coalesce(dxcc,0), lower(coalesce(country,'')), lower(band), upper(mode) FROM qso
|
||||
WHERE band IS NOT NULL AND band != ''
|
||||
AND mode IS NOT NULL AND mode != ''`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := make(map[string]*EntitySlot, 256)
|
||||
out := make(map[int]*EntitySlot, 256)
|
||||
for rows.Next() {
|
||||
var call, country, band, mode string
|
||||
if err := rows.Scan(&call, &country, &band, &mode); err != nil {
|
||||
var storedDXCC int
|
||||
if err := rows.Scan(&call, &storedDXCC, &country, &band, &mode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := country
|
||||
if resolveEntity != nil {
|
||||
if name := strings.ToLower(strings.TrimSpace(resolveEntity(call))); name != "" {
|
||||
key = name
|
||||
}
|
||||
key := 0
|
||||
if keyFor != nil {
|
||||
key = keyFor(call, storedDXCC, country)
|
||||
} else {
|
||||
key = storedDXCC
|
||||
}
|
||||
if key == "" {
|
||||
if key == 0 {
|
||||
continue
|
||||
}
|
||||
e, ok := out[key]
|
||||
if !ok {
|
||||
e = &EntitySlot{
|
||||
Country: key,
|
||||
Country: country,
|
||||
Bands: make(map[string]struct{}),
|
||||
Slots: make(map[string]map[string]struct{}),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user