2024-09-08 01:01:29 +02:00
|
|
|
package inflation
|
|
|
|
|
|
|
|
import (
|
|
|
|
"embed"
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"git.tijl.dev/tijl/tijl.dev-core/internal/config"
|
|
|
|
"git.tijl.dev/tijl/tijl.dev-core/modules/i18n"
|
|
|
|
log "git.tijl.dev/tijl/tijl.dev-core/modules/logger"
|
|
|
|
"git.tijl.dev/tijl/tijl.dev-core/modules/web"
|
|
|
|
"github.com/go-echarts/go-echarts/v2/charts"
|
|
|
|
"github.com/go-echarts/go-echarts/v2/opts"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Datas struct {
|
|
|
|
Name string
|
|
|
|
ProcessFunction func(map[time.Time]float32) map[time.Time]float32
|
|
|
|
}
|
|
|
|
|
|
|
|
var rawData = make(map[string]map[time.Time]float32)
|
|
|
|
|
|
|
|
var datas = map[string]Datas{
|
|
|
|
"CPIAUCSL": {
|
|
|
|
Name: "CPI USA BASE",
|
2024-09-08 12:16:04 +02:00
|
|
|
ProcessFunction: toPercentNeg,
|
2024-09-08 01:01:29 +02:00
|
|
|
},
|
2024-09-08 01:31:07 +02:00
|
|
|
"PCEPI": {
|
|
|
|
Name: "PCECPI USA",
|
2024-09-08 12:16:04 +02:00
|
|
|
ProcessFunction: toPercentNeg,
|
2024-09-08 01:31:07 +02:00
|
|
|
},
|
2024-09-08 01:01:29 +02:00
|
|
|
"M2SL": {
|
|
|
|
Name: "M2 USA",
|
2024-09-08 12:16:04 +02:00
|
|
|
ProcessFunction: toPercentNeg,
|
2024-09-08 01:01:29 +02:00
|
|
|
},
|
|
|
|
"CP0000EZ19M086NEST": {
|
|
|
|
Name: "HICP ECB",
|
2024-09-08 12:16:04 +02:00
|
|
|
ProcessFunction: toPercentNeg,
|
2024-09-08 01:01:29 +02:00
|
|
|
},
|
2024-09-08 01:31:07 +02:00
|
|
|
"MABMM301EZM189S": {
|
|
|
|
Name: "M3 ECB",
|
2024-09-08 12:16:04 +02:00
|
|
|
ProcessFunction: toPercentNeg,
|
|
|
|
},
|
|
|
|
"CBBTCUSD": {
|
|
|
|
Name: "Bitcoin",
|
|
|
|
ProcessFunction: toPercent,
|
|
|
|
},
|
|
|
|
"APU0000708111": {
|
|
|
|
Name: "Eggs",
|
2024-09-08 01:31:07 +02:00
|
|
|
ProcessFunction: toPercent,
|
|
|
|
},
|
2024-09-08 01:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//go:embed locales/*
|
|
|
|
var Embed embed.FS
|
|
|
|
|
|
|
|
func Setup() {
|
|
|
|
err := LoadData()
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("inflation.Setup: Error loading data")
|
|
|
|
}
|
|
|
|
|
|
|
|
i18n.RegisterTranslations(Embed, "locales")
|
|
|
|
|
|
|
|
web.RegisterAppSetupFunc(func(a *fiber.App) {
|
2024-09-08 12:16:04 +02:00
|
|
|
a.Get("app/inflation", func(c *fiber.Ctx) error {
|
|
|
|
processedData := make(map[string]map[time.Time]float32)
|
|
|
|
for id, data := range datas {
|
|
|
|
processedData[id] = data.ProcessFunction(rawData[id])
|
|
|
|
}
|
|
|
|
|
|
|
|
line := charts.NewLine()
|
|
|
|
|
|
|
|
startYear, endYear := getYearRange(processedData)
|
|
|
|
yearList := createYearList(startYear, endYear)
|
|
|
|
for seriesID, timeData := range processedData {
|
|
|
|
|
|
|
|
aggregatedData := aggregateDataByYear(timeData)
|
|
|
|
|
|
|
|
yAxisT := []opts.LineData{}
|
|
|
|
var useAmount float32 = 1
|
|
|
|
for _, year := range yearList {
|
|
|
|
if value, exists := aggregatedData[year]; exists {
|
|
|
|
|
|
|
|
current := (value / 100) * useAmount
|
|
|
|
useAmount = useAmount + current
|
|
|
|
yAxisT = append(yAxisT, opts.LineData{Value: useAmount})
|
|
|
|
} else {
|
|
|
|
yAxisT = append(yAxisT, opts.LineData{Value: nil})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
line.AddSeries(datas[seriesID].Name, yAxisT)
|
|
|
|
}
|
|
|
|
|
|
|
|
line.SetXAxis(yearList)
|
|
|
|
|
|
|
|
line.SetGlobalOptions(
|
|
|
|
charts.WithTitleOpts(opts.Title{
|
|
|
|
Title: "Inflation",
|
|
|
|
Subtitle: "Copyright Tijl 2024",
|
|
|
|
}),
|
|
|
|
charts.WithDataZoomOpts(opts.DataZoom{
|
|
|
|
Orient: "horizontal",
|
|
|
|
Type: "slider",
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
|
|
|
|
rendered := line.RenderSnippet()
|
|
|
|
|
|
|
|
data := *web.Common(c)
|
|
|
|
data["Title"] = "tmp"
|
|
|
|
data["RenderedScript"] = template.JS(handleScriptElement(rendered.Script))
|
|
|
|
data["RenderedElement"] = template.HTML(rendered.Element)
|
|
|
|
return c.Render("apps/inflation/index", data, "layouts/base")
|
|
|
|
|
|
|
|
})
|
|
|
|
a.Get("/app/inflation/base", func(c *fiber.Ctx) error {
|
2024-09-08 01:01:29 +02:00
|
|
|
|
|
|
|
processedData := make(map[string]map[time.Time]float32)
|
|
|
|
for id, data := range datas {
|
|
|
|
processedData[id] = data.ProcessFunction(rawData[id])
|
|
|
|
}
|
|
|
|
|
|
|
|
line := charts.NewLine()
|
|
|
|
|
|
|
|
startYear, endYear := getYearRange(processedData)
|
|
|
|
yearList := createYearList(startYear, endYear)
|
|
|
|
for seriesID, timeData := range processedData {
|
|
|
|
|
|
|
|
aggregatedData := aggregateDataByYear(timeData)
|
|
|
|
|
|
|
|
yAxisT := []opts.LineData{}
|
|
|
|
for _, year := range yearList {
|
|
|
|
if value, exists := aggregatedData[year]; exists {
|
|
|
|
yAxisT = append(yAxisT, opts.LineData{Value: value})
|
|
|
|
} else {
|
|
|
|
yAxisT = append(yAxisT, opts.LineData{Value: nil})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
line.AddSeries(datas[seriesID].Name, yAxisT)
|
|
|
|
}
|
|
|
|
|
|
|
|
line.SetXAxis(yearList)
|
|
|
|
|
|
|
|
line.SetGlobalOptions(
|
|
|
|
charts.WithTitleOpts(opts.Title{
|
|
|
|
Title: "Inflation",
|
|
|
|
Subtitle: "Copyright Tijl 2024",
|
|
|
|
}),
|
|
|
|
charts.WithDataZoomOpts(opts.DataZoom{
|
|
|
|
Orient: "horizontal",
|
|
|
|
Type: "slider",
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
|
|
|
|
rendered := line.RenderSnippet()
|
|
|
|
|
|
|
|
data := *web.Common(c)
|
|
|
|
data["Title"] = "tmp"
|
|
|
|
data["RenderedScript"] = template.JS(handleScriptElement(rendered.Script))
|
|
|
|
data["RenderedElement"] = template.HTML(rendered.Element)
|
|
|
|
return c.Render("apps/inflation/index", data, "layouts/base")
|
|
|
|
})
|
|
|
|
}, 1000)
|
|
|
|
}
|
|
|
|
|
|
|
|
func LoadData() error {
|
|
|
|
|
|
|
|
var baseDataPath = config.Config.DataLocation + "/apps/inflation/data/"
|
|
|
|
if _, err := os.Stat(baseDataPath); os.IsNotExist(err) {
|
|
|
|
err := os.MkdirAll(baseDataPath, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load fred data
|
|
|
|
const fredBaseUrl = "https://fred.stlouisfed.org/graph/fredgraph.csv?id="
|
|
|
|
|
|
|
|
for id := range datas {
|
|
|
|
info, err := os.Stat(baseDataPath + id + ".csv")
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
modTime := info.ModTime()
|
|
|
|
currentTime := time.Now()
|
|
|
|
|
|
|
|
if modTime.Year() == currentTime.Year() && modTime.Month() == currentTime.Month() {
|
|
|
|
csvdata, err := readCSV(baseDataPath + id + ".csv")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rawData[id] = csvdata
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := http.Get(fredBaseUrl + id)
|
2024-09-08 01:31:07 +02:00
|
|
|
log.Debug().Str("id", id).Msg("inflation.LoadData: getting")
|
2024-09-08 01:01:29 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
return fmt.Errorf("failed to fetch data: %v", resp.Status)
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := os.Create(baseDataPath + id + ".csv")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
_, err = io.Copy(file, resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
csvdata, err := readCSV(baseDataPath + id + ".csv")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rawData[id] = csvdata
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|