Skip to content

Commit e2fc0e9

Browse files
committed
refactor: updated attendance_report.jasper and .jrxml
1 parent 3214cf9 commit e2fc0e9

File tree

7 files changed

+976
-79
lines changed

7 files changed

+976
-79
lines changed

backend/src/jasper/jasper_service.go

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,35 @@ func GenerateUsageReportPDF(db *database.DB, queryCtx *models.QueryContext, hasP
184184
return service.generateUsageReportPDF(userID)
185185
}
186186

187+
// recompileTemplate compiles a .jrxml file using JasperStarter
188+
// This is called when a .jasper file is missing or corrupt (e.g., Studio-compiled with UUID suffix)
189+
func recompileTemplate(templateDir, baseTemplateName string) error {
190+
jrxmlPath := filepath.Join(templateDir, baseTemplateName+".jrxml")
191+
192+
logrus.WithFields(logrus.Fields{
193+
"template": baseTemplateName,
194+
}).Info("Recompiling template from .jrxml source with JasperStarter")
195+
196+
cmd := exec.Command("/opt/jasperstarter/bin/jasperstarter", "compile", jrxmlPath)
197+
cmd.Dir = templateDir
198+
output, err := cmd.CombinedOutput()
199+
if err != nil {
200+
logrus.WithFields(logrus.Fields{
201+
"template": baseTemplateName,
202+
"error": err,
203+
"output": string(output),
204+
}).Error("Failed to recompile template")
205+
return fmt.Errorf("failed to recompile template %s: %w", baseTemplateName, err)
206+
}
207+
208+
logrus.WithFields(logrus.Fields{
209+
"template": baseTemplateName,
210+
"output": string(output),
211+
}).Info("Successfully recompiled template from .jrxml source")
212+
213+
return nil
214+
}
215+
187216
func generateReportPDF(config models.PDFConfig, filterSummary []models.PDFFilterLine, templateName string) ([]byte, error) {
188217
type ReportData struct {
189218
Rows [][]string `json:"rows"`
@@ -203,6 +232,17 @@ func generateReportPDF(config models.PDFConfig, filterSummary []models.PDFFilter
203232
return nil, fmt.Errorf("failed to marshal report data: %w", err)
204233
}
205234

235+
// IMPORTANT: The go-jasper library wraps string parameter values in quotes for command-line safety.
236+
// See: https://github.com/evertonvps/go-jasper/blob/main/go-jasper.go#L88
237+
//
238+
// This causes JasperReports to receive parameter values like "\"Attendance\"" instead of "Attendance".
239+
// We CANNOT strip quotes here because the backend values don't have quotes yet!
240+
// The quotes are added by go-jasper when generating command-line args.
241+
//
242+
// Solution: JRXML templates must use .replaceAll("\"", "") to strip quotes at display time.
243+
// Future developers: ALL command-line parameters (ReportTitle, GeneratedDate, FilterSummary, etc.)
244+
// MUST include .replaceAll("\"", "") in their JRXML template fields.
245+
// Table data from JSON does NOT need quote stripping (not passed as command-line args).
206246
title := config.Title
207247
if title == "" {
208248
title = "Report"
@@ -273,16 +313,45 @@ func generateReportPDF(config models.PDFConfig, filterSummary []models.PDFFilter
273313
templateDir = "/templates"
274314
}
275315
compiledTemplatePath := filepath.Join(templateDir, templateName+".jasper")
316+
jrxmlTemplatePath := filepath.Join(templateDir, templateName+".jrxml")
276317

277318
pdfBytes, err := gj.Process(compiledTemplatePath)
278319
if err != nil {
279-
logrus.WithFields(logrus.Fields{
280-
"template_path": compiledTemplatePath,
281-
"template_dir": templateDir,
282-
"error": err,
283-
}).Error("Failed to process compiled template")
320+
// Check if this is a Jaspersoft Studio compilation error (NoClassDefFoundError with UUID suffix)
321+
// If so, recompile from .jrxml source and retry
322+
errStr := err.Error()
323+
if strings.Contains(errStr, "NoClassDefFoundError") && strings.Contains(errStr, "_") {
324+
logrus.WithFields(logrus.Fields{
325+
"template": templateName,
326+
"error": err,
327+
"jrxml_path": jrxmlTemplatePath,
328+
}).Warn("Detected Jaspersoft Studio-compiled .jasper file (UUID suffix), recompiling with JasperStarter")
329+
330+
if recompileErr := recompileTemplate(templateDir, templateName); recompileErr != nil {
331+
return nil, fmt.Errorf("failed to recover from Studio compilation: %w (original error: %w)", recompileErr, err)
332+
}
333+
334+
pdfBytes, err = gj.Process(compiledTemplatePath)
335+
if err != nil {
336+
logrus.WithFields(logrus.Fields{
337+
"template_path": compiledTemplatePath,
338+
"template_dir": templateDir,
339+
"error": err,
340+
}).Error("Failed to process compiled template after recompilation")
341+
return nil, fmt.Errorf("failed to process template after recompilation: %w", err)
342+
}
284343

285-
return nil, fmt.Errorf("failed to process template: %w", err)
344+
logrus.WithFields(logrus.Fields{
345+
"template": templateName,
346+
}).Info("Successfully recovered from Studio compilation and generated PDF")
347+
} else {
348+
logrus.WithFields(logrus.Fields{
349+
"template_path": compiledTemplatePath,
350+
"template_dir": templateDir,
351+
"error": err,
352+
}).Error("Failed to process compiled template")
353+
return nil, fmt.Errorf("failed to process template: %w", err)
354+
}
286355
}
287356

288357
return pdfBytes, nil
5.23 KB
Binary file not shown.

0 commit comments

Comments
 (0)