Centralize daypart key canonicalization
This commit is contained in:
@@ -400,7 +400,7 @@ func multipleSummaryDates(summaries []forecast.DailySummary) bool {
|
||||
}
|
||||
|
||||
func daypartKey(daypart forecast.DaypartSummary, prefixDate bool) string {
|
||||
key := normalizedKey(daypart.Name)
|
||||
key := forecast.CanonicalDaypartKey(daypart.Name)
|
||||
if key == "" {
|
||||
key = "unnamed"
|
||||
}
|
||||
@@ -409,21 +409,3 @@ func daypartKey(daypart forecast.DaypartSummary, prefixDate bool) string {
|
||||
}
|
||||
return daypart.Period.Start.Format(timeutil.DateLayout) + "_" + key
|
||||
}
|
||||
|
||||
func normalizedKey(value string) string {
|
||||
lower := strings.ToLower(strings.TrimSpace(value))
|
||||
var out strings.Builder
|
||||
lastUnderscore := false
|
||||
for _, r := range lower {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||
out.WriteRune(r)
|
||||
lastUnderscore = false
|
||||
continue
|
||||
}
|
||||
if !lastUnderscore {
|
||||
out.WriteByte('_')
|
||||
lastUnderscore = true
|
||||
}
|
||||
}
|
||||
return strings.Trim(out.String(), "_")
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package forecast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
|
||||
@@ -14,6 +16,25 @@ type DaypartDefinition struct {
|
||||
End string `json:"end"`
|
||||
}
|
||||
|
||||
// CanonicalDaypartKey returns the stable identity for a configured daypart name.
|
||||
func CanonicalDaypartKey(value string) string {
|
||||
lower := strings.ToLower(strings.TrimSpace(value))
|
||||
var out strings.Builder
|
||||
lastUnderscore := false
|
||||
for _, r := range lower {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||
out.WriteRune(r)
|
||||
lastUnderscore = false
|
||||
continue
|
||||
}
|
||||
if !lastUnderscore {
|
||||
out.WriteByte('_')
|
||||
lastUnderscore = true
|
||||
}
|
||||
}
|
||||
return strings.Trim(out.String(), "_")
|
||||
}
|
||||
|
||||
type DaypartWindow struct {
|
||||
Name string `json:"name"`
|
||||
Start time.Time `json:"start"`
|
||||
|
||||
@@ -658,3 +658,26 @@ func assertTimedValueClose(t *testing.T, name string, got *TimedValue, want floa
|
||||
t.Fatalf("%s = %#v, want %v", name, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalDaypartKey(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
value string
|
||||
want string
|
||||
}{
|
||||
{name: "blank", value: " \t ", want: ""},
|
||||
{name: "casing", value: "MORNING", want: "morning"},
|
||||
{name: "punctuation runs", value: " Morning--Commute / School ", want: "morning_commute_school"},
|
||||
{name: "Unicode", value: "Déjà Vu", want: "déjà_vu"},
|
||||
{name: "letters and digits", value: "Day 1 Outlook", want: "day_1_outlook"},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := CanonicalDaypartKey(tt.value); got != tt.want {
|
||||
t.Fatalf("CanonicalDaypartKey(%q) = %q, want %q", tt.value, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
if CanonicalDaypartKey("Morning commute") != CanonicalDaypartKey("MORNING--COMMUTE") {
|
||||
t.Fatal("equivalent configured names must have the same canonical daypart key")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ package generatedtext
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/facts"
|
||||
@@ -698,7 +696,7 @@ func orderedDaypartRows(dayparts *map[string]briefing.DerivedDaypartSummaryModul
|
||||
}
|
||||
|
||||
func daypartModuleKeyCandidates(daypart forecast.DaypartSummary) []string {
|
||||
key := normalizedDaypartKey(daypart.Name)
|
||||
key := forecast.CanonicalDaypartKey(daypart.Name)
|
||||
if key == "" {
|
||||
key = "unnamed"
|
||||
}
|
||||
@@ -708,24 +706,6 @@ func daypartModuleKeyCandidates(daypart forecast.DaypartSummary) []string {
|
||||
return []string{key, daypart.Period.Start.Format(timeutil.DateLayout) + "_" + key}
|
||||
}
|
||||
|
||||
func normalizedDaypartKey(value string) string {
|
||||
lower := strings.ToLower(strings.TrimSpace(value))
|
||||
var out strings.Builder
|
||||
lastUnderscore := false
|
||||
for _, r := range lower {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||
out.WriteRune(r)
|
||||
lastUnderscore = false
|
||||
continue
|
||||
}
|
||||
if !lastUnderscore {
|
||||
out.WriteByte('_')
|
||||
lastUnderscore = true
|
||||
}
|
||||
}
|
||||
return strings.Trim(out.String(), "_")
|
||||
}
|
||||
|
||||
func locationName(metadata briefing.Metadata) string {
|
||||
if metadata.Location != nil {
|
||||
if metadata.Location.Name != "" && metadata.Location.Region != "" {
|
||||
|
||||
Reference in New Issue
Block a user