diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3a3a78e --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +# weather Makefile + +GOCMD=go +GOBUILDCMD=${GOCMD} build +GOOPTIONS=-mod=vendor -ldflags="-s -w" + +RMCMD=rm +BINNAME=weather + +SRCFILES=cmd/weather/*.go + +all: build + +build: + ${GOBUILDCMD} ${GOOPTIONS} ${SRCFILES} + +clean: + ${RMCMD} -f ${BINNAME} diff --git a/README.md b/README.md index a0f5cbd..a62f936 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ weather is a small program that fetch weather informations, and store them to in ### Build ```bash -go build -mod=vendor +make ``` ### Sample config in weather.ini @@ -38,7 +38,7 @@ measurements=temp,humidity,pressure ## License ```text -Copyright (c) 2019, PaulBSD +Copyright (c) 2020, PaulBSD All rights reserved. Redistribution and use in source and binary forms, with or without @@ -64,4 +64,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the fuelprices project. -``` \ No newline at end of file +``` diff --git a/functions.go b/functions.go deleted file mode 100644 index 176ed42..0000000 --- a/functions.go +++ /dev/null @@ -1,135 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "io/ioutil" - "net/http" - "os" - "time" - - client "github.com/influxdata/influxdb1-client/v2" - "gopkg.in/ini.v1" -) - -// GetConfig fetch config from ini file -func GetConfig(weatherconfig *WeatherConfig, configfile string) error { - flag.Usage = Usage - - config, err := ini.Load(configfile) - if err != nil { - return err - } - - var wc WeatherConfig - - weatherSection := config.Section("weather") - - wc.WeatherAppID = weatherSection.Key("appid").MustString("appid") - wc.WeatherCities = weatherSection.Key("cities").Strings(",") - if len(wc.WeatherCities) < 1 { - return fmt.Errorf("No cities provided in config") - } - wc.WeatherMeasurements = weatherSection.Key("measurements").Strings(",") - if len(wc.WeatherMeasurements) < 1 { - return fmt.Errorf("No measurements provided in config") - } - - influxdbSection := config.Section("influxdb") - wc.InfluxURL = influxdbSection.Key("url").MustString("http://localhost:8086") - wc.InfluxUser = influxdbSection.Key("username").MustString("username") - wc.InfluxPass = influxdbSection.Key("password").MustString("password") - wc.InfluxDB = influxdbSection.Key("database").MustString("me") - - *weatherconfig = wc - - return err -} - -// FetchData fetch data from api -func FetchData(wc *WeatherConfig, city string) (Data, error) { - var d Data - - pollTo := 30 * time.Millisecond - - c := &http.Client{Timeout: pollTo * time.Second, Transport: &http.Transport{ - IdleConnTimeout: pollTo, - DisableCompression: false, - }} - - q := fmt.Sprintf("https://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric", city, wc.WeatherAppID) - - r, err := c.Get(q) - if err != nil { - return Data{}, err - } - - defer r.Body.Close() - - b, err := ioutil.ReadAll(r.Body) - if err != nil { - return Data{}, err - } - - err = json.Unmarshal(b, &d) - if err != nil { - return Data{}, err - } - - d.Points = map[string]interface{}{"temperature": d.Main.Temperature, - "humidity": d.Main.Humidity, - "pressure": d.Main.Pressure} - - return d, nil -} - -// SendToInflux sends time series data to influxdb -func SendToInflux(wc *WeatherConfig, d Data) error { - - httpClient, err := client.NewHTTPClient(client.HTTPConfig{ - Addr: wc.InfluxURL, - Username: wc.InfluxUser, - Password: wc.InfluxPass, - }) - if err != nil { - return err - } - defer httpClient.Close() - - bp, err := client.NewBatchPoints(client.BatchPointsConfig{ - Database: wc.InfluxDB, - }) - if err != nil { - return err - } - - for key, value := range d.Points { - tags := map[string]string{"city": d.City} - fields := map[string]interface{}{"value": value} - - point, err := client.NewPoint( - key, - tags, - fields, - time.Now(), - ) - if err != nil { - return err - } - - bp.AddPoint(point) - err = httpClient.Write(bp) - if err != nil { - return err - } - } - - return nil -} - -// Usage displays possible arguments -func Usage() { - flag.PrintDefaults() - os.Exit(1) -} diff --git a/go.mod b/go.mod index 8747b85..e5e4bca 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ -module weather +module git.paulbsd.com/paulbsd/weather -go 1.12 +go 1.13 require ( github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc diff --git a/src/config/main.go b/src/config/main.go new file mode 100644 index 0000000..05c3f68 --- /dev/null +++ b/src/config/main.go @@ -0,0 +1,51 @@ +package config + +import ( + "flag" + "fmt" + + "git.paulbsd.com/paulbsd/weather/utils" + "gopkg.in/ini.v1" +) + +// GetConfig fetch config from ini file +func GetConfig(config *Config, configfile string) error { + flag.Usage = utils.Usage + + cfg, err := ini.Load(configfile) + if err != nil { + return err + } + + weatherSection := cfg.Section("weather") + + config.WeatherAppID = weatherSection.Key("appid").MustString("appid") + config.WeatherCities = weatherSection.Key("cities").Strings(",") + if len(config.WeatherCities) < 1 { + return fmt.Errorf("No cities provided in config") + } + config.WeatherMeasurements = weatherSection.Key("measurements").Strings(",") + if len(config.WeatherMeasurements) < 1 { + return fmt.Errorf("No measurements provided in config") + } + + influxdbSection := cfg.Section("influxdb") + config.InfluxURL = influxdbSection.Key("url").MustString("http://localhost:8086") + config.InfluxUser = influxdbSection.Key("username").MustString("username") + config.InfluxPass = influxdbSection.Key("password").MustString("password") + config.InfluxDB = influxdbSection.Key("database").MustString("me") + + return err +} + +// Config is the main configuration +type Config struct { + WeatherAppID string + WeatherCities []string + WeatherMeasurements []string + InfluxURL string + InfluxAddr string + InfluxUser string + InfluxPass string + InfluxDB string +} diff --git a/src/data/main.go b/src/data/main.go new file mode 100644 index 0000000..5bfcb59 --- /dev/null +++ b/src/data/main.go @@ -0,0 +1,103 @@ +package data + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" + + "git.paulbsd.com/paulbsd/weather/src/config" + client "github.com/influxdata/influxdb1-client/v2" +) + +// FetchData fetch data from api +func FetchData(c *config.Config, city string) (Data, error) { + var d Data + + pollTo := 30 * time.Millisecond + + httpClient := &http.Client{Timeout: pollTo * time.Second, Transport: &http.Transport{ + IdleConnTimeout: pollTo, + DisableCompression: false, + }} + + q := fmt.Sprintf("https://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric", city, c.WeatherAppID) + + r, err := httpClient.Get(q) + if err != nil { + return Data{}, err + } + + defer r.Body.Close() + + b, err := ioutil.ReadAll(r.Body) + if err != nil { + return Data{}, err + } + + err = json.Unmarshal(b, &d) + if err != nil { + return Data{}, err + } + + d.Points = map[string]interface{}{"temperature": d.Main.Temperature, + "humidity": d.Main.Humidity, + "pressure": d.Main.Pressure} + + return d, nil +} + +// SendDataToInflux sends time series data to influxdb +func SendDataToInflux(c *config.Config, d Data) error { + httpClient, err := client.NewHTTPClient(client.HTTPConfig{ + Addr: c.InfluxURL, + Username: c.InfluxUser, + Password: c.InfluxPass, + }) + if err != nil { + return err + } + defer httpClient.Close() + + bp, err := client.NewBatchPoints(client.BatchPointsConfig{ + Database: c.InfluxDB, + }) + if err != nil { + return err + } + + for key, value := range d.Points { + tags := map[string]string{"city": d.City} + fields := map[string]interface{}{"value": value} + + point, err := client.NewPoint( + key, + tags, + fields, + time.Now(), + ) + if err != nil { + return err + } + + bp.AddPoint(point) + err = httpClient.Write(bp) + if err != nil { + return err + } + } + + return nil +} + +// Data is the struct for temperature and humidity for a city +type Data struct { + City string `json:"name"` + Main struct { + Temperature float64 `json:"temp"` + Humidity int64 `json:"humidity"` + Pressure float64 `json:"pressure"` + } + Points map[string]interface{} +} diff --git a/types.go b/types.go deleted file mode 100644 index c4ed14a..0000000 --- a/types.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -// WeatherConfig is the main configuration -type WeatherConfig struct { - WeatherAppID string - WeatherCities []string - WeatherMeasurements []string - InfluxURL string - InfluxAddr string - InfluxUser string - InfluxPass string - InfluxDB string -} - -// Data is the struct for temperature and humidity for a city -type Data struct { - City string `json:"name"` - Main struct { - Temperature float64 `json:"temp"` - Humidity int64 `json:"humidity"` - Pressure float64 `json:"pressure"` - } - Points map[string]interface{} -} diff --git a/utils/flags.go b/utils/flags.go new file mode 100644 index 0000000..d6f52a5 --- /dev/null +++ b/utils/flags.go @@ -0,0 +1,12 @@ +package utils + +import ( + "flag" + "os" +) + +// Usage displays possible arguments +func Usage() { + flag.PrintDefaults() + os.Exit(1) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 28ecc97..c5b5302 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,7 +1,7 @@ # github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc github.com/influxdata/influxdb1-client -github.com/influxdata/influxdb1-client/v2 github.com/influxdata/influxdb1-client/models github.com/influxdata/influxdb1-client/pkg/escape +github.com/influxdata/influxdb1-client/v2 # gopkg.in/ini.v1 v1.44.0 gopkg.in/ini.v1 diff --git a/weather.go b/weather.go deleted file mode 100644 index 9ac26d2..0000000 --- a/weather.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - - _ "github.com/influxdata/influxdb1-client" -) - -func main() { - var wc WeatherConfig - var configpath string - var err error - - flag.StringVar(&configpath, "configfile", "weather.ini", "config file to use with fuelprices section") - flag.Parse() - - err = GetConfig(&wc, configpath) - if err != nil { - log.Fatal(err) - } - - for _, city := range wc.WeatherCities { - d, err := FetchData(&wc, city) - if err != nil { - log.Fatal(err) - } - - err = SendToInflux(&wc, d) - if err != nil { - log.Fatal(err) - } else { - log.Println(fmt.Sprintf("Successfully sent data for %s", city)) - } - } -}