Added "alert" field to weather route and fixed some bugs

This commit is contained in:
2025-08-29 23:19:19 +02:00
parent a4ddc43579
commit 9fec72251d
5 changed files with 154 additions and 22 deletions

View File

@@ -36,13 +36,33 @@ which yield the following:
```json ```json
{ {
"date": "Thursday, 2025/07/31", "date": "Friday, 2025/08/29",
"temperature": "29°C", "temperature": "18°C",
"min": "19°C", "min": "18°C",
"max": "29°C", "max": "24°C",
"condition": "Clear", "condition": "Clouds",
"feelsLike": "29°C", "feelsLike": "18°C",
"emoji": "" "emoji": "",
"alerts": [
{
"event": "Yellow Thunderstorm Warning",
"startDate": "Friday, 2025/08/29 2:00 AM",
"endDate": "Friday, 2025/08/29 11:59 PM",
"description": "Moderate intensity weather phenomena expected EASTERN ALPINE AND PRE-ALPINE SECTOR"
},
{
"event": "Yellow Thunderstorm Warning",
"startDate": "Saturday, 2025/08/30 12:00 AM",
"endDate": "Saturday, 2025/08/30 5:59 AM",
"description": "Moderate intensity weather phenomena expected"
},
{
"event": "Orange Thunderstorm Warning",
"startDate": "Friday, 2025/08/29 9:00 AM",
"endDate": "Friday, 2025/08/29 11:59 PM",
"description": "Severe weather expected"
}
]
} }
``` ```
@@ -57,13 +77,33 @@ which yields:
```json ```json
{ {
"date": "Thursday, 2025/07/31", "date": "Friday, 2025/08/29",
"temperature": "61°F", "temperature": "50°F",
"min": "51°F", "min": "50°F",
"max": "61°F", "max": "56°F",
"condition": "Clear", "condition": "Clouds",
"feelsLike": "61°F", "feelsLike": "50°F",
"emoji": "" "emoji": "",
"alerts": [
{
"event": "Yellow Thunderstorm Warning",
"startDate": "Friday, 2025/08/29 2:00 AM",
"endDate": "Friday, 2025/08/29 11:59 PM",
"description": "Moderate intensity weather phenomena expected EASTERN ALPINE AND PRE-ALPINE SECTOR"
},
{
"event": "Yellow Thunderstorm Warning",
"startDate": "Saturday, 2025/08/30 12:00 AM",
"endDate": "Saturday, 2025/08/30 5:59 AM",
"description": "Moderate intensity weather phenomena expected"
},
{
"event": "Orange Thunderstorm Warning",
"startDate": "Friday, 2025/08/29 9:00 AM",
"endDate": "Friday, 2025/08/29 11:59 PM",
"description": "Severe weather expected"
}
]
} }
``` ```

View File

@@ -112,6 +112,11 @@ func GetWeather(res http.ResponseWriter, req *http.Request, cache *types.Cache[t
path := strings.TrimPrefix(req.URL.Path, "/weather/") path := strings.TrimPrefix(req.URL.Path, "/weather/")
cityName := strings.Trim(path, "/") // Remove trailing slash if present cityName := strings.Trim(path, "/") // Remove trailing slash if present
if cityName == "" {
jsonError(res, "error", "specify city name", http.StatusMethodNotAllowed)
return
}
// Check whether the 'i' parameter(imperial mode) is specified // Check whether the 'i' parameter(imperial mode) is specified
isImperial := req.URL.Query().Has("i") isImperial := req.URL.Query().Has("i")
@@ -165,6 +170,11 @@ func GetMetrics(res http.ResponseWriter, req *http.Request, cache *types.Cache[t
path := strings.TrimPrefix(req.URL.Path, "/metrics/") path := strings.TrimPrefix(req.URL.Path, "/metrics/")
cityName := strings.Trim(path, "/") // Remove trailing slash if present cityName := strings.Trim(path, "/") // Remove trailing slash if present
if cityName == "" {
jsonError(res, "error", "specify city name", http.StatusMethodNotAllowed)
return
}
// Check whether the 'i' parameter(imperial mode) is specified // Check whether the 'i' parameter(imperial mode) is specified
isImperial := req.URL.Query().Has("i") isImperial := req.URL.Query().Has("i")
@@ -215,6 +225,11 @@ func GetWind(res http.ResponseWriter, req *http.Request, cache *types.Cache[type
path := strings.TrimPrefix(req.URL.Path, "/wind/") path := strings.TrimPrefix(req.URL.Path, "/wind/")
cityName := strings.Trim(path, "/") // Remove trailing slash if present cityName := strings.Trim(path, "/") // Remove trailing slash if present
if cityName == "" {
jsonError(res, "error", "specify city name", http.StatusMethodNotAllowed)
return
}
// Check whether the 'i' parameter(imperial mode) is specified // Check whether the 'i' parameter(imperial mode) is specified
isImperial := req.URL.Query().Has("i") isImperial := req.URL.Query().Has("i")
@@ -265,6 +280,11 @@ func GetForecast(
path := strings.TrimPrefix(req.URL.Path, "/forecast/") path := strings.TrimPrefix(req.URL.Path, "/forecast/")
cityName := strings.Trim(path, "/") // Remove trailing slash if present cityName := strings.Trim(path, "/") // Remove trailing slash if present
if cityName == "" {
jsonError(res, "error", "specify city name", http.StatusMethodNotAllowed)
return
}
// Check whether the 'i' parameter(imperial mode) is specified // Check whether the 'i' parameter(imperial mode) is specified
isImperial := req.URL.Query().Has("i") isImperial := req.URL.Query().Has("i")
@@ -360,6 +380,11 @@ func GetStatistics(res http.ResponseWriter, req *http.Request, statDB *types.Sta
path := strings.TrimPrefix(req.URL.Path, "/stats/") path := strings.TrimPrefix(req.URL.Path, "/stats/")
cityName := strings.Trim(path, "/") // Remove trailing slash if present cityName := strings.Trim(path, "/") // Remove trailing slash if present
if cityName == "" {
jsonError(res, "error", "specify city name", http.StatusMethodNotAllowed)
return
}
// Check whether the 'i' parameter(imperial mode) is specified // Check whether the 'i' parameter(imperial mode) is specified
isImperial := req.URL.Query().Has("i") isImperial := req.URL.Query().Has("i")

View File

@@ -53,7 +53,7 @@ func GetWeather(city *types.City, apiKey string) (types.Weather, error) {
params.Set("lon", strconv.FormatFloat(city.Lon, 'f', -1, 64)) params.Set("lon", strconv.FormatFloat(city.Lon, 'f', -1, 64))
params.Set("appid", apiKey) params.Set("appid", apiKey)
params.Set("units", "metric") params.Set("units", "metric")
params.Set("exclude", "minutely,hourly,alerts") params.Set("exclude", "minutely,hourly")
url.RawQuery = params.Encode() url.RawQuery = params.Encode()
@@ -81,6 +81,12 @@ func GetWeather(city *types.City, apiKey string) (types.Weather, error) {
Max float64 `json:"max"` Max float64 `json:"max"`
} `json:"temp"` } `json:"temp"`
} `json:"daily"` } `json:"daily"`
Alerts []struct {
Event string `json:"event"`
Start int64 `json:"start"`
End int64 `json:"end"`
Description string `json:"description"`
} `json:"alerts"`
} }
var weather WeatherRes var weather WeatherRes
@@ -107,6 +113,27 @@ func GetWeather(city *types.City, apiKey string) (types.Weather, error) {
isNight := strings.HasSuffix(weather.Current.Weather[0].Icon, "n") isNight := strings.HasSuffix(weather.Current.Weather[0].Icon, "n")
emoji := GetEmoji(condition, isNight) emoji := GetEmoji(condition, isNight)
// Format weather alerts
var alerts []types.WeatherAlert
for _, alert := range weather.Alerts {
// Format both start and end timestamp as 'YYYY-MM-DD'
utcStartDate := time.Unix(int64(alert.Start), 0)
startDate := types.ZephyrAlertDate{Date: utcStartDate}
utcEndDate := time.Unix(int64(alert.End), 0)
endDate := types.ZephyrAlertDate{Date: utcEndDate}
// Extract the first line of alert description
eventDescription := strings.Split(alert.Description, "\n")[0]
alerts = append(alerts, types.WeatherAlert{
Event: alert.Event,
Start: startDate,
End: endDate,
Description: eventDescription,
})
}
return types.Weather{ return types.Weather{
Date: weatherDate, Date: weatherDate,
Temperature: strconv.FormatFloat(weather.Current.Temperature, 'f', -1, 64), Temperature: strconv.FormatFloat(weather.Current.Temperature, 'f', -1, 64),
@@ -115,5 +142,6 @@ func GetWeather(city *types.City, apiKey string) (types.Weather, error) {
FeelsLike: strconv.FormatFloat(weather.Current.FeelsLike, 'f', -1, 64), FeelsLike: strconv.FormatFloat(weather.Current.FeelsLike, 'f', -1, 64),
Condition: weather.Current.Weather[0].Title, Condition: weather.Current.Weather[0].Title,
Emoji: emoji, Emoji: emoji,
Alerts: alerts,
}, nil }, nil
} }

View File

@@ -62,3 +62,32 @@ func (t ZephyrTime) MarshalJSON() ([]byte, error) {
return []byte("\"" + fmtTime + "\""), nil return []byte("\"" + fmtTime + "\""), nil
} }
type ZephyrAlertDate struct {
Date time.Time
}
func (t *ZephyrAlertDate) UnmarshalJSON(b []byte) error {
s := strings.Trim(string(b), "\"")
if s == "" {
return nil
}
var err error
t.Date, err = time.Parse("Monday, 2006/01/02 15:04", s)
if err != nil {
return err
}
return nil
}
func (t ZephyrAlertDate) MarshalJSON() ([]byte, error) {
if t.Date.IsZero() {
return []byte("\"\""), nil
}
fmtTime := t.Date.Format("Monday, 2006/01/02 3:04 PM")
return []byte("\"" + fmtTime + "\""), nil
}

View File

@@ -1,5 +1,14 @@
package types package types
// The WeatherAlert data type, representing a
// weather alert
type WeatherAlert struct {
Event string `json:"event"`
Start ZephyrAlertDate `json:"startDate"`
End ZephyrAlertDate `json:"endDate"`
Description string `json:"description"`
}
// The Weather data type, representing the weather of a certain location // The Weather data type, representing the weather of a certain location
type Weather struct { type Weather struct {
Date ZephyrDate `json:"date"` Date ZephyrDate `json:"date"`
@@ -9,4 +18,5 @@ type Weather struct {
Condition string `json:"condition"` Condition string `json:"condition"`
FeelsLike string `json:"feelsLike"` FeelsLike string `json:"feelsLike"`
Emoji string `json:"emoji"` Emoji string `json:"emoji"`
Alerts []WeatherAlert `json:"alerts"`
} }