2019-06-05 21:48:21 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
|
|
|
"bytes"
|
2019-07-20 14:49:14 +02:00
|
|
|
"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"
|
|
|
|
)
|
|
|
|
|
2019-06-05 23:03:40 +02:00
|
|
|
// 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")
|
2019-07-20 14:49:14 +02:00
|
|
|
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(",")
|
2019-07-20 14:49:14 +02:00
|
|
|
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(",")
|
2019-07-20 14:49:14 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-06-05 23:03:40 +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
|
|
|
}
|
|
|
|
|
2019-06-05 23:03:40 +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
|
|
|
|
}
|
|
|
|
|
2019-06-05 23:03:40 +02:00
|
|
|
// 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:23:57 +02:00
|
|
|
|
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)
|
|
|
|
|
2019-06-26 02:23:57 +02:00
|
|
|
if list != nil {
|
|
|
|
for _, i := range list.Attr {
|
2019-07-23 21:49:46 +02:00
|
|
|
if i.Name.Local == valueattr {
|
2019-06-26 02:23:57 +02:00
|
|
|
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-26 02:23:57 +02:00
|
|
|
}
|
2019-06-05 21:48:21 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-26 02:23:57 +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)
|
|
|
|
}
|