chore: release v0.23.2
This commit is contained in:
@@ -29,6 +29,13 @@ type Info struct {
|
||||
IsUser bool `json:"is_user"`
|
||||
LastUpload string `json:"last_upload,omitempty"` // YYYY-MM-DD
|
||||
DaysAgo int `json:"days_ago"` // days since last upload (-1 if unknown)
|
||||
// FeedDate is the newest upload date anywhere in ARRL's file, i.e. when ARRL
|
||||
// last regenerated it, and FeedStale says that date is more than two days old.
|
||||
// Without them "last uploaded 7 days ago" is unreadable: it may mean the
|
||||
// station went quiet, or that the FEED has been frozen for a week — which is
|
||||
// exactly what ARRL served on 2026-08-02, its newest line dated 2026-07-27.
|
||||
FeedDate string `json:"feed_date,omitempty"`
|
||||
FeedStale bool `json:"feed_stale"`
|
||||
}
|
||||
|
||||
// Manager holds the parsed list + cache location.
|
||||
@@ -36,6 +43,7 @@ type Manager struct {
|
||||
mu sync.RWMutex
|
||||
users map[string]time.Time // UPPER(callsign) → last-upload date (UTC)
|
||||
updated time.Time // when the cache was last refreshed
|
||||
newest time.Time // newest upload date in the file = when ARRL built it
|
||||
dir string
|
||||
client *http.Client
|
||||
}
|
||||
@@ -100,6 +108,7 @@ func (m *Manager) Download(ctx context.Context) (int, error) {
|
||||
// parse loads the CSV bytes into the map and returns the count.
|
||||
func (m *Manager) parse(data []byte) int {
|
||||
users := make(map[string]time.Time, 1<<20)
|
||||
var newest time.Time
|
||||
sc := bufio.NewScanner(bytes.NewReader(data))
|
||||
sc.Buffer(make([]byte, 1024*1024), 1024*1024)
|
||||
for sc.Scan() {
|
||||
@@ -120,16 +129,29 @@ func (m *Manager) parse(data []byte) int {
|
||||
continue
|
||||
}
|
||||
users[call] = t
|
||||
if t.After(newest) {
|
||||
newest = t
|
||||
}
|
||||
}
|
||||
if len(users) == 0 {
|
||||
return 0
|
||||
}
|
||||
m.mu.Lock()
|
||||
m.users = users
|
||||
m.newest = newest
|
||||
m.mu.Unlock()
|
||||
return len(users)
|
||||
}
|
||||
|
||||
// feedInfo fills the feed-age fields shared by every Lookup result.
|
||||
func (m *Manager) feedInfo(i Info) Info {
|
||||
if !m.newest.IsZero() {
|
||||
i.FeedDate = m.newest.Format("2006-01-02")
|
||||
i.FeedStale = time.Since(m.newest) > 48*time.Hour
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Lookup reports whether callsign is a LoTW user and how long ago it uploaded.
|
||||
// Tries the exact call, then (for portable calls like "EA8/DL1ABC" or "F5ABC/P")
|
||||
// the longest slash-separated segment — the base call LoTW is keyed on.
|
||||
@@ -154,13 +176,13 @@ func (m *Manager) Lookup(callsign string) Info {
|
||||
t, ok = m.users[base]
|
||||
}
|
||||
if !ok {
|
||||
return Info{DaysAgo: -1}
|
||||
return m.feedInfo(Info{DaysAgo: -1})
|
||||
}
|
||||
days := int(time.Since(t).Hours() / 24)
|
||||
if days < 0 {
|
||||
days = 0
|
||||
}
|
||||
return Info{IsUser: true, LastUpload: t.Format("2006-01-02"), DaysAgo: days}
|
||||
return m.feedInfo(Info{IsUser: true, LastUpload: t.Format("2006-01-02"), DaysAgo: days})
|
||||
}
|
||||
|
||||
// Count returns how many callsigns are loaded.
|
||||
|
||||
Reference in New Issue
Block a user