seekia/internal/createCharts/createCharts.go

193 lines
4.6 KiB
Go

// charts provides functions to create charts
// These are used to show statistics about users
// See userStatistics.go to see how input statistics are created
package createCharts
import "seekia/internal/statisticsDatum"
import goChart "github.com/wcharczuk/go-chart/v2"
import "github.com/wcharczuk/go-chart/v2/drawing"
import "image"
import "errors"
// Inputs:
// -string: Chart title
// -[]statisticsDatum.StatisticsDatum: Statistics datums list
// -func(float64)(string, error): formatYAxisValuesFunction
// -This will take values such as 1000000 and turn them to "1 million"
// -bool: Y-axis units exist
// -string: Y-axis units (example: " Users")
// Outputs:
// -image.Image
// -error
func CreateBarChart(chartTitle string, chartStatisticsDatumsList []statisticsDatum.StatisticsDatum, formatYAxisValuesFunction func(float64)(string, error), yAxisUnitsProvided bool, yAxisUnits string)(image.Image, error){
if (len(chartStatisticsDatumsList) == 0) {
return nil, errors.New("CreateBarChart called with empty chartStatisticsDatumsList")
}
chartDatumsList := make([]goChart.Value, 0, len(chartStatisticsDatumsList))
for _, statisticsDatum := range chartStatisticsDatumsList{
datumLabel := statisticsDatum.LabelFormatted
datumValue := statisticsDatum.Value
// We make sure this function does not error
_, err := formatYAxisValuesFunction(datumValue)
if (err != nil){
return nil, errors.New("Invalid chartStatisticsDatumsList: Datum value is invalid. Reason: " + err.Error())
}
newChartValue := goChart.Value{
Label: datumLabel,
Value: datumValue,
}
chartDatumsList = append(chartDatumsList, newChartValue)
}
if (len(chartDatumsList) == 1){
// This package cannot create bar charts with only 1 datum
// Thus, we must add an empty datum
newChartValue := goChart.Value{
Style: goChart.Style{
StrokeColor: drawing.ColorWhite,
FillColor: drawing.ColorWhite,
},
Label: "",
Value: .001,
}
chartDatumsList = append(chartDatumsList, newChartValue)
}
chartStyleObject := goChart.Style{
Padding: goChart.Box{
Top: 40,
Bottom: 0,
Left: 0,
Right: 0,
},
}
titleStyleObject := goChart.Style{
Padding: goChart.Box{
Top: 10,
Bottom: 30,
},
FontSize: 15,
FontColor: drawing.ColorBlack,
}
yAxisObject := goChart.YAxis{
ValueFormatter: func(v interface{}) string {
valueFloat64 := v.(float64)
valueFormatted, err := formatYAxisValuesFunction(valueFloat64)
if (err != nil){
return "ERROR"
}
if (yAxisUnitsProvided == false){
return valueFormatted
}
result := valueFormatted + yAxisUnits
return result
},
}
barChartObject := goChart.BarChart{
Title: chartTitle,
TitleStyle: titleStyleObject,
Background: chartStyleObject,
Height: 500,
BarWidth: 60,
Bars: chartDatumsList,
YAxis: yAxisObject,
}
collector := &goChart.ImageWriter{}
barChartObject.Render(goChart.PNG, collector)
goImage, err := collector.Image()
if (err != nil) { return nil, err }
return goImage, nil
}
func CreateDonutChart(chartTitle string, chartStatisticsDatumsList []statisticsDatum.StatisticsDatum)(image.Image, error){
if (len(chartStatisticsDatumsList) == 0) {
return nil, errors.New("CreateDonutChart called with empty chartStatisticsDatumsList")
}
chartDatumsList := make([]goChart.Value, 0, len(chartStatisticsDatumsList))
for _, statisticsDatum := range chartStatisticsDatumsList{
datumLabel := statisticsDatum.LabelFormatted
// Value is always a number representing the percentage of the donut
datumValue := statisticsDatum.Value
newChartValue := goChart.Value{
Label: datumLabel,
Value: datumValue,
}
chartDatumsList = append(chartDatumsList, newChartValue)
}
chartStyleObject := goChart.Style{
Padding: goChart.Box{
Top: 20,
Bottom: 0,
Left: 0,
Right: 0,
},
}
titleStyleObject := goChart.Style{
FontSize: 15,
FontColor: drawing.ColorBlack,
}
donutChartObject := goChart.DonutChart{
Title: chartTitle,
TitleStyle: titleStyleObject,
Background: chartStyleObject,
Height: 500,
Values: chartDatumsList,
}
if (len(chartDatumsList) == 1){
// Default is transparent, we need to add color
sliceStyleObject := goChart.Style{
FillColor: drawing.ColorRed,
}
donutChartObject.SliceStyle = sliceStyleObject
}
collector := &goChart.ImageWriter{}
donutChartObject.Render(goChart.PNG, collector)
goImage, err := collector.Image()
if (err != nil) { return nil, err }
return goImage, nil
}