Skip to content

Commit

Permalink
feat: Add caching mechanism to optimize weather data fetching from Op…
Browse files Browse the repository at this point in the history
…enMeteo API
  • Loading branch information
VinVorteX committed Oct 5, 2024
1 parent 06c3942 commit e0113bf
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 11 deletions.
34 changes: 31 additions & 3 deletions backends/open-meteo.com.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@ import (
"net/http"
"regexp"
"strings"
"sync"
"time"

"github.com/schachmat/wego/iface"
)

type cachedData struct {
Data iface.Data
Timestamp time.Time
}

type openmeteoConfig struct {
apiKey string
language string
debug bool
apiKey string
language string
debug bool
cache map[string]cachedData // adding a cache map
cacheDuration time.Duration // adding the duration for which the data will be valid
}

type curCond struct {
Expand Down Expand Up @@ -110,6 +118,8 @@ var (
func (opmeteo *openmeteoConfig) Setup() {
flag.StringVar(&opmeteo.apiKey, "openmeteo-api-key", "", "openmeteo backend: the api `KEY` to use if commercial usage")
flag.BoolVar(&opmeteo.debug, "openmeteo-debug", false, "openmeteo backend: print raw requests and responses")
opmeteo.cache = make(map[string]cachedData) // initializing a cache
opmeteo.cacheDuration = 1 * time.Hour // setting the cache duation to 1 hour
}

func (opmeteo *openmeteoConfig) parseDaily(dailyInfo Hourly) []iface.Day {
Expand Down Expand Up @@ -161,11 +171,22 @@ func parseCurCond(current curCond) (ret iface.Cond) {

}

var mu sync.Mutex

func (opmeteo *openmeteoConfig) Fetch(location string, numdays int) iface.Data {
var ret iface.Data
var params []string
var loc string

//checking cache before fething the data
mu.Lock()
if cached, ok := opmeteo.cache[location]; ok {
if time.Since(cached.Timestamp) < opmeteo.cacheDuration {
return cached.Data
}
}
mu.Unlock()

if numdays <= 0 {
log.Fatal("Number of days less than 1 ")
}
Expand Down Expand Up @@ -219,6 +240,13 @@ func (opmeteo *openmeteoConfig) Fetch(location string, numdays int) iface.Data {
ret.Forecast = forecast
}

mu.Lock()
opmeteo.cache[location] = cachedData{
Data: ret, //store the fetched data in the cache
Timestamp: time.Now(), // store the current time in the cache
}
mu.Unlock()

return ret
}

Expand Down
41 changes: 33 additions & 8 deletions backends/openweathermap.org.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,39 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/schachmat/wego/iface"
"io"
"log"
"net/http"
"regexp"
"strings"
"sync"
"time"

"github.com/schachmat/wego/iface"
)

type openWeatherConfig struct {
apiKey string
lang string
debug bool
apiKey string
lang string
debug bool
cache map[string]cacheEntry
cacheMux sync.Mutex
}

type cacheEntry struct {
data iface.Data
Timestamp time.Time
}

type openWeatherResponse struct {
Cod string `json:"cod"`
City struct {
Name string `json:"name"`
Country string `json:"country"`
TimeZone int64 `json: "timezone"`
Name string `json:"name"`
Country string `json:"country"`
TimeZone int64 `json: "timezone"`
// sunrise/sunset are once per call
SunRise int64 `json: "sunrise"`
SunSet int64 `json: "sunset"`
SunSet int64 `json: "sunset"`
} `json:"city"`
List []dataBlock `json:"list"`
}
Expand Down Expand Up @@ -57,12 +66,15 @@ type dataBlock struct {

const (
openweatherURI = "http://api.openweathermap.org/data/2.5/forecast?%s&appid=%s&units=metric&lang=%s"
cacheDuration = 1 * time.Hour
)

func (c *openWeatherConfig) Setup() {
flag.StringVar(&c.apiKey, "owm-api-key", "", "openweathermap backend: the api `KEY` to use")
flag.StringVar(&c.lang, "owm-lang", "en", "openweathermap backend: the `LANGUAGE` to request from openweathermap")
flag.BoolVar(&c.debug, "owm-debug", false, "openweathermap backend: print raw requests and responses")

c.cache = make(map[string]cacheEntry) // initializing the cache
}

func (c *openWeatherConfig) fetch(url string) (*openWeatherResponse, error) {
Expand Down Expand Up @@ -236,6 +248,14 @@ func (c *openWeatherConfig) Fetch(location string, numdays int) iface.Data {
if len(c.apiKey) == 0 {
log.Fatal("No openweathermap.org API key specified.\nYou have to register for one at https://home.openweathermap.org/users/sign_up")
}

c.cacheMux.Lock()
if entry, found := c.cache[location]; found && time.Since(entry.Timestamp)<cacheDuration {
c.cacheMux.Unlock()
return entry.data
}
c.cacheMux.Unlock()

if matched, err := regexp.MatchString(`^-?[0-9]*(\.[0-9]+)?,-?[0-9]*(\.[0-9]+)?$`, location); matched && err == nil {
s := strings.Split(location, ",")
loc = fmt.Sprintf("lat=%s&lon=%s", s[0], s[1])
Expand All @@ -257,6 +277,11 @@ func (c *openWeatherConfig) Fetch(location string, numdays int) iface.Data {
}

if numdays == 0 {
c.cacheMux.Lock()
c.cache[location] = cacheEntry{
data: ret,
Timestamp: time.Now(),
}
return ret
}
ret.Forecast = c.parseDaily(resp.List, numdays)
Expand Down

0 comments on commit e0113bf

Please sign in to comment.