Made forecast-period conditionCode optional

This commit is contained in:
2026-05-28 07:46:57 -05:00
parent 6ea27cb2c5
commit 626df6bfc1
9 changed files with 111 additions and 60 deletions

View File

@@ -334,7 +334,7 @@ func TestForecastUSUnitsWithXMLFormatUppercaseQuery(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: time.Now().UTC(),
EndTime: time.Now().UTC().Add(time.Hour),
ConditionCode: 1,
ConditionCode: wmoCodePtr(1),
TemperatureC: float64Ptr(10),
}},
},
@@ -367,7 +367,7 @@ func TestForecastPrecisionTwo(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: time.Now().UTC(),
EndTime: time.Now().UTC().Add(time.Hour),
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
TemperatureC: float64Ptr(12.345),
}},
},
@@ -412,7 +412,7 @@ func TestForecastJSONOmitsLegacyDescriptionFields(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: time.Now().UTC(),
EndTime: time.Now().UTC().Add(time.Hour),
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
TextDescription: "Cloudy",
}},
},
@@ -463,7 +463,7 @@ func TestForecastTimezoneOffsetUppercaseTZConvertsAllTimes(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: periodStart,
EndTime: periodEnd,
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
}},
},
}, "/forecast/hourly")
@@ -498,7 +498,7 @@ func TestForecastTimezoneAbbreviationCDT(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: issuedAt,
EndTime: issuedAt.Add(time.Hour),
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
}},
},
}, "/forecast/hourly")
@@ -525,7 +525,7 @@ func TestForecastTimezoneCityAliasChicago(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: issuedAt,
EndTime: issuedAt.Add(time.Hour),
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
}},
},
}, "/forecast/hourly")
@@ -587,9 +587,9 @@ func TestForecastHourlyTodayFiltersByStartDateUTCDefault(t *testing.T) {
Product: model.ForecastProductHourly,
IssuedAt: time.Date(2026, 7, 10, 11, 0, 0, 0, time.UTC),
Periods: []model.WeatherForecastPeriod{
{StartTime: time.Date(2026, 7, 10, 0, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 10, 1, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 10, 23, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 2, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 3, 0, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 10, 0, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 10, 1, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 10, 23, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 2, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 3, 0, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
},
},
}, "/forecast/hourly/today")
@@ -622,9 +622,9 @@ func TestForecastHourlyTomorrowFiltersByStartDateUTCDefault(t *testing.T) {
Product: model.ForecastProductHourly,
IssuedAt: time.Date(2026, 7, 10, 11, 0, 0, 0, time.UTC),
Periods: []model.WeatherForecastPeriod{
{StartTime: time.Date(2026, 7, 10, 23, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 1, 0, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 15, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 16, 0, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 10, 23, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 1, 0, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 15, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 16, 0, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
},
},
}, "/forecast/hourly/tomorrow")
@@ -657,9 +657,9 @@ func TestForecastHourlyTodayTimezoneAffectsDaySlice(t *testing.T) {
Product: model.ForecastProductHourly,
IssuedAt: time.Date(2026, 7, 10, 11, 0, 0, 0, time.UTC),
Periods: []model.WeatherForecastPeriod{
{StartTime: time.Date(2026, 7, 10, 4, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 10, 5, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 3, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 4, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 5, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 6, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 10, 4, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 10, 5, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 3, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 4, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 5, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 6, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
},
},
}, "/forecast/hourly/today")
@@ -690,8 +690,8 @@ func TestForecastHourlyTomorrowTimezoneAffectsDaySlice(t *testing.T) {
Product: model.ForecastProductHourly,
IssuedAt: time.Date(2026, 7, 10, 11, 0, 0, 0, time.UTC),
Periods: []model.WeatherForecastPeriod{
{StartTime: time.Date(2026, 7, 11, 3, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 4, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 5, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 6, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 3, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 4, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 5, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 6, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
},
},
}, "/forecast/hourly/tomorrow")
@@ -724,7 +724,7 @@ func TestForecastHourlyTodaySupportsSameFlags(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: time.Date(2026, 7, 10, 13, 0, 0, 0, time.UTC),
EndTime: time.Date(2026, 7, 10, 14, 0, 0, 0, time.UTC),
ConditionCode: 1,
ConditionCode: wmoCodePtr(1),
TemperatureC: float64Ptr(10.123),
}},
},
@@ -755,7 +755,7 @@ func TestForecastHourlyTomorrowSupportsSameFlags(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: time.Date(2026, 7, 11, 13, 0, 0, 0, time.UTC),
EndTime: time.Date(2026, 7, 11, 14, 0, 0, 0, time.UTC),
ConditionCode: 1,
ConditionCode: wmoCodePtr(1),
TemperatureC: float64Ptr(10.123),
}},
},
@@ -835,7 +835,7 @@ func TestForecastHourlyTodayNoMatchingPeriodsReturnsDataWithEmptyPeriods(t *test
Periods: []model.WeatherForecastPeriod{{
StartTime: time.Date(2026, 7, 11, 13, 0, 0, 0, time.UTC),
EndTime: time.Date(2026, 7, 11, 14, 0, 0, 0, time.UTC),
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
}},
},
}, "/forecast/hourly/today")
@@ -888,7 +888,7 @@ func TestForecastNarrativePopulatedJSONEnvelope(t *testing.T) {
StartTime: time.Now().UTC(),
EndTime: time.Now().UTC().Add(12 * time.Hour),
Name: "Tonight",
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
TextDescription: "Mostly clear overnight.",
}},
},
@@ -952,7 +952,7 @@ func TestForecastNarrativeSupportsSameFlags(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: time.Date(2026, 7, 10, 13, 0, 0, 0, time.UTC),
EndTime: time.Date(2026, 7, 11, 1, 0, 0, 0, time.UTC),
ConditionCode: 1,
ConditionCode: wmoCodePtr(1),
TemperatureC: float64Ptr(20.123),
}},
},
@@ -1012,9 +1012,9 @@ func TestForecastNarrativeTodayTimezoneAffectsDaySlice(t *testing.T) {
Product: model.ForecastProductNarrative,
IssuedAt: time.Date(2026, 7, 10, 11, 0, 0, 0, time.UTC),
Periods: []model.WeatherForecastPeriod{
{StartTime: time.Date(2026, 7, 10, 4, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 10, 5, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 3, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 15, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 5, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 17, 30, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 10, 4, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 10, 5, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 3, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 15, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 5, 30, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 17, 30, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
},
},
}, "/forecast/narrative/today")
@@ -1045,9 +1045,9 @@ func TestForecastNarrativeTomorrowFiltersByStartDateUTCDefault(t *testing.T) {
Product: model.ForecastProductNarrative,
IssuedAt: time.Date(2026, 7, 10, 11, 0, 0, 0, time.UTC),
Periods: []model.WeatherForecastPeriod{
{StartTime: time.Date(2026, 7, 10, 23, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 11, 0, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 12, 0, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 11, 12, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC), ConditionCode: model.WMOUnknown},
{StartTime: time.Date(2026, 7, 10, 23, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 11, 0, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 11, 12, 0, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
{StartTime: time.Date(2026, 7, 11, 12, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC), ConditionCode: wmoCodePtr(model.WMOUnknown)},
},
},
}, "/forecast/narrative/tomorrow")
@@ -1806,6 +1806,11 @@ func float64Ptr(v float64) *float64 {
return &v
}
func wmoCodePtr(v model.WMOCode) *model.WMOCode {
out := v
return &out
}
type forecastTimePayload struct {
Data struct {
IssuedAt time.Time `json:"issuedAt"`

View File

@@ -23,28 +23,28 @@ type WeatherForecastRunUS struct {
// WeatherForecastPeriodUS is the US-customary response shape for forecast periods.
type WeatherForecastPeriodUS struct {
StartTime time.Time `json:"startTime" xml:"startTime"`
EndTime time.Time `json:"endTime" xml:"endTime"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
IsDay *bool `json:"isDay,omitempty" xml:"isDay,omitempty"`
ConditionCode model.WMOCode `json:"conditionCode" xml:"conditionCode"`
TextDescription string `json:"textDescription,omitempty" xml:"textDescription,omitempty"`
TemperatureF *float64 `json:"temperatureF,omitempty" xml:"temperatureF,omitempty"`
TemperatureFMin *float64 `json:"temperatureFMin,omitempty" xml:"temperatureFMin,omitempty"`
TemperatureFMax *float64 `json:"temperatureFMax,omitempty" xml:"temperatureFMax,omitempty"`
DewpointF *float64 `json:"dewpointF,omitempty" xml:"dewpointF,omitempty"`
RelativeHumidityPercent *float64 `json:"relativeHumidityPercent,omitempty" xml:"relativeHumidityPercent,omitempty"`
WindDirectionDegrees *float64 `json:"windDirectionDegrees,omitempty" xml:"windDirectionDegrees,omitempty"`
WindSpeedMph *float64 `json:"windSpeedMph,omitempty" xml:"windSpeedMph,omitempty"`
WindGustMph *float64 `json:"windGustMph,omitempty" xml:"windGustMph,omitempty"`
BarometricPressureInHg *float64 `json:"barometricPressureInHg,omitempty" xml:"barometricPressureInHg,omitempty"`
VisibilityMiles *float64 `json:"visibilityMiles,omitempty" xml:"visibilityMiles,omitempty"`
ApparentTemperatureF *float64 `json:"apparentTemperatureF,omitempty" xml:"apparentTemperatureF,omitempty"`
CloudCoverPercent *float64 `json:"cloudCoverPercent,omitempty" xml:"cloudCoverPercent,omitempty"`
ProbabilityOfPrecipitationPercent *float64 `json:"probabilityOfPrecipitationPercent,omitempty" xml:"probabilityOfPrecipitationPercent,omitempty"`
PrecipitationAmountIn *float64 `json:"precipitationAmountIn,omitempty" xml:"precipitationAmountIn,omitempty"`
SnowfallDepthIn *float64 `json:"snowfallDepthIn,omitempty" xml:"snowfallDepthIn,omitempty"`
UVIndex *float64 `json:"uvIndex,omitempty" xml:"uvIndex,omitempty"`
StartTime time.Time `json:"startTime" xml:"startTime"`
EndTime time.Time `json:"endTime" xml:"endTime"`
Name string `json:"name,omitempty" xml:"name,omitempty"`
IsDay *bool `json:"isDay,omitempty" xml:"isDay,omitempty"`
ConditionCode *model.WMOCode `json:"conditionCode,omitempty" xml:"conditionCode,omitempty"`
TextDescription string `json:"textDescription,omitempty" xml:"textDescription,omitempty"`
TemperatureF *float64 `json:"temperatureF,omitempty" xml:"temperatureF,omitempty"`
TemperatureFMin *float64 `json:"temperatureFMin,omitempty" xml:"temperatureFMin,omitempty"`
TemperatureFMax *float64 `json:"temperatureFMax,omitempty" xml:"temperatureFMax,omitempty"`
DewpointF *float64 `json:"dewpointF,omitempty" xml:"dewpointF,omitempty"`
RelativeHumidityPercent *float64 `json:"relativeHumidityPercent,omitempty" xml:"relativeHumidityPercent,omitempty"`
WindDirectionDegrees *float64 `json:"windDirectionDegrees,omitempty" xml:"windDirectionDegrees,omitempty"`
WindSpeedMph *float64 `json:"windSpeedMph,omitempty" xml:"windSpeedMph,omitempty"`
WindGustMph *float64 `json:"windGustMph,omitempty" xml:"windGustMph,omitempty"`
BarometricPressureInHg *float64 `json:"barometricPressureInHg,omitempty" xml:"barometricPressureInHg,omitempty"`
VisibilityMiles *float64 `json:"visibilityMiles,omitempty" xml:"visibilityMiles,omitempty"`
ApparentTemperatureF *float64 `json:"apparentTemperatureF,omitempty" xml:"apparentTemperatureF,omitempty"`
CloudCoverPercent *float64 `json:"cloudCoverPercent,omitempty" xml:"cloudCoverPercent,omitempty"`
ProbabilityOfPrecipitationPercent *float64 `json:"probabilityOfPrecipitationPercent,omitempty" xml:"probabilityOfPrecipitationPercent,omitempty"`
PrecipitationAmountIn *float64 `json:"precipitationAmountIn,omitempty" xml:"precipitationAmountIn,omitempty"`
SnowfallDepthIn *float64 `json:"snowfallDepthIn,omitempty" xml:"snowfallDepthIn,omitempty"`
UVIndex *float64 `json:"uvIndex,omitempty" xml:"uvIndex,omitempty"`
}
func ForecastPayload(run *model.WeatherForecastRun, units Units, precision int, tz *time.Location) any {
@@ -69,7 +69,7 @@ func ForecastPayload(run *model.WeatherForecastRun, units Units, precision int,
EndTime: inLocationTime(p.EndTime, tz),
Name: p.Name,
IsDay: copyBoolPtr(p.IsDay),
ConditionCode: p.ConditionCode,
ConditionCode: copyWMOCodePtr(p.ConditionCode),
TextDescription: p.TextDescription,
TemperatureF: roundedPtr(celsiusToFahrenheitPtr(p.TemperatureC), precision),
TemperatureFMin: roundedPtr(celsiusToFahrenheitPtr(p.TemperatureCMin), precision),
@@ -110,7 +110,7 @@ func ForecastPayload(run *model.WeatherForecastRun, units Units, precision int,
EndTime: inLocationTime(p.EndTime, tz),
Name: p.Name,
IsDay: copyBoolPtr(p.IsDay),
ConditionCode: p.ConditionCode,
ConditionCode: copyWMOCodePtr(p.ConditionCode),
TextDescription: p.TextDescription,
TemperatureC: roundedPtr(copyFloat64Ptr(p.TemperatureC), precision),
TemperatureCMin: roundedPtr(copyFloat64Ptr(p.TemperatureCMin), precision),

View File

@@ -5,6 +5,8 @@ package presenter
import (
"math"
"time"
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
)
func celsiusToFahrenheitPtr(v *float64) *float64 {
@@ -47,6 +49,14 @@ func copyTimePtr(v *time.Time) *time.Time {
return &out
}
func copyWMOCodePtr(v *model.WMOCode) *model.WMOCode {
if v == nil {
return nil
}
out := *v
return &out
}
func inLocationTime(v time.Time, loc *time.Location) time.Time {
if loc == nil {
return v

View File

@@ -55,7 +55,7 @@ func TestForecastPayloadUS(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: issuedAt,
EndTime: issuedAt.Add(1 * time.Hour),
ConditionCode: 63,
ConditionCode: wmoCodePtr(63),
TemperatureC: float64Ptr(0),
TemperatureCMin: float64Ptr(-5),
TemperatureCMax: float64Ptr(5),
@@ -91,7 +91,7 @@ func TestForecastPayloadOmitsLegacyDescriptionFields(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: time.Date(2026, 3, 20, 12, 0, 0, 0, time.UTC),
EndTime: time.Date(2026, 3, 20, 13, 0, 0, 0, time.UTC),
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
TextDescription: "Cloudy",
}},
}
@@ -111,7 +111,7 @@ func TestForecastPayloadTimezoneConversionMetricAndUS(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: issuedAt.Add(1 * time.Hour),
EndTime: issuedAt.Add(2 * time.Hour),
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
}},
}
@@ -153,7 +153,7 @@ func TestForecastPayloadNoTimezonePreservesUTCAndCopySemantics(t *testing.T) {
Periods: []model.WeatherForecastPeriod{{
StartTime: issuedAt,
EndTime: issuedAt.Add(time.Hour),
ConditionCode: model.WMOUnknown,
ConditionCode: wmoCodePtr(model.WMOUnknown),
}},
}
@@ -438,6 +438,11 @@ func boolPtr(v bool) *bool {
return &v
}
func wmoCodePtr(v model.WMOCode) *model.WMOCode {
out := v
return &out
}
func assertApprox(t *testing.T, got *float64, want, eps float64) {
t.Helper()
if got == nil {

View File

@@ -2,7 +2,11 @@
// Layer: adapters/outbound/postgres forecast feature.
package postgres
import "gitea.maximumdirect.net/ejr/weatherfeeder/model"
import (
"database/sql"
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
)
func mapForecastParentRow(row forecastParentRow) model.WeatherForecastRun {
return model.WeatherForecastRun{
@@ -23,7 +27,7 @@ func mapForecastPeriodRow(row forecastPeriodRow) model.WeatherForecastPeriod {
EndTime: row.EndTime.UTC(),
Name: stringValue(row.Name),
IsDay: boolPtr(row.IsDay),
ConditionCode: model.WMOCode(row.ConditionCode),
ConditionCode: wmoCodePtr(row.ConditionCode),
TextDescription: stringValue(row.TextDescription),
TemperatureC: float64Ptr(row.TemperatureC),
TemperatureCMin: float64Ptr(row.TemperatureCMin),
@@ -43,3 +47,11 @@ func mapForecastPeriodRow(row forecastPeriodRow) model.WeatherForecastPeriod {
UVIndex: float64Ptr(row.UVIndex),
}
}
func wmoCodePtr(v sql.NullInt64) *model.WMOCode {
if !v.Valid {
return nil
}
out := model.WMOCode(v.Int64)
return &out
}

View File

@@ -25,7 +25,7 @@ type forecastPeriodRow struct {
EndTime time.Time
Name sql.NullString
IsDay sql.NullBool
ConditionCode int
ConditionCode sql.NullInt64
TextDescription sql.NullString
TemperatureC sql.NullFloat64
TemperatureCMin sql.NullFloat64

View File

@@ -66,7 +66,7 @@ func TestMapForecastPeriodRowNullables(t *testing.T) {
period := mapForecastPeriodRow(forecastPeriodRow{
StartTime: start,
EndTime: end,
ConditionCode: 80,
ConditionCode: sql.NullInt64{Int64: 80, Valid: true},
Name: sql.NullString{String: "Midnight", Valid: true},
TemperatureC: sql.NullFloat64{Float64: 12.5, Valid: true},
TemperatureCMin: sql.NullFloat64{Valid: false},
@@ -84,6 +84,21 @@ func TestMapForecastPeriodRowNullables(t *testing.T) {
if !period.StartTime.Equal(start) || !period.EndTime.Equal(end) {
t.Fatalf("unexpected time range: %s - %s", period.StartTime, period.EndTime)
}
if period.ConditionCode == nil || *period.ConditionCode != 80 {
t.Fatalf("expected condition code pointer 80, got %v", period.ConditionCode)
}
}
func TestMapForecastPeriodRowConditionCodeNullable(t *testing.T) {
period := mapForecastPeriodRow(forecastPeriodRow{
StartTime: time.Date(2026, 3, 20, 0, 0, 0, 0, time.UTC),
EndTime: time.Date(2026, 3, 20, 1, 0, 0, 0, time.UTC),
ConditionCode: sql.NullInt64{Valid: false},
})
if period.ConditionCode != nil {
t.Fatalf("expected nil condition code, got %v", period.ConditionCode)
}
}
func TestMapDiscussionParentRowNullables(t *testing.T) {

View File

@@ -7,7 +7,9 @@ Periods: {{len .Data.Periods}}
{{- range $i, $period := .Data.Periods}}
[{{$i}}] {{$period.StartTime}} -> {{$period.EndTime}}
{{- if $period.ConditionCode}}
Condition Code: {{$period.ConditionCode}}
{{- end}}
{{- if $period.TextDescription}}
Summary: {{$period.TextDescription}}
{{- end}}

View File

@@ -10,7 +10,9 @@ Periods: {{len .Data.Periods}}
{{- if $period.Name}}
Name: {{$period.Name}}
{{- end}}
{{- if $period.ConditionCode}}
Condition Code: {{$period.ConditionCode}}
{{- end}}
{{- if $period.TextDescription}}
Summary: {{$period.TextDescription}}
{{- end}}