fuelprices/functions.go

192 lines
4.4 KiB
Go
Raw Normal View History

2019-06-05 21:48:21 +02:00
package main
import (
"archive/zip"
"bytes"
"errors"
2019-06-05 21:48:21 +02:00
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
2019-06-26 02:10:35 +02:00
"time"
2019-06-05 21:48:21 +02:00
"github.com/antchfx/xmlquery"
2019-06-26 02:10:35 +02:00
_ "github.com/influxdata/influxdb1-client"
client "github.com/influxdata/influxdb1-client/v2"
2019-06-05 21:48:21 +02:00
"gopkg.in/ini.v1"
)
// GetConfig fetch config from ini file
2019-07-23 21:49:46 +02:00
func (fpc *FuelPricesConfig) GetConfig() error {
2019-06-05 21:48:21 +02:00
flag.Usage = Usage
2019-07-23 21:49:46 +02:00
flag.StringVar(&fpc.ConfigPath, "configfile", "common.ini", "config file to use with fuelprices section")
flag.Parse()
config, err := ini.Load(fpc.ConfigPath)
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
2019-06-05 21:48:21 +02:00
fuelpricesSection := config.Section("fuelprices")
fpc.RemoteURL = fuelpricesSection.Key("remote_url").MustString("https://donnees.roulez-eco.fr/opendata/instantane")
fpc.RemoteFilename = fuelpricesSection.Key("remote_filename").MustString("PrixCarburants_instantane.xml")
fpc.XPathBase = fuelpricesSection.Key("xpath_base").MustString(".//pdv[@id='%s']/prix[@nom='%s']")
fpc.Table = fuelpricesSection.Key("table").MustString("fuel_price")
2019-06-09 13:40:19 +02:00
fpc.Pos = fuelpricesSection.Key("pos").Strings(",")
if len(fpc.Pos) < 1 {
err := errors.New("No pos defined")
return err
}
2019-06-09 13:40:19 +02:00
fpc.Types = fuelpricesSection.Key("types").Strings(",")
if len(fpc.Types) < 1 {
err := errors.New("No fuel types defined")
return err
}
influxdbSection := config.Section("influxdb")
fpc.InfluxHost = influxdbSection.Key("hostname").MustString("localhost")
fpc.InfluxPort = influxdbSection.Key("port").MustInt(8086)
fpc.InfluxUser = influxdbSection.Key("username").MustString("username")
fpc.InfluxPass = influxdbSection.Key("password").MustString("password")
fpc.InfluxDB = influxdbSection.Key("database").MustString("me")
2019-06-05 21:48:21 +02:00
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
return nil
2019-06-05 21:48:21 +02:00
}
// DownloadFile fetch file from webserver
2019-07-23 21:49:46 +02:00
func DownloadFile(fpc *FuelPricesConfig, zipfile *ZipFile) error {
2019-06-26 02:10:35 +02:00
pollTo := 30 * time.Millisecond
2019-07-23 21:49:46 +02:00
client := &http.Client{Timeout: pollTo * time.Second, Transport: &http.Transport{
2019-06-26 02:10:35 +02:00
IdleConnTimeout: pollTo,
DisableCompression: false,
}}
2019-07-23 21:49:46 +02:00
resp, err := client.Get(fpc.RemoteURL)
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
2019-06-05 21:48:21 +02:00
2019-06-26 02:10:35 +02:00
defer resp.Body.Close()
2019-07-23 21:49:46 +02:00
zipfile.Content, err = ioutil.ReadAll(resp.Body)
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
2019-06-05 21:48:21 +02:00
2019-06-26 02:10:35 +02:00
time.Sleep(pollTo)
2019-06-05 21:48:21 +02:00
2019-07-09 16:41:35 +02:00
return nil
2019-06-05 21:48:21 +02:00
}
// ExtractZip get the XML file to be processed
2019-07-23 21:49:46 +02:00
func ExtractZip(fpc *FuelPricesConfig, zipfile *ZipFile, xmlfile *XMLFile) error {
unzipped, err := zip.NewReader(bytes.NewReader(zipfile.Content), int64(len(zipfile.Content)))
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
2019-06-05 21:48:21 +02:00
2019-07-23 21:49:46 +02:00
for _, file := range unzipped.File {
if file.Name == fpc.RemoteFilename {
rc, err := file.Open()
2019-06-05 21:48:21 +02:00
if err != nil {
return err
}
2019-07-23 21:49:46 +02:00
xmlfile.Content, err = ioutil.ReadAll(rc)
2019-06-05 21:48:21 +02:00
rc.Close()
} else {
log.Fatal("File not found")
}
}
return err
}
// GetPrices parses the XML file and get values of prices
2019-07-23 21:49:46 +02:00
func GetPrices(fpc *FuelPricesConfig, prices *[]Price, xmlfile *XMLFile) error {
2019-06-05 21:48:21 +02:00
var xml *xmlquery.Node
2019-07-23 21:49:46 +02:00
var valueattr = "valeur"
2019-07-09 16:41:35 +02:00
2019-07-23 21:49:46 +02:00
file := bytes.NewReader(xmlfile.Content)
xml, err := xmlquery.Parse(file)
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
2019-06-05 21:48:21 +02:00
2019-06-26 02:10:35 +02:00
for _, station := range fpc.Pos {
for _, fuel := range fpc.Types {
2019-06-26 02:10:35 +02:00
query := fmt.Sprintf(fpc.XPathBase, station, fuel)
2019-06-05 21:48:21 +02:00
list := xmlquery.FindOne(xml, query)
if list != nil {
for _, i := range list.Attr {
2019-07-23 21:49:46 +02:00
if i.Name.Local == valueattr {
if s, err := strconv.ParseFloat(i.Value, 64); err == nil {
2019-07-23 21:49:46 +02:00
*prices = append(*prices, Price{ID: station, Fuel: fuel, Amount: s})
}
2019-06-05 21:48:21 +02:00
}
}
} else {
log.Println(fmt.Sprintf("Fuel type not found for point of sale, skipping. Query : %s", query))
2019-06-05 21:48:21 +02:00
}
}
}
return err
}
2019-06-26 02:10:35 +02:00
// SendToInflux sends time series data to influxdb
2019-07-09 16:41:35 +02:00
func SendToInflux(fpc *FuelPricesConfig, prices *[]Price) error {
2019-06-26 02:10:35 +02:00
httpClient, err := client.NewHTTPClient(client.HTTPConfig{
Addr: fmt.Sprintf("http://%s:%d", fpc.InfluxHost, fpc.InfluxPort),
Username: fpc.InfluxUser,
Password: fpc.InfluxPass,
})
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
2019-06-26 02:10:35 +02:00
bp, err := client.NewBatchPoints(client.BatchPointsConfig{
Database: fpc.InfluxDB,
})
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
2019-06-26 02:10:35 +02:00
for _, p := range *prices {
tags := map[string]string{"pdv": p.ID, "fuel": p.Fuel}
fields := map[string]interface{}{"value": p.Amount}
point, _ := client.NewPoint(
fpc.Table,
tags,
fields,
time.Now(),
)
log.Println(point)
bp.AddPoint(point)
err = httpClient.Write(bp)
2019-07-09 16:41:35 +02:00
if err != nil {
return err
}
2019-06-05 21:48:21 +02:00
}
2019-07-09 16:41:35 +02:00
return nil
2019-06-05 21:48:21 +02:00
}
// Usage displays possible arguments
func Usage() {
flag.PrintDefaults()
os.Exit(1)
}