From f21a8e5736eac67b1970ea0b1b11c91e82dba91f Mon Sep 17 00:00:00 2001 From: shoopea Date: Sat, 11 Dec 2021 11:50:24 +0800 Subject: [PATCH] set colors --- bot.go | 922 -------------------------------------------- bot_graph.go | 930 +++++++++++++++++++++++++++++++++++++++++++++ config.go | 44 +++ config.sample.json | 3 +- version.go | 8 +- 5 files changed, 980 insertions(+), 927 deletions(-) create mode 100644 bot_graph.go diff --git a/bot.go b/bot.go index 94d9493..c738d6f 100644 --- a/bot.go +++ b/bot.go @@ -5,16 +5,10 @@ import ( "encoding/hex" "encoding/json" "fmt" - "math" "regexp" - "sort" "strconv" "time" - "gonum.org/v1/plot" - "gonum.org/v1/plot/plotter" - "gonum.org/v1/plot/plotutil" - "gonum.org/v1/plot/vg" tb "gopkg.in/tucnak/telebot.v2" ) @@ -938,922 +932,6 @@ func botRegister(m *tb.Message) { return } -func botGraphValue(m *tb.Message) { - var ( - maxVal float64 - unitFactor float64 - unitName string - ) - for _, dStats := range cfg.StatsMonthly { - for _, stat := range dStats { - valueFloat := float64(stat.CompanyValueLastQuarter) - if math.Abs(valueFloat) > maxVal { - maxVal = math.Abs(valueFloat) - } - } - } - if maxVal > 1000000000 { - unitFactor = 1000000000 - unitName = "billion" - } else if maxVal > 1000000 { - unitFactor = 1000000 - unitName = "million" - } else { - unitFactor = 1 - unitName = "" - } - - var vals map[int]plotter.XYs - vals = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - vals[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphValue : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - valueFloat := float64(stat.CompanyValueLastQuarter) - pt := plotter.XY{ - X: dateFloat, - Y: valueFloat / unitFactor, - } - vals[ccID] = append(vals[ccID], pt) - } - sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Company Values" - p.X.Label.Text = "Year" - if unitName != "" { - p.Y.Label.Text = fmt.Sprintf("Value (%s)", unitName) - } else { - p.Y.Label.Text = "Value" - } - - i := 0 - for ccID, xys := range vals { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphValue : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphValue : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphValueDelta(m *tb.Message) { - var ( - maxVal float64 - unitFactor float64 - unitName string - ) - - var vals map[int]plotter.XYs - vals = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - vals[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphValueDelta : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - pt := plotter.XY{ - X: float64(d.Year()) + float64(d.Month()-1)/12, - Y: float64(stat.CompanyValuePreviousQuarter), - } - vals[ccID] = append(vals[ccID], pt) - } - sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) - } - - for ccID, v := range vals { - v2 := make(plotter.XYs, 0) - for i := 0; i < len(v); i = i + 3 { - v2 = append(v2, v[i]) - } - vals[ccID] = v2 - } - - for ccID, v := range vals { - v0 := v[0].Y - for i := 1; i < len(v); i++ { - v[i].Y, v0 = v[i].Y-v0, v[i].Y - if math.Abs(v[i].Y) > maxVal { - maxVal = math.Abs(v[i].Y) - } - } - vals[ccID] = v[1:] - } - - if maxVal > 1000000000 { - unitFactor = 1000000000 - unitName = "billion" - } else if maxVal > 1000000 { - unitFactor = 1000000 - unitName = "million" - } else { - unitFactor = 1 - unitName = "" - } - - for ccID, v := range vals { - for i := 0; i < len(v); i++ { - v[i].Y = v[i].Y / unitFactor - if math.IsNaN(v[i].Y) { - logInfoDebug("botGraphValueDelta : NaN : %d / %f", ccID, v[i].X) - } - } - vals[ccID] = v - } - - p := plot.New() - p.Title.Text = "Company Values (delta)" - p.X.Label.Text = "Year" - if unitName != "" { - p.Y.Label.Text = fmt.Sprintf("Variation (%s)", unitName) - } else { - p.Y.Label.Text = "Variation" - } - - i := 0 - for ccID, xys := range vals { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphValueDelta : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphValueDelta : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphMoney(m *tb.Message) { - var ( - maxVal float64 - unitFactor float64 - unitName string - ) - for _, dStats := range cfg.StatsMonthly { - for _, stat := range dStats { - valueFloat := float64(stat.Money) - if math.Abs(valueFloat) > maxVal { - maxVal = math.Abs(valueFloat) - } - } - } - if maxVal > 1000000000 { - unitFactor = 1000000000 - unitName = "billion" - } else if maxVal > 1000000 { - unitFactor = 1000000 - unitName = "million" - } else { - unitFactor = 1 - unitName = "" - } - - var vals map[int]plotter.XYs - vals = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - vals[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphMoney : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - valueFloat := float64(stat.Money) - pt := plotter.XY{ - X: dateFloat, - Y: valueFloat / unitFactor, - } - vals[ccID] = append(vals[ccID], pt) - } - sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Company Money" - p.X.Label.Text = "Year" - if unitName != "" { - p.Y.Label.Text = fmt.Sprintf("Money (%s)", unitName) - } else { - p.Y.Label.Text = "Money" - } - - i := 0 - for ccID, xys := range vals { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphMoney : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphMoney : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphIncome(m *tb.Message) { - var ( - maxVal float64 - unitFactor float64 - unitName string - ) - for _, dStats := range cfg.StatsMonthly { - for _, stat := range dStats { - valueFloat := float64(stat.Income) - if math.Abs(valueFloat) > maxVal { - maxVal = math.Abs(valueFloat) - } - } - } - if maxVal > 1000000000 { - unitFactor = 1000000000 - unitName = "billion" - } else if maxVal > 1000000 { - unitFactor = 1000000 - unitName = "million" - } else { - unitFactor = 1 - unitName = "" - } - - var vals map[int]plotter.XYs - vals = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - vals[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphIncome : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - valueFloat := float64(stat.Income) - pt := plotter.XY{ - X: dateFloat, - Y: valueFloat / unitFactor, - } - vals[ccID] = append(vals[ccID], pt) - } - sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Company Income" - p.X.Label.Text = "Year" - if unitName != "" { - p.Y.Label.Text = fmt.Sprintf("Income (%s)", unitName) - } else { - p.Y.Label.Text = "Income" - } - - i := 0 - for ccID, xys := range vals { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphIncome : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphIncome : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphLoan(m *tb.Message) { - var ( - maxVal float64 - unitFactor float64 - unitName string - ) - for _, dStats := range cfg.StatsMonthly { - for _, stat := range dStats { - valueFloat := float64(stat.Loan) - if math.Abs(valueFloat) > maxVal { - maxVal = math.Abs(valueFloat) - } - } - } - if maxVal > 1000000000 { - unitFactor = 1000000000 - unitName = "billion" - } else if maxVal > 1000000 { - unitFactor = 1000000 - unitName = "million" - } else { - unitFactor = 1 - unitName = "" - } - - var vals map[int]plotter.XYs - vals = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - vals[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphLoan : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - valueFloat := float64(stat.Loan) - pt := plotter.XY{ - X: dateFloat, - Y: valueFloat / unitFactor, - } - vals[ccID] = append(vals[ccID], pt) - } - sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Company Loan" - p.X.Label.Text = "Year" - if unitName != "" { - p.Y.Label.Text = fmt.Sprintf("Loan (%s)", unitName) - } else { - p.Y.Label.Text = "Loan" - } - - i := 0 - for ccID, xys := range vals { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphLoan : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphLoan : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphPerf(m *tb.Message) { - var ( - maxVal float64 - unitFactor float64 - unitName string - ) - for _, dStats := range cfg.StatsMonthly { - for _, stat := range dStats { - valueFloat := float64(stat.PerformancePreviousQuarter) - if math.Abs(valueFloat) > maxVal { - maxVal = math.Abs(valueFloat) - } - } - } - if maxVal > 1000000000 { - unitFactor = 1000000000 - unitName = "billion" - } else if maxVal > 1000000 { - unitFactor = 1000000 - unitName = "million" - } else { - unitFactor = 1 - unitName = "" - } - - var vals map[int]plotter.XYs - vals = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - vals[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphPerf : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - valueFloat := float64(stat.PerformancePreviousQuarter) - pt := plotter.XY{ - X: dateFloat, - Y: valueFloat / unitFactor, - } - vals[ccID] = append(vals[ccID], pt) - } - sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Company Perf" - p.X.Label.Text = "Year" - if unitName != "" { - p.Y.Label.Text = fmt.Sprintf("Perf (%s)", unitName) - } else { - p.Y.Label.Text = "Perf" - } - - i := 0 - for ccID, xys := range vals { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphPerf : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphPerf : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphPlanes(m *tb.Message) { - var planes, airports map[int]plotter.XYs - planes = make(map[int]plotter.XYs) - airports = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - planes[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphPlanes : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - pt := plotter.XY{ - X: dateFloat, - Y: float64(stat.Planes), - } - planes[ccID] = append(planes[ccID], pt) - pt = plotter.XY{ - X: dateFloat, - Y: float64(stat.Airports), - } - airports[ccID] = append(airports[ccID], pt) - } - sort.Slice(planes[ccID], func(i, j int) bool { return planes[ccID][i].X < planes[ccID][j].X }) - sort.Slice(airports[ccID], func(i, j int) bool { return airports[ccID][i].X < airports[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Planes summary" - p.X.Label.Text = "Year" - p.Y.Label.Text = "Planes" - p.Y.Min = 0 - - i := 0 - for ccID, xys := range planes { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphPlanes : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphPlanes : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphBusses(m *tb.Message) { - var busses, busStops map[int]plotter.XYs - busses = make(map[int]plotter.XYs) - busStops = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - busses[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphBusses : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - pt := plotter.XY{ - X: dateFloat, - Y: float64(stat.Busses), - } - busses[ccID] = append(busses[ccID], pt) - pt = plotter.XY{ - X: dateFloat, - Y: float64(stat.BusStops), - } - busStops[ccID] = append(busStops[ccID], pt) - } - sort.Slice(busses[ccID], func(i, j int) bool { return busses[ccID][i].X < busses[ccID][j].X }) - sort.Slice(busStops[ccID], func(i, j int) bool { return busStops[ccID][i].X < busStops[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Busses summary" - p.X.Label.Text = "Year" - p.Y.Label.Text = "Busses" - p.Y.Min = 0 - - i := 0 - for ccID, xys := range busses { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphBusses : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphBusses : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphTrains(m *tb.Message) { - var trains, trainStations map[int]plotter.XYs - trains = make(map[int]plotter.XYs) - trainStations = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - trains[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphTrains : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - pt := plotter.XY{ - X: dateFloat, - Y: float64(stat.Trains), - } - trains[ccID] = append(trains[ccID], pt) - pt = plotter.XY{ - X: dateFloat, - Y: float64(stat.TrainStations), - } - trainStations[ccID] = append(trainStations[ccID], pt) - } - sort.Slice(trains[ccID], func(i, j int) bool { return trains[ccID][i].X < trains[ccID][j].X }) - sort.Slice(trainStations[ccID], func(i, j int) bool { return trainStations[ccID][i].X < trainStations[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Trains summary" - p.X.Label.Text = "Year" - p.Y.Label.Text = "Trains" - p.Y.Min = 0 - - i := 0 - for ccID, xys := range trains { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphTrains : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphTrains : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphLorries(m *tb.Message) { - var lorries, lorriesStations map[int]plotter.XYs - lorries = make(map[int]plotter.XYs) - lorriesStations = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - lorries[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphLorries : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - pt := plotter.XY{ - X: dateFloat, - Y: float64(stat.Lorries), - } - lorries[ccID] = append(lorries[ccID], pt) - pt = plotter.XY{ - X: dateFloat, - Y: float64(stat.LorryStations), - } - lorriesStations[ccID] = append(lorriesStations[ccID], pt) - } - sort.Slice(lorries[ccID], func(i, j int) bool { return lorries[ccID][i].X < lorries[ccID][j].X }) - sort.Slice(lorriesStations[ccID], func(i, j int) bool { return lorriesStations[ccID][i].X < lorriesStations[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Lorries summary" - p.X.Label.Text = "Year" - p.Y.Label.Text = "Lorries" - p.Y.Min = 0 - - i := 0 - for ccID, xys := range lorries { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphLorries : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphLorries : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - -func botGraphShips(m *tb.Message) { - var ships, harbours map[int]plotter.XYs - ships = make(map[int]plotter.XYs) - harbours = make(map[int]plotter.XYs) - for ccID, dStats := range cfg.StatsMonthly { - ships[ccID] = make(plotter.XYs, 0) - for dStr, stat := range dStats { - d, err := time.Parse("20060102", dStr) - logErrorDebug(err, "botGraphShips : time.Parse") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) - return - } - dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 - pt := plotter.XY{ - X: dateFloat, - Y: float64(stat.Ships), - } - ships[ccID] = append(ships[ccID], pt) - pt = plotter.XY{ - X: dateFloat, - Y: float64(stat.Harbours), - } - harbours[ccID] = append(harbours[ccID], pt) - } - sort.Slice(ships[ccID], func(i, j int) bool { return ships[ccID][i].X < ships[ccID][j].X }) - sort.Slice(harbours[ccID], func(i, j int) bool { return harbours[ccID][i].X < harbours[ccID][j].X }) - } - - p := plot.New() - p.Title.Text = "Ships summary" - p.X.Label.Text = "Year" - p.Y.Label.Text = "Ships" - p.Y.Min = 0 - - i := 0 - for ccID, xys := range ships { - cc := cfg.Clients[ccID] - - l, s, err := plotter.NewLinePoints(xys) - logErrorDebug(err, "botGraphShips : plotter.NewLinePoints") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) - return - } - - l.Color = plotutil.Color(i) - l.Dashes = plotutil.Dashes(2) - s.Color = plotutil.Color(i) - s.Shape = plotutil.Shape(0) - - p.Add(l) - p.Add(s) - p.Legend.Add(cc.Username, l, s) - - i++ - } - - err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") - logErrorDebug(err, "botGraphShips : plot.Save") - if err != nil { - bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) - return - } - - bot.SendChatImage(m.Chat.ID, "/app/data/points.png") - - return -} - func PrintText(m *tb.Message) { logInfoDebug("[%d] %s(%d) | %s(%d) : %s\n", m.ID, m.Chat.Title, m.Chat.ID, m.Sender.Username, m.Sender.ID, m.Text) return diff --git a/bot_graph.go b/bot_graph.go new file mode 100644 index 0000000..b88d0ea --- /dev/null +++ b/bot_graph.go @@ -0,0 +1,930 @@ +package main + +import ( + "fmt" + "math" + "sort" + "time" + + "gonum.org/v1/plot" + "gonum.org/v1/plot/plotter" + "gonum.org/v1/plot/plotutil" + "gonum.org/v1/plot/vg" + tb "gopkg.in/tucnak/telebot.v2" +) + +func botGraphValue(m *tb.Message) { + var ( + maxVal float64 + unitFactor float64 + unitName string + ) + for _, dStats := range cfg.StatsMonthly { + for _, stat := range dStats { + valueFloat := float64(stat.CompanyValueLastQuarter) + if math.Abs(valueFloat) > maxVal { + maxVal = math.Abs(valueFloat) + } + } + } + if maxVal > 1000000000 { + unitFactor = 1000000000 + unitName = "billion" + } else if maxVal > 1000000 { + unitFactor = 1000000 + unitName = "million" + } else { + unitFactor = 1 + unitName = "" + } + + var vals map[int]plotter.XYs + vals = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + vals[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphValue : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + valueFloat := float64(stat.CompanyValueLastQuarter) + pt := plotter.XY{ + X: dateFloat, + Y: valueFloat / unitFactor, + } + vals[ccID] = append(vals[ccID], pt) + } + sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Company Values" + p.X.Label.Text = "Year" + if unitName != "" { + p.Y.Label.Text = fmt.Sprintf("Value (%s)", unitName) + } else { + p.Y.Label.Text = "Value" + } + + i := 0 + for ccID, xys := range vals { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphValue : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphValue : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphValueDelta(m *tb.Message) { + var ( + maxVal float64 + unitFactor float64 + unitName string + ) + + var vals map[int]plotter.XYs + vals = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + vals[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphValueDelta : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + pt := plotter.XY{ + X: float64(d.Year()) + float64(d.Month()-1)/12, + Y: float64(stat.CompanyValuePreviousQuarter), + } + vals[ccID] = append(vals[ccID], pt) + } + sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) + } + + for ccID, v := range vals { + v2 := make(plotter.XYs, 0) + for i := 0; i < len(v); i = i + 3 { + v2 = append(v2, v[i]) + } + vals[ccID] = v2 + } + + for ccID, v := range vals { + v0 := v[0].Y + for i := 1; i < len(v); i++ { + v[i].Y, v0 = v[i].Y-v0, v[i].Y + if math.Abs(v[i].Y) > maxVal { + maxVal = math.Abs(v[i].Y) + } + } + vals[ccID] = v[1:] + } + + if maxVal > 1000000000 { + unitFactor = 1000000000 + unitName = "billion" + } else if maxVal > 1000000 { + unitFactor = 1000000 + unitName = "million" + } else { + unitFactor = 1 + unitName = "" + } + + for ccID, v := range vals { + for i := 0; i < len(v); i++ { + v[i].Y = v[i].Y / unitFactor + if math.IsNaN(v[i].Y) { + logInfoDebug("botGraphValueDelta : NaN : %d / %f", ccID, v[i].X) + } + } + vals[ccID] = v + } + + p := plot.New() + p.Title.Text = "Company Values (delta)" + p.X.Label.Text = "Year" + if unitName != "" { + p.Y.Label.Text = fmt.Sprintf("Variation (%s)", unitName) + } else { + p.Y.Label.Text = "Variation" + } + + i := 0 + for ccID, xys := range vals { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphValueDelta : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphValueDelta : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphMoney(m *tb.Message) { + var ( + maxVal float64 + unitFactor float64 + unitName string + ) + for _, dStats := range cfg.StatsMonthly { + for _, stat := range dStats { + valueFloat := float64(stat.Money) + if math.Abs(valueFloat) > maxVal { + maxVal = math.Abs(valueFloat) + } + } + } + if maxVal > 1000000000 { + unitFactor = 1000000000 + unitName = "billion" + } else if maxVal > 1000000 { + unitFactor = 1000000 + unitName = "million" + } else { + unitFactor = 1 + unitName = "" + } + + var vals map[int]plotter.XYs + vals = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + vals[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphMoney : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + valueFloat := float64(stat.Money) + pt := plotter.XY{ + X: dateFloat, + Y: valueFloat / unitFactor, + } + vals[ccID] = append(vals[ccID], pt) + } + sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Company Money" + p.X.Label.Text = "Year" + if unitName != "" { + p.Y.Label.Text = fmt.Sprintf("Money (%s)", unitName) + } else { + p.Y.Label.Text = "Money" + } + + i := 0 + for ccID, xys := range vals { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphMoney : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphMoney : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphIncome(m *tb.Message) { + var ( + maxVal float64 + unitFactor float64 + unitName string + ) + for _, dStats := range cfg.StatsMonthly { + for _, stat := range dStats { + valueFloat := float64(stat.Income) + if math.Abs(valueFloat) > maxVal { + maxVal = math.Abs(valueFloat) + } + } + } + if maxVal > 1000000000 { + unitFactor = 1000000000 + unitName = "billion" + } else if maxVal > 1000000 { + unitFactor = 1000000 + unitName = "million" + } else { + unitFactor = 1 + unitName = "" + } + + var vals map[int]plotter.XYs + vals = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + vals[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphIncome : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + valueFloat := float64(stat.Income) + pt := plotter.XY{ + X: dateFloat, + Y: valueFloat / unitFactor, + } + vals[ccID] = append(vals[ccID], pt) + } + sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Company Income" + p.X.Label.Text = "Year" + if unitName != "" { + p.Y.Label.Text = fmt.Sprintf("Income (%s)", unitName) + } else { + p.Y.Label.Text = "Income" + } + + i := 0 + for ccID, xys := range vals { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphIncome : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphIncome : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphLoan(m *tb.Message) { + var ( + maxVal float64 + unitFactor float64 + unitName string + ) + for _, dStats := range cfg.StatsMonthly { + for _, stat := range dStats { + valueFloat := float64(stat.Loan) + if math.Abs(valueFloat) > maxVal { + maxVal = math.Abs(valueFloat) + } + } + } + if maxVal > 1000000000 { + unitFactor = 1000000000 + unitName = "billion" + } else if maxVal > 1000000 { + unitFactor = 1000000 + unitName = "million" + } else { + unitFactor = 1 + unitName = "" + } + + var vals map[int]plotter.XYs + vals = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + vals[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphLoan : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + valueFloat := float64(stat.Loan) + pt := plotter.XY{ + X: dateFloat, + Y: valueFloat / unitFactor, + } + vals[ccID] = append(vals[ccID], pt) + } + sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Company Loan" + p.X.Label.Text = "Year" + if unitName != "" { + p.Y.Label.Text = fmt.Sprintf("Loan (%s)", unitName) + } else { + p.Y.Label.Text = "Loan" + } + + i := 0 + for ccID, xys := range vals { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphLoan : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphLoan : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphPerf(m *tb.Message) { + var ( + maxVal float64 + unitFactor float64 + unitName string + ) + for _, dStats := range cfg.StatsMonthly { + for _, stat := range dStats { + valueFloat := float64(stat.PerformancePreviousQuarter) + if math.Abs(valueFloat) > maxVal { + maxVal = math.Abs(valueFloat) + } + } + } + if maxVal > 1000000000 { + unitFactor = 1000000000 + unitName = "billion" + } else if maxVal > 1000000 { + unitFactor = 1000000 + unitName = "million" + } else { + unitFactor = 1 + unitName = "" + } + + var vals map[int]plotter.XYs + vals = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + vals[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphPerf : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + valueFloat := float64(stat.PerformancePreviousQuarter) + pt := plotter.XY{ + X: dateFloat, + Y: valueFloat / unitFactor, + } + vals[ccID] = append(vals[ccID], pt) + } + sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Company Perf" + p.X.Label.Text = "Year" + if unitName != "" { + p.Y.Label.Text = fmt.Sprintf("Perf (%s)", unitName) + } else { + p.Y.Label.Text = "Perf" + } + + i := 0 + for ccID, xys := range vals { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphPerf : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphPerf : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphPlanes(m *tb.Message) { + var planes, airports map[int]plotter.XYs + planes = make(map[int]plotter.XYs) + airports = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + planes[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphPlanes : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + pt := plotter.XY{ + X: dateFloat, + Y: float64(stat.Planes), + } + planes[ccID] = append(planes[ccID], pt) + pt = plotter.XY{ + X: dateFloat, + Y: float64(stat.Airports), + } + airports[ccID] = append(airports[ccID], pt) + } + sort.Slice(planes[ccID], func(i, j int) bool { return planes[ccID][i].X < planes[ccID][j].X }) + sort.Slice(airports[ccID], func(i, j int) bool { return airports[ccID][i].X < airports[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Planes summary" + p.X.Label.Text = "Year" + p.Y.Label.Text = "Planes" + p.Y.Min = 0 + + i := 0 + for ccID, xys := range planes { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphPlanes : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphPlanes : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphBusses(m *tb.Message) { + var busses, busStops map[int]plotter.XYs + busses = make(map[int]plotter.XYs) + busStops = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + busses[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphBusses : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + pt := plotter.XY{ + X: dateFloat, + Y: float64(stat.Busses), + } + busses[ccID] = append(busses[ccID], pt) + pt = plotter.XY{ + X: dateFloat, + Y: float64(stat.BusStops), + } + busStops[ccID] = append(busStops[ccID], pt) + } + sort.Slice(busses[ccID], func(i, j int) bool { return busses[ccID][i].X < busses[ccID][j].X }) + sort.Slice(busStops[ccID], func(i, j int) bool { return busStops[ccID][i].X < busStops[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Busses summary" + p.X.Label.Text = "Year" + p.Y.Label.Text = "Busses" + p.Y.Min = 0 + + i := 0 + for ccID, xys := range busses { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphBusses : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphBusses : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphTrains(m *tb.Message) { + var trains, trainStations map[int]plotter.XYs + trains = make(map[int]plotter.XYs) + trainStations = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + trains[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphTrains : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + pt := plotter.XY{ + X: dateFloat, + Y: float64(stat.Trains), + } + trains[ccID] = append(trains[ccID], pt) + pt = plotter.XY{ + X: dateFloat, + Y: float64(stat.TrainStations), + } + trainStations[ccID] = append(trainStations[ccID], pt) + } + sort.Slice(trains[ccID], func(i, j int) bool { return trains[ccID][i].X < trains[ccID][j].X }) + sort.Slice(trainStations[ccID], func(i, j int) bool { return trainStations[ccID][i].X < trainStations[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Trains summary" + p.X.Label.Text = "Year" + p.Y.Label.Text = "Trains" + p.Y.Min = 0 + + i := 0 + for ccID, xys := range trains { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphTrains : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphTrains : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphLorries(m *tb.Message) { + var lorries, lorriesStations map[int]plotter.XYs + lorries = make(map[int]plotter.XYs) + lorriesStations = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + lorries[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphLorries : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + pt := plotter.XY{ + X: dateFloat, + Y: float64(stat.Lorries), + } + lorries[ccID] = append(lorries[ccID], pt) + pt = plotter.XY{ + X: dateFloat, + Y: float64(stat.LorryStations), + } + lorriesStations[ccID] = append(lorriesStations[ccID], pt) + } + sort.Slice(lorries[ccID], func(i, j int) bool { return lorries[ccID][i].X < lorries[ccID][j].X }) + sort.Slice(lorriesStations[ccID], func(i, j int) bool { return lorriesStations[ccID][i].X < lorriesStations[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Lorries summary" + p.X.Label.Text = "Year" + p.Y.Label.Text = "Lorries" + p.Y.Min = 0 + + i := 0 + for ccID, xys := range lorries { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphLorries : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphLorries : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} + +func botGraphShips(m *tb.Message) { + var ships, harbours map[int]plotter.XYs + ships = make(map[int]plotter.XYs) + harbours = make(map[int]plotter.XYs) + for ccID, dStats := range cfg.StatsMonthly { + ships[ccID] = make(plotter.XYs, 0) + for dStr, stat := range dStats { + d, err := time.Parse("20060102", dStr) + logErrorDebug(err, "botGraphShips : time.Parse") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err)) + return + } + dateFloat := float64(d.Year()) + float64(d.Month()-1)/12 + pt := plotter.XY{ + X: dateFloat, + Y: float64(stat.Ships), + } + ships[ccID] = append(ships[ccID], pt) + pt = plotter.XY{ + X: dateFloat, + Y: float64(stat.Harbours), + } + harbours[ccID] = append(harbours[ccID], pt) + } + sort.Slice(ships[ccID], func(i, j int) bool { return ships[ccID][i].X < ships[ccID][j].X }) + sort.Slice(harbours[ccID], func(i, j int) bool { return harbours[ccID][i].X < harbours[ccID][j].X }) + } + + p := plot.New() + p.Title.Text = "Ships summary" + p.X.Label.Text = "Year" + p.Y.Label.Text = "Ships" + p.Y.Min = 0 + + i := 0 + for ccID, xys := range ships { + cc := cfg.Clients[ccID] + + l, s, err := plotter.NewLinePoints(xys) + logErrorDebug(err, "botGraphShips : plotter.NewLinePoints") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err)) + return + } + + l.Color = cfg.GetClientColor(ccID) + l.Dashes = plotutil.Dashes(2) + s.Color = cfg.GetClientColor(ccID) + s.Shape = plotutil.Shape(0) + + p.Add(l) + p.Add(s) + p.Legend.Add(cc.Username, l, s) + + i++ + } + + err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png") + logErrorDebug(err, "botGraphShips : plot.Save") + if err != nil { + bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err)) + return + } + + bot.SendChatImage(m.Chat.ID, "/app/data/points.png") + + return +} diff --git a/config.go b/config.go index a05f09d..6ad8f8f 100644 --- a/config.go +++ b/config.go @@ -3,6 +3,7 @@ package main import ( _ "embed" "encoding/json" + "image/color" "io/ioutil" "time" @@ -128,3 +129,46 @@ func (c *Config) GetCompanyClient(id uint8) *ClientConfig { } return nil } + +func (c *Config) GetClientColor(id int) color.Color { + cc, ok := c.Clients[id] + if !ok { + return color.RGBA{0, 0, 0, 255} // Black + } + switch cc.Color { + case "Dark Blue": + return color.RGBA{28, 68, 140, 255} + case "Pale Green": + return color.RGBA{76, 116, 88, 255} + case "Pink": + return color.RGBA{188, 84, 108, 255} + case "Yellow": + return color.RGBA{212, 156, 32, 255} + case "Red": + return color.RGBA{196, 0, 0, 255} + case "Light Blue": + return color.RGBA{52, 112, 132, 255} + case "Green": + return color.RGBA{84, 132, 20, 255} + case "Dark Green": + return color.RGBA{80, 104, 60, 255} + case "Blue": + return color.RGBA{24, 120, 220, 255} + case "Cream": + return color.RGBA{184, 112, 80, 255} + case "Mauve": + return color.RGBA{80, 80, 116, 255} + case "Purple": + return color.RGBA{104, 76, 196, 255} + case "Orange": + return color.RGBA{252, 156, 0, 255} + case "Brown": + return color.RGBA{124, 104, 72, 255} + case "Gray": + return color.RGBA{116, 116, 116, 255} + case "White": + return color.RGBA{184, 184, 184, 255} + default: + return color.RGBA{0, 0, 0, 255} // Black + } +} diff --git a/config.sample.json b/config.sample.json index be3eef8..5b4bc51 100644 --- a/config.sample.json +++ b/config.sample.json @@ -15,5 +15,6 @@ "threshold":600000000000, "start_date":"2021-11-01T17:22:28+02:00" }, - "stats":null + "stats_monthly":null, + "stats_daily":null } \ No newline at end of file diff --git a/version.go b/version.go index 4cc430b..125ed79 100644 --- a/version.go +++ b/version.go @@ -1,6 +1,6 @@ // Code generated by version.sh (@generated) DO NOT EDIT. package main -var githash = "eec4d75" -var buildstamp = "2021-12-11_02:53:36" -var commits = "255" -var version = "eec4d75-b255 - 2021-12-11_02:53:36" +var githash = "bd6a542" +var buildstamp = "2021-12-11_03:50:14" +var commits = "256" +var version = "bd6a542-b256 - 2021-12-11_03:50:14"