refactor(go): split training/store, ucca/rules, ucca_handlers, document_export under 500 LOC
Each of the four oversized files (training/store.go 1569 LOC, ucca/rules.go 1231 LOC, ucca_handlers.go 1135 LOC, document_export.go 1101 LOC) is split by logical group into same-package files, all under the 500-line hard cap. Zero behavior changes, no renamed exported symbols. Also fixed pre-existing hazard_library split (missing functions and duplicate UUID keys from a prior session). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
261
ai-compliance-sdk/internal/iace/document_export_excel.go
Normal file
261
ai-compliance-sdk/internal/iace/document_export_excel.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package iace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
// ExportExcel generates an XLSX workbook with project data across multiple sheets
|
||||
func (e *DocumentExporter) ExportExcel(
|
||||
project *Project,
|
||||
sections []TechFileSection,
|
||||
hazards []Hazard,
|
||||
assessments []RiskAssessment,
|
||||
mitigations []Mitigation,
|
||||
) ([]byte, error) {
|
||||
if project == nil {
|
||||
return nil, fmt.Errorf("project must not be nil")
|
||||
}
|
||||
|
||||
f := excelize.NewFile()
|
||||
defer f.Close()
|
||||
|
||||
overviewSheet := "Uebersicht"
|
||||
f.SetSheetName("Sheet1", overviewSheet)
|
||||
e.xlsxOverview(f, overviewSheet, project)
|
||||
|
||||
hazardSheet := "Gefaehrdungsprotokoll"
|
||||
f.NewSheet(hazardSheet)
|
||||
e.xlsxHazardLog(f, hazardSheet, hazards, assessments)
|
||||
|
||||
mitigationSheet := "Massnahmen"
|
||||
f.NewSheet(mitigationSheet)
|
||||
e.xlsxMitigations(f, mitigationSheet, mitigations)
|
||||
|
||||
matrixSheet := "Risikomatrix"
|
||||
f.NewSheet(matrixSheet)
|
||||
e.xlsxRiskMatrix(f, matrixSheet, assessments)
|
||||
|
||||
sectionSheet := "Sektionen"
|
||||
f.NewSheet(sectionSheet)
|
||||
e.xlsxSections(f, sectionSheet, sections)
|
||||
|
||||
buf, err := f.WriteToBuffer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write Excel: %w", err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (e *DocumentExporter) xlsxOverview(f *excelize.File, sheet string, project *Project) {
|
||||
headerStyle, _ := f.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{Bold: true, Size: 11},
|
||||
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"D9E1F2"}},
|
||||
})
|
||||
|
||||
f.SetColWidth(sheet, "A", "A", 30)
|
||||
f.SetColWidth(sheet, "B", "B", 50)
|
||||
|
||||
rows := [][]string{
|
||||
{"Eigenschaft", "Wert"},
|
||||
{"Maschinenname", project.MachineName},
|
||||
{"Maschinentyp", project.MachineType},
|
||||
{"Hersteller", project.Manufacturer},
|
||||
{"Beschreibung", project.Description},
|
||||
{"CE-Kennzeichnungsziel", project.CEMarkingTarget},
|
||||
{"Projektstatus", string(project.Status)},
|
||||
{"Vollstaendigkeits-Score", fmt.Sprintf("%.1f%%", project.CompletenessScore*100)},
|
||||
{"Erstellt am", project.CreatedAt.Format("02.01.2006 15:04")},
|
||||
{"Aktualisiert am", project.UpdatedAt.Format("02.01.2006 15:04")},
|
||||
}
|
||||
|
||||
for i, row := range rows {
|
||||
rowNum := i + 1
|
||||
f.SetCellValue(sheet, cellRef("A", rowNum), row[0])
|
||||
f.SetCellValue(sheet, cellRef("B", rowNum), row[1])
|
||||
if i == 0 {
|
||||
f.SetCellStyle(sheet, cellRef("A", rowNum), cellRef("B", rowNum), headerStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DocumentExporter) xlsxHazardLog(f *excelize.File, sheet string, hazards []Hazard, assessments []RiskAssessment) {
|
||||
headerStyle, _ := f.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"},
|
||||
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}},
|
||||
Alignment: &excelize.Alignment{Horizontal: "center"},
|
||||
})
|
||||
|
||||
headers := []string{"Nr", "Name", "Kategorie", "Beschreibung", "S", "E", "P", "A",
|
||||
"Inherent Risk", "C_eff", "Residual Risk", "Risk Level", "Akzeptabel"}
|
||||
|
||||
colWidths := map[string]float64{
|
||||
"A": 6, "B": 25, "C": 20, "D": 35, "E": 8, "F": 8, "G": 8, "H": 8,
|
||||
"I": 14, "J": 10, "K": 14, "L": 18, "M": 12,
|
||||
}
|
||||
for col, w := range colWidths {
|
||||
f.SetColWidth(sheet, col, col, w)
|
||||
}
|
||||
|
||||
cols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"}
|
||||
for i, h := range headers {
|
||||
f.SetCellValue(sheet, cellRef(cols[i], 1), h)
|
||||
}
|
||||
f.SetCellStyle(sheet, "A1", cellRef(cols[len(cols)-1], 1), headerStyle)
|
||||
|
||||
assessMap := buildAssessmentMap(assessments)
|
||||
|
||||
for i, hazard := range hazards {
|
||||
row := i + 2
|
||||
a := assessMap[hazard.ID.String()]
|
||||
|
||||
f.SetCellValue(sheet, cellRef("A", row), i+1)
|
||||
f.SetCellValue(sheet, cellRef("B", row), hazard.Name)
|
||||
f.SetCellValue(sheet, cellRef("C", row), hazard.Category)
|
||||
f.SetCellValue(sheet, cellRef("D", row), hazard.Description)
|
||||
|
||||
if a != nil {
|
||||
f.SetCellValue(sheet, cellRef("E", row), a.Severity)
|
||||
f.SetCellValue(sheet, cellRef("F", row), a.Exposure)
|
||||
f.SetCellValue(sheet, cellRef("G", row), a.Probability)
|
||||
f.SetCellValue(sheet, cellRef("H", row), a.Avoidance)
|
||||
f.SetCellValue(sheet, cellRef("I", row), fmt.Sprintf("%.1f", a.InherentRisk))
|
||||
f.SetCellValue(sheet, cellRef("J", row), fmt.Sprintf("%.2f", a.CEff))
|
||||
f.SetCellValue(sheet, cellRef("K", row), fmt.Sprintf("%.1f", a.ResidualRisk))
|
||||
f.SetCellValue(sheet, cellRef("L", row), riskLevelLabel(a.RiskLevel))
|
||||
|
||||
acceptStr := "Nein"
|
||||
if a.IsAcceptable {
|
||||
acceptStr = "Ja"
|
||||
}
|
||||
f.SetCellValue(sheet, cellRef("M", row), acceptStr)
|
||||
|
||||
r, g, b := riskLevelColor(a.RiskLevel)
|
||||
style, _ := f.NewStyle(&excelize.Style{
|
||||
Fill: excelize.Fill{
|
||||
Type: "pattern",
|
||||
Pattern: 1,
|
||||
Color: []string{rgbHex(r, g, b)},
|
||||
},
|
||||
Alignment: &excelize.Alignment{Horizontal: "center"},
|
||||
})
|
||||
f.SetCellStyle(sheet, cellRef("L", row), cellRef("L", row), style)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DocumentExporter) xlsxMitigations(f *excelize.File, sheet string, mitigations []Mitigation) {
|
||||
headerStyle, _ := f.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"},
|
||||
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}},
|
||||
Alignment: &excelize.Alignment{Horizontal: "center"},
|
||||
})
|
||||
|
||||
headers := []string{"Nr", "Name", "Typ", "Beschreibung", "Status", "Verifikationsmethode", "Ergebnis"}
|
||||
cols := []string{"A", "B", "C", "D", "E", "F", "G"}
|
||||
|
||||
f.SetColWidth(sheet, "A", "A", 6)
|
||||
f.SetColWidth(sheet, "B", "B", 25)
|
||||
f.SetColWidth(sheet, "C", "C", 15)
|
||||
f.SetColWidth(sheet, "D", "D", 35)
|
||||
f.SetColWidth(sheet, "E", "E", 15)
|
||||
f.SetColWidth(sheet, "F", "F", 22)
|
||||
f.SetColWidth(sheet, "G", "G", 25)
|
||||
|
||||
for i, h := range headers {
|
||||
f.SetCellValue(sheet, cellRef(cols[i], 1), h)
|
||||
}
|
||||
f.SetCellStyle(sheet, "A1", cellRef(cols[len(cols)-1], 1), headerStyle)
|
||||
|
||||
for i, m := range mitigations {
|
||||
row := i + 2
|
||||
f.SetCellValue(sheet, cellRef("A", row), i+1)
|
||||
f.SetCellValue(sheet, cellRef("B", row), m.Name)
|
||||
f.SetCellValue(sheet, cellRef("C", row), reductionTypeLabel(m.ReductionType))
|
||||
f.SetCellValue(sheet, cellRef("D", row), m.Description)
|
||||
f.SetCellValue(sheet, cellRef("E", row), mitigationStatusLabel(m.Status))
|
||||
f.SetCellValue(sheet, cellRef("F", row), string(m.VerificationMethod))
|
||||
f.SetCellValue(sheet, cellRef("G", row), m.VerificationResult)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DocumentExporter) xlsxRiskMatrix(f *excelize.File, sheet string, assessments []RiskAssessment) {
|
||||
headerStyle, _ := f.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"},
|
||||
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}},
|
||||
Alignment: &excelize.Alignment{Horizontal: "center"},
|
||||
})
|
||||
|
||||
f.SetColWidth(sheet, "A", "A", 25)
|
||||
f.SetColWidth(sheet, "B", "B", 12)
|
||||
|
||||
f.SetCellValue(sheet, "A1", "Risikostufe")
|
||||
f.SetCellValue(sheet, "B1", "Anzahl")
|
||||
f.SetCellStyle(sheet, "A1", "B1", headerStyle)
|
||||
|
||||
counts := countByRiskLevel(assessments)
|
||||
|
||||
levels := []RiskLevel{
|
||||
RiskLevelNotAcceptable,
|
||||
RiskLevelVeryHigh,
|
||||
RiskLevelCritical,
|
||||
RiskLevelHigh,
|
||||
RiskLevelMedium,
|
||||
RiskLevelLow,
|
||||
RiskLevelNegligible,
|
||||
}
|
||||
|
||||
row := 2
|
||||
for _, level := range levels {
|
||||
count := counts[level]
|
||||
f.SetCellValue(sheet, cellRef("A", row), riskLevelLabel(level))
|
||||
f.SetCellValue(sheet, cellRef("B", row), count)
|
||||
|
||||
r, g, b := riskLevelColor(level)
|
||||
style, _ := f.NewStyle(&excelize.Style{
|
||||
Fill: excelize.Fill{
|
||||
Type: "pattern",
|
||||
Pattern: 1,
|
||||
Color: []string{rgbHex(r, g, b)},
|
||||
},
|
||||
})
|
||||
f.SetCellStyle(sheet, cellRef("A", row), cellRef("B", row), style)
|
||||
row++
|
||||
}
|
||||
|
||||
totalStyle, _ := f.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{Bold: true},
|
||||
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"D9E1F2"}},
|
||||
})
|
||||
f.SetCellValue(sheet, cellRef("A", row), "Gesamt")
|
||||
f.SetCellValue(sheet, cellRef("B", row), len(assessments))
|
||||
f.SetCellStyle(sheet, cellRef("A", row), cellRef("B", row), totalStyle)
|
||||
}
|
||||
|
||||
func (e *DocumentExporter) xlsxSections(f *excelize.File, sheet string, sections []TechFileSection) {
|
||||
headerStyle, _ := f.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"},
|
||||
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}},
|
||||
Alignment: &excelize.Alignment{Horizontal: "center"},
|
||||
})
|
||||
|
||||
f.SetColWidth(sheet, "A", "A", 25)
|
||||
f.SetColWidth(sheet, "B", "B", 40)
|
||||
f.SetColWidth(sheet, "C", "C", 15)
|
||||
|
||||
headers := []string{"Sektion", "Titel", "Status"}
|
||||
cols := []string{"A", "B", "C"}
|
||||
|
||||
for i, h := range headers {
|
||||
f.SetCellValue(sheet, cellRef(cols[i], 1), h)
|
||||
}
|
||||
f.SetCellStyle(sheet, "A1", cellRef(cols[len(cols)-1], 1), headerStyle)
|
||||
|
||||
for i, s := range sections {
|
||||
row := i + 2
|
||||
f.SetCellValue(sheet, cellRef("A", row), s.SectionType)
|
||||
f.SetCellValue(sheet, cellRef("B", row), s.Title)
|
||||
f.SetCellValue(sheet, cellRef("C", row), string(s.Status))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user