Skip to content

Commit 6d2adbe

Browse files
committed
add multi-target Excel report
1 parent e6548f0 commit 6d2adbe

File tree

2 files changed

+150
-31
lines changed

2 files changed

+150
-31
lines changed

internal/common/common.go

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -307,36 +307,40 @@ func (rc *ReportingCommand) Run() error {
307307
}
308308
reportFilePaths = append(reportFilePaths, reportPath)
309309
}
310-
// keep all the targets table values for a combined HTML report
311-
if util.StringInList(report.FormatHtml, formats) {
312-
allTargetsTableValues = append(allTargetsTableValues, allTableValues)
313-
}
310+
// keep all the targets table values for combined reports
311+
allTargetsTableValues = append(allTargetsTableValues, allTableValues)
314312
}
315313
if len(allTargetsTableValues) > 1 {
316-
// list of target names for the combined HTML report
314+
// list of target names for the combined report
317315
// - only those that we received output from
318316
targetNames := make([]string, 0)
319317
for _, targetScriptOutputs := range orderedTargetScriptOutputs {
320318
targetNames = append(targetNames, targetScriptOutputs.targetName)
321319
}
322-
reportBytes, err := report.CreateMultiTarget(report.FormatHtml, allTargetsTableValues, targetNames)
323-
if err != nil {
324-
err = fmt.Errorf("failed to create multi-target report: %w", err)
325-
fmt.Fprintf(os.Stderr, "Error: %+v\n", err)
326-
slog.Error(err.Error())
327-
rc.Cmd.SilenceUsage = true
328-
return err
329-
}
330-
reportFilename := fmt.Sprintf("%s.%s", "all_hosts", report.FormatHtml)
331-
reportPath := filepath.Join(appContext.OutputDir, reportFilename)
332-
if err = report.WriteReport(reportBytes, reportPath); err != nil {
333-
err = fmt.Errorf("failed to write multi-target report: %w", err)
334-
fmt.Fprintf(os.Stderr, "Error: %+v\n", err)
335-
slog.Error(err.Error())
336-
rc.Cmd.SilenceUsage = true
337-
return err
320+
multiTargetFormats := []string{report.FormatHtml, report.FormatXlsx}
321+
for _, format := range multiTargetFormats {
322+
if !util.StringInList(format, formats) {
323+
continue
324+
}
325+
reportBytes, err := report.CreateMultiTarget(format, allTargetsTableValues, targetNames)
326+
if err != nil {
327+
err = fmt.Errorf("failed to create multi-target %s report: %w", format, err)
328+
fmt.Fprintf(os.Stderr, "Error: %+v\n", err)
329+
slog.Error(err.Error())
330+
rc.Cmd.SilenceUsage = true
331+
return err
332+
}
333+
reportFilename := fmt.Sprintf("%s.%s", "all_hosts", format)
334+
reportPath := filepath.Join(appContext.OutputDir, reportFilename)
335+
if err = report.WriteReport(reportBytes, reportPath); err != nil {
336+
err = fmt.Errorf("failed to write multi-target %s report: %w", format, err)
337+
fmt.Fprintf(os.Stderr, "Error: %+v\n", err)
338+
slog.Error(err.Error())
339+
rc.Cmd.SilenceUsage = true
340+
return err
341+
}
342+
reportFilePaths = append(reportFilePaths, reportPath)
338343
}
339-
reportFilePaths = append(reportFilePaths, reportPath)
340344
}
341345
if len(reportFilePaths) > 0 {
342346
fmt.Println("Report files:")

internal/report/report.go

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,12 @@ func Create(format string, allTableValues []TableValues, scriptOutputs map[strin
8787

8888
func CreateMultiTarget(format string, allTargetsTableValues [][]TableValues, targetNames []string) (out []byte, err error) {
8989
switch format {
90-
// case "txt":
91-
// return createTextReportMultiTarget(allTargetsTableValues)
92-
// case "json":
93-
// return createJsonReportMultiTarget(allTargetsTableValues)
9490
case "html":
9591
return createHtmlReportMultiTarget(allTargetsTableValues, targetNames)
96-
// case "xlsx":
97-
// return createXlsxReportMultiTarget(allTargetsTableValues)
92+
case "xlsx":
93+
return createXlsxReportMultiTarget(allTargetsTableValues, targetNames)
9894
}
99-
panic("only HTML multi-target report supported currently")
100-
//panic(fmt.Sprintf("expected one of %s, got %s", strings.Join(FormatOptions, ", "), format))
95+
panic("only HTML and XLSX multi-target report supported currently")
10196
}
10297

10398
func createTextReport(allTableValues []TableValues) (out []byte, err error) {
@@ -255,6 +250,95 @@ func renderXlsxTable(tableValues TableValues, f *excelize.File, sheetName string
255250
*row++
256251
}
257252

253+
func renderXlsxTableMultiTarget(tableIdx int, allTargetsTableValues [][]TableValues, targetNames []string, f *excelize.File, sheetName string, row *int) {
254+
col := 1
255+
// print the table name
256+
tableNameStyle, _ := f.NewStyle(&excelize.Style{
257+
Font: &excelize.Font{
258+
Bold: true,
259+
},
260+
})
261+
targetNameStyle, _ := f.NewStyle(&excelize.Style{
262+
Font: &excelize.Font{
263+
Bold: true,
264+
},
265+
})
266+
fieldNameStyle, _ := f.NewStyle(&excelize.Style{
267+
Font: &excelize.Font{
268+
Bold: true,
269+
},
270+
})
271+
272+
f.SetCellValue(sheetName, cellName(col, *row), allTargetsTableValues[0][tableIdx].Name)
273+
f.SetCellStyle(sheetName, cellName(col, *row), cellName(col, *row), tableNameStyle)
274+
275+
if !allTargetsTableValues[0][tableIdx].HasRows {
276+
col += 2
277+
// print the target names
278+
for _, targetName := range targetNames {
279+
f.SetCellValue(sheetName, cellName(col, *row), targetName)
280+
f.SetCellStyle(sheetName, cellName(col, *row), cellName(col, *row), targetNameStyle)
281+
col++
282+
}
283+
*row++
284+
285+
// print the field names and values from each target
286+
for fieldIdx, field := range allTargetsTableValues[0][tableIdx].Fields {
287+
col = 2
288+
f.SetCellValue(sheetName, cellName(col, *row), field.Name)
289+
f.SetCellStyle(sheetName, cellName(col, *row), cellName(col, *row), fieldNameStyle)
290+
col++
291+
for targetIdx := 0; targetIdx < len(targetNames); targetIdx++ {
292+
var fieldValue string
293+
if len(allTargetsTableValues[targetIdx][tableIdx].Fields[fieldIdx].Values) > 0 {
294+
fieldValue = allTargetsTableValues[targetIdx][tableIdx].Fields[fieldIdx].Values[0]
295+
}
296+
f.SetCellValue(sheetName, cellName(col, *row), fieldValue)
297+
col++
298+
}
299+
*row++
300+
}
301+
} else {
302+
for targetIdx, targetName := range targetNames {
303+
// print the target name
304+
col = 2
305+
f.SetCellValue(sheetName, cellName(col, *row), targetName)
306+
f.SetCellStyle(sheetName, cellName(col, *row), cellName(col, *row), targetNameStyle)
307+
*row++
308+
309+
// if no data found, print a message and skip to the next target
310+
if len(allTargetsTableValues[targetIdx][tableIdx].Fields) == 0 || len(allTargetsTableValues[targetIdx][tableIdx].Fields[0].Values) == 0 {
311+
f.SetCellValue(sheetName, cellName(col, *row), noDataFound)
312+
*row += 2
313+
continue
314+
}
315+
316+
// print the field names as column headings across the top of the table
317+
col = 2
318+
for _, field := range allTargetsTableValues[targetIdx][tableIdx].Fields {
319+
f.SetCellValue(sheetName, cellName(col, *row), field.Name)
320+
f.SetCellStyle(sheetName, cellName(col, *row), cellName(col, *row), fieldNameStyle)
321+
col++
322+
}
323+
*row++
324+
// print the rows of values
325+
tableRows := len(allTargetsTableValues[targetIdx][tableIdx].Fields[0].Values)
326+
for tableRow := 0; tableRow < tableRows; tableRow++ {
327+
col = 2
328+
for _, field := range allTargetsTableValues[targetIdx][tableIdx].Fields {
329+
value := getValueForCell(field.Values[tableRow])
330+
f.SetCellValue(sheetName, cellName(col, *row), value)
331+
col++
332+
}
333+
*row++
334+
}
335+
*row++
336+
}
337+
}
338+
*row++
339+
340+
}
341+
258342
func DefaultXlsxTableRendererFunc(tableValues TableValues, f *excelize.File, sheetName string, row *int) {
259343
headerStyle, _ := f.NewStyle(&excelize.Style{
260344
Font: &excelize.Font{
@@ -309,6 +393,7 @@ func DefaultXlsxTableRendererFunc(tableValues TableValues, f *excelize.File, she
309393

310394
const (
311395
XlsxPrimarySheetName = "Report"
396+
XlsxBriefSheetName = "Brief"
312397
)
313398

314399
func createXlsxReport(allTableValues []TableValues) (out []byte, err error) {
@@ -321,7 +406,7 @@ func createXlsxReport(allTableValues []TableValues) (out []byte, err error) {
321406
for _, tableValues := range allTableValues {
322407
if tableValues.Name == SystemSummaryTableName {
323408
row := 1
324-
sheetName := SystemSummaryTableName
409+
sheetName := XlsxBriefSheetName
325410
f.NewSheet(sheetName)
326411
f.SetColWidth(sheetName, "A", "L", 25)
327412
renderXlsxTable(tableValues, f, sheetName, &row)
@@ -340,6 +425,36 @@ func createXlsxReport(allTableValues []TableValues) (out []byte, err error) {
340425
return
341426
}
342427

428+
func createXlsxReportMultiTarget(allTargetsTableValues [][]TableValues, targetNames []string) (out []byte, err error) {
429+
f := excelize.NewFile()
430+
sheetName := XlsxPrimarySheetName
431+
f.SetSheetName("Sheet1", sheetName)
432+
f.SetColWidth(sheetName, "A", "A", 15)
433+
f.SetColWidth(sheetName, "B", "L", 25)
434+
row := 1
435+
for tableIdx, tableValues := range allTargetsTableValues[0] {
436+
if tableValues.Name == SystemSummaryTableName {
437+
row := 1
438+
sheetName := XlsxBriefSheetName
439+
f.NewSheet(sheetName)
440+
f.SetColWidth(sheetName, "A", "A", 15)
441+
f.SetColWidth(sheetName, "B", "L", 25)
442+
renderXlsxTableMultiTarget(tableIdx, allTargetsTableValues, targetNames, f, sheetName, &row)
443+
} else {
444+
renderXlsxTableMultiTarget(tableIdx, allTargetsTableValues, targetNames, f, sheetName, &row)
445+
}
446+
}
447+
var buf bytes.Buffer
448+
w := bufio.NewWriter(&buf)
449+
_, err = f.WriteTo(w)
450+
if err != nil {
451+
err = fmt.Errorf("failed to write multi-target xlsx report to buffer: %v", err)
452+
return
453+
}
454+
out = buf.Bytes()
455+
return
456+
}
457+
343458
func getValueForCell(value string) (val interface{}) {
344459
intValue, err := strconv.Atoi(value)
345460
if err == nil {

0 commit comments

Comments
 (0)