diff --git a/main.go b/main.go index 12544b2..8b718ae 100644 --- a/main.go +++ b/main.go @@ -36,10 +36,35 @@ type Record struct { Wsdc json.RawMessage `json:"Western Sydney Data Centre"` } +type KeyValue struct { + Key string + Value interface{} +} + +var sha1ver string // sha1 revision used to build the program +var buildTime string // when the executable was built + +// Add a new key-value pair to the slice +func addData(data []KeyValue, key string, value interface{}) []KeyValue { + return append(data, KeyValue{Key: key, Value: value}) +} + +// Find a value by key +func findData(data []KeyValue, key string) (interface{}, bool) { + for _, kv := range data { + if kv.Key == key { + return kv.Value, true + } + } + return nil, false // Return false if key not found +} + func GenerateAvgVcpusCharts(f *excelize.File, data any, name string) { var err error - rydeAvgCpu := make(map[string]interface{}) - wsdcAvgCpu := make(map[string]interface{}) + //rydeAvgCpu := make(map[string]interface{}) + rydeAvgCpu := []KeyValue{} + //wsdcAvgCpu := make(map[string]interface{}) + wsdcAvgCpu := []KeyValue{} var avgCpuColumns []string // Access AvgCpu using reflection @@ -75,8 +100,11 @@ func GenerateAvgVcpusCharts(f *excelize.File, data any, name string) { } } - rydeAvgCpu[fieldName] = rydeData - wsdcAvgCpu[fieldName] = wsdcData + rydeAvgCpu = addData(rydeAvgCpu, fieldName, rydeData) + wsdcAvgCpu = addData(wsdcAvgCpu, fieldName, wsdcData) + + //rydeAvgCpu[fieldName] = rydeData + //wsdcAvgCpu[fieldName] = wsdcData avgCpuColumns = append(avgCpuColumns, fieldName) } @@ -88,7 +116,8 @@ func GenerateAvgVcpusCharts(f *excelize.File, data any, name string) { func GenerateCharts(f *excelize.File, data any, name string, location string) { var err error - parsedData := make(map[string]interface{}) + //parsedData := make(map[string]interface{}) + parsedData := []KeyValue{} var dataColumns = []string{"RCC", "WSDC"} // Create interfaces to hold unmarshaled data @@ -117,21 +146,24 @@ func GenerateCharts(f *excelize.File, data any, name string, location string) { } // store the data together - parsedData["RCC"] = rcc - parsedData["WSDC"] = wsdc + parsedData = addData(parsedData, "RCC", rcc) + parsedData = addData(parsedData, "WSDC", wsdc) + //parsedData["RCC"] = rcc + //parsedData["WSDC"] = wsdc //prettyPrint(parsedData) // Generate worksheet and graph AvgChart(f, name, location, dataColumns, parsedData) } -func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuColumns []string, data map[string]interface{}) { +func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuColumns []string, data []KeyValue) { var err error var chartSeries []excelize.ChartSeries var dataDates []string var col int var row int + fmt.Printf("Creating worksheet %s\n", worksheetName) _, err = f.NewSheet(worksheetName) if err != nil { log.Fatal(err) @@ -144,12 +176,14 @@ func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuCol f.SetCellValue(worksheetName, cell, avgCpuColumns[i]) } - // Get the values for the dates column + // Get the values for the dates column using the first object in data for _, v := range data { - for date := range v.(map[string]interface{}) { - dataDates = append(dataDates, date) + if dateMap, ok := v.Value.(map[string]interface{}); ok { + for date := range dateMap { + dataDates = append(dataDates, date) + } } - break + break // Assuming you only need the dates from the first entry } // Sort dates using the custom function @@ -165,22 +199,27 @@ func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuCol f.SetCellValue(worksheetName, cell, dataDates[i]) } - for pool, v := range data { // iterate each resource pool type + // Iterate over each KeyValue in the data slice (resource pool types) + for _, poolKv := range data { + pool := poolKv.Key // The pool name + // Find the column that matches, add one to account for the date column col = slices.Index(avgCpuColumns, pool) + 1 - //fmt.Printf("Pool: %s, column: %d\n", pool, col) + fmt.Printf("Pool: %s, column: %d\n", pool, col) - for date, val := range v.(map[string]interface{}) { // use type assertion to loop over map[string]interface{} - //fmt.Printf("val: %v\n", val) + // Type assertion to confirm that poolKv.Value is a map[string]interface{} + if dateMap, ok := poolKv.Value.(map[string]interface{}); ok { + for date, val := range dateMap { + fmt.Printf("Date: %s, Value: %v\n", date, val) - // Find the correct row, add one to account for sheet heading - row = slices.Index(dataDates, date) + 1 + // Find the correct row, add one to account for sheet heading + row = slices.Index(dataDates, date) + 1 - cell := string(rune('A'+col)) + strconv.Itoa(row+1) - //fmt.Printf("Adding value %f (%s) to %s]\n", val, date, cell) - f.SetCellValue(worksheetName, cell, val) + cell := string(rune('A'+col)) + strconv.Itoa(row+1) + //fmt.Printf("Adding value %f (%s) to %s]\n", val, date, cell) + f.SetCellValue(worksheetName, cell, val) + } } - // Create the chartseries for this resource pool thisChartSeries := excelize.ChartSeries{ Name: "'" + worksheetName + "'!$" + string(rune('A'+col)) + "$1", // Reference the cell containing the resource pool name, eg Tin in $B$1 @@ -220,6 +259,11 @@ func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuCol XAxis: excelize.ChartAxis{ MajorGridLines: true, MinorGridLines: true, + Title: []excelize.RichTextRun{ + { + Text: "Month Year", + }, + }, Font: excelize.Font{ Color: "000000", }, @@ -227,6 +271,11 @@ func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuCol YAxis: excelize.ChartAxis{ MajorGridLines: true, MinorGridLines: true, + Title: []excelize.RichTextRun{ + { + Text: "Count/Size", + }, + }, Font: excelize.Font{ Color: "000000", }, @@ -248,6 +297,9 @@ func main() { outputFile := flag.String("output", "book1.xlsx", "The filename to use when writing excel workbook") flag.Parse() + fmt.Println("Excel chart generation utility, written by Nathan Coad (nathan.coad@dell.com)") + fmt.Printf("Built on %s from sha1 %s\n", buildTime, sha1ver) + // Create the workbook f := excelize.NewFile() defer func() {