// 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 }