add ristretto caching and process daily and rolling data
This commit is contained in:
@@ -2,33 +2,42 @@ package meteo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v5"
|
||||
"github.com/dgraph-io/ristretto/v2"
|
||||
)
|
||||
|
||||
type inMemory struct {
|
||||
data any
|
||||
mu *sync.RWMutex
|
||||
expiry time.Time
|
||||
}
|
||||
type Service struct {
|
||||
inMemory
|
||||
cache *ristretto.Cache[string, string]
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
cache, err := ristretto.NewCache(&ristretto.Config[string, string]{
|
||||
NumCounters: 1024,
|
||||
MaxCost: 1 << 30,
|
||||
BufferItems: 64,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Error("cannot init cache", "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Service{
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetWeatherByCity(ctx context.Context, params GetMeteoData) ([]MeteoData, error) {
|
||||
func (s *Service) GetWeatherByCity(ctx context.Context, params GetMeteoData) (MeteoData, error) {
|
||||
fromDate, err := time.Parse("2006-01-02", params.Date)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return MeteoData{}, err
|
||||
}
|
||||
toDate := fromDate.AddDate(0, 0, params.Days-1)
|
||||
|
||||
@@ -36,13 +45,15 @@ func (s *Service) GetWeatherByCity(ctx context.Context, params GetMeteoData) ([]
|
||||
url := fmt.Sprintf("http://localhost:8080/data?city=%s&from=%s&to=%s",
|
||||
params.Location, params.Date, toDate.Format("2006-01-02"))
|
||||
|
||||
slog.Info("url", "url", url)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusBadRequest {
|
||||
resp.Body.Close()
|
||||
return nil, backoff.Permanent(errors.New("bad request"))
|
||||
}
|
||||
|
||||
@@ -52,11 +63,92 @@ func (s *Service) GetWeatherByCity(ctx context.Context, params GetMeteoData) ([]
|
||||
result, err := backoff.Retry(ctx, operation, backoff.WithBackOff(backoff.NewExponentialBackOff()))
|
||||
if err != nil {
|
||||
slog.Error("somethin happened")
|
||||
return []MeteoData{}, err
|
||||
return MeteoData{}, err
|
||||
}
|
||||
// TODO add ristretto
|
||||
defer result.Body.Close()
|
||||
|
||||
slog.Info("fetched data", "data", result)
|
||||
body, err := io.ReadAll(result.Body)
|
||||
if err != nil {
|
||||
slog.Error("error reading response body", "err", err)
|
||||
return MeteoData{}, err
|
||||
}
|
||||
|
||||
return []MeteoData{}, nil
|
||||
var serviceAResponse struct {
|
||||
MeteoData []MeteoDataFromServiceA `json:"meteo_data"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &serviceAResponse); err != nil {
|
||||
slog.Error("error unmarshaling response", "err", err)
|
||||
return MeteoData{}, err
|
||||
}
|
||||
|
||||
if len(serviceAResponse.MeteoData) == 0 {
|
||||
return MeteoData{}, nil
|
||||
}
|
||||
|
||||
if params.Agg == AggDaily {
|
||||
return s.processDailyData(serviceAResponse.MeteoData, params)
|
||||
}
|
||||
|
||||
return s.processRolling7Data(serviceAResponse.MeteoData, params)
|
||||
}
|
||||
|
||||
func (s *Service) processDailyData(data []MeteoDataFromServiceA, params GetMeteoData) (MeteoData, error) {
|
||||
days := make([]MeteoDataPerDay, 0, len(data))
|
||||
|
||||
for _, d := range data {
|
||||
avgTemp := (d.MaxTemp + d.MinTemp) / 2
|
||||
day := MeteoDataPerDay{
|
||||
MaxTemp: d.MaxTemp,
|
||||
MinTemp: d.MinTemp,
|
||||
AvgTemp: avgTemp,
|
||||
Rainfall: d.Rainfall,
|
||||
Cloudiness: d.Cloudiness,
|
||||
}
|
||||
|
||||
if params.Unit == UnitF {
|
||||
day.ConvertValue()
|
||||
}
|
||||
|
||||
days = append(days, day)
|
||||
}
|
||||
|
||||
return MeteoData{
|
||||
Location: params.Location,
|
||||
Unit: params.Unit,
|
||||
From: params.Date,
|
||||
Days: &days,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) processRolling7Data(data []MeteoDataFromServiceA, params GetMeteoData) (MeteoData, error) {
|
||||
if len(data) < 7 {
|
||||
return MeteoData{}, errors.New("insufficient data for rolling 7-day calculation")
|
||||
}
|
||||
|
||||
var sumTemp, sumRainfall float32
|
||||
var sumCloudiness int
|
||||
|
||||
for i := len(data) - 7; i < len(data); i++ {
|
||||
avgTemp := (data[i].MaxTemp + data[i].MinTemp) / 2
|
||||
sumTemp += avgTemp
|
||||
sumRainfall += data[i].Rainfall
|
||||
sumCloudiness += data[i].Cloudiness
|
||||
}
|
||||
|
||||
rolling7 := &Rolling7Data{
|
||||
AvgTemp: sumTemp / 7,
|
||||
AvgCloudiness: sumCloudiness / 7,
|
||||
SumRainfall: sumRainfall,
|
||||
}
|
||||
|
||||
if params.Unit == UnitF {
|
||||
rolling7.AvgTemp = rolling7.AvgTemp*9/5 + 32
|
||||
}
|
||||
|
||||
return MeteoData{
|
||||
Location: params.Location,
|
||||
Unit: params.Unit,
|
||||
From: params.Date,
|
||||
Rolling7: rolling7,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user