package reports import ( "encoding/json" "fmt" "io" "strings" "time" ) // WriteJSON schreibt einen Bericht als JSON. // // Das Modell wird unveraendert ausgegeben. Eine eigene Darstellung fuer die // Ausgabe waere ein zweiter Ort, an dem eine Kennzahl ihr Feld „gemessen" // verlieren koennte. func WriteJSON(outputWriter io.Writer, report *Report) error { jsonEncoder := json.NewEncoder(outputWriter) jsonEncoder.SetIndent("", " ") return jsonEncoder.Encode(report) } // Write gibt einen Bericht im gewaehlten Format aus. func Write(outputWriter io.Writer, report *Report, outputFormat Format) error { switch outputFormat { case FormatCSV: return WriteCSV(outputWriter, report) case FormatPDF: return WritePDF(outputWriter, report) case FormatJSON: return WriteJSON(outputWriter, report) default: return fmt.Errorf("das format %q ist unbekannt", outputFormat) } } // FileName bildet den Dateinamen eines Berichts. // // Enthalten sind Art und Erzeugungszeitpunkt. Ohne den Zeitpunkt liegen nach // drei Abrufen drei Dateien „bericht.pdf (2)" im Download-Ordner, und keine // laesst sich mehr zuordnen. func FileName(report *Report, outputFormat Format) string { return fmt.Sprintf("syncova-%s-%s.%s", report.Type, report.GeneratedAt.UTC().Format("20060102-150405"), outputFormat.FileExtension()) } // SanitizeFileName entfernt Zeichen, die in einem Dateinamen stoeren. // // Der Name entsteht zwar aus festen Bestandteilen, geht aber in einen // Content-Disposition-Kopf ein. Ein Anfuehrungszeichen oder ein Zeilenumbruch // darin liesse sich zum Einschleusen weiterer Kopffelder nutzen. func SanitizeFileName(fileName string) string { var sanitizedName strings.Builder for _, nameRune := range fileName { switch { case nameRune >= 'a' && nameRune <= 'z', nameRune >= 'A' && nameRune <= 'Z', nameRune >= '0' && nameRune <= '9', nameRune == '-', nameRune == '_', nameRune == '.': sanitizedName.WriteRune(nameRune) default: sanitizedName.WriteRune('-') } } return sanitizedName.String() } // newReport legt einen Bericht mit den Kopfangaben einer Definition an. func newReport(definition Definition, periodFrom, periodTo time.Time, generatedBy string) *Report { return &Report{ Type: definition.Type, Title: definition.Title, Description: definition.Description, PeriodFrom: periodFrom, PeriodTo: periodTo, GeneratedAt: time.Now().UTC(), GeneratedBy: generatedBy, Sections: make([]Section, 0, 4), } }