CbmRoot
Loading...
Searching...
No Matches
QaReportGenerator.cxx
Go to the documentation of this file.
1/* Copyright (C) 2025 GSI/VECC, Darmstadt/Kolkata
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Souvik Chattopadhyay[committer] */
4
5#include "QaReportGenerator.h"
6
8#include "CbmQaReportFigure.h"
11#include "CbmQaReportSection.h"
12#include "CbmQaReportTable.h"
13#include "Logger.h"
14
15#include <TCanvas.h>
16#include <TKey.h>
17#include <TString.h>
18#include <TSystem.h>
19
20#include <algorithm>
21#include <cstring>
22#include <iostream>
23
24#include <yaml-cpp/yaml.h>
25
27using namespace cbm::qa::report;
29
30
31// ---------------------------------------------------------------------
32QaReportGenerator::QaReportGenerator(const std::string& qaFile, const std::string& setup, const std::string& outDir,
33 EngineType engine, const std::string& figExt, bool ignoreSame)
34 : fSetup(setup)
35 , fOutDir(outDir)
36 , fEngine(engine)
37{
38 fFile.reset(TFile::Open(qaFile.c_str(), "READONLY"));
39 if (!fFile || !fFile->IsOpen()) {
40 throw std::runtime_error("Cannot open input file: " + qaFile);
41 }
42
43 fIgnoreSame = ignoreSame;
44 fReport = std::make_unique<Builder>("QA Compare Status", outDir.c_str());
45 fReport->GetHeader()->SetSubtitle("The CBM Collaboration");
46 fReport->GetHeader()->SetAuthor(gSystem->Getenv("USER"));
47 fReport->GetHeader()->SetSetup(setup.c_str());
48 fReport->SetFigureExtention(figExt.c_str());
49}
50
51// ---------------------------------------------------------------------
53{
54 std::string path = fFile->GetName();
55
56
57 Process(path);
58 switch (fEngine) {
59 case EngineType::kHtml: {
60 HtmlEngine e;
61 fReport->CreateScript(e);
62 break;
63 }
66 fReport->CreateScript(e);
67 break;
68 }
70 default: {
72 fReport->CreateScript(e);
73 break;
74 }
75 }
76}
77
78
79void QaReportGenerator::Process(const std::string& Path)
80{
81
82
83 LOG(info) << "Processing root file: " << Path;
84 std::unique_ptr<TFile> f(TFile::Open(Path.c_str(), "READ"));
85 if (!f || f->IsZombie()) {
86 std::cerr << "Cannot open file\n";
87 return;
88 }
89
90
91 TIter setups(f->GetListOfKeys());
92 while (auto* setupKey = static_cast<TKey*>(setups())) {
93 const std::string setupName = setupKey->GetName();
94 LOG(info) << "Processing setup: " << setupName;
95
96
97 auto* setupDir = dynamic_cast<TDirectory*>(f->GetDirectory(setupName.c_str()));
98 if (!setupDir) {
99 LOG(warn) << " Not a folder, skipping: " << setupName;
100 continue;
101 }
102
103
104 TIter fileLabels(setupDir->GetListOfKeys());
105 while (auto* fileKey = static_cast<TKey*>(fileLabels())) {
106 const std::string fileLabel = fileKey->GetName();
107 LOG(info) << " Found file label: " << fileLabel;
108
109 auto* fileDir = dynamic_cast<TDirectory*>(setupDir->GetDirectory(fileLabel.c_str()));
110 if (!fileDir) {
111 LOG(warn) << " Not a folder, skipping: " << fileLabel;
112 continue;
113 }
114
115
116 TIter tasks(fileDir->GetListOfKeys());
117 while (auto* taskKey = static_cast<TKey*>(tasks())) {
118 const std::string taskName = taskKey->GetName();
119 // Add section for task name
120 std::string sectionName = "Comparison Results for " + taskName;
121 auto section = std::make_shared<Section>(taskName, sectionName);
122 fReport->AddSection(section);
123
124 LOG(info) << " Found task: " << taskName << " in " << fileLabel;
125 auto* taskDir = dynamic_cast<TDirectory*>(fileDir->GetDirectory(taskName.c_str()));
126 std::string currentRPath = taskName;
127 // + "/" + taskName;
128 // std::string currentRPath = "";
129 GetResults(taskDir, currentRPath, taskName);
130
131
132 Size_t nCols = fVersionLabel.size() + 1; // +1 for the group name column
133 Size_t nRows = fResults.size();
134
135 LOG(info) << " Creating table with " << nRows << " rows and " << nCols << " columns for task: " << taskName;
136 auto table = std::make_shared<Table>(taskName, nRows, nCols, taskName);
137 for (size_t i = 0; i < nCols; ++i) {
138 if (i == 0) {
139 table->SetColumnTitle(i, "Histogram");
140 }
141 else {
142 table->SetColumnTitle(i, fVersionLabel[i - 1]);
143 }
144
145 for (size_t j = 0; j < nRows; ++j) {
146 if (i == 0) {
147 table->SetCell(j, i, fObjlist[j]); // First column is the group name
148 }
149 else {
150 const auto& result = fResults.at(fObjlist[j])[i - 1];
151
152 std::string cellText = CmpInferenceToString(result.GetCmpInference());
153 LOG(info) << "Processing result for group: " << fObjlist[j] << " | Version: " << fVersionLabel[i - 1]
154 << " | Result: " << cellText;
155 table->SetCell(j, i, cellText);
156 }
157 }
158 }
159
160
161 section->Add(table);
162 // Add canvas figures to the same section
163 std::vector<std::string> canvasPaths = GetCanvasPaths(taskDir);
164 for (const auto& path : canvasPaths) {
165 if (auto* canv = dynamic_cast<TCanvas*>(taskDir->Get(path.c_str()))) {
166 auto fig = std::make_shared<Figure>(path);
167 fig->AddPlot(fReport->SaveCanvas(canv, path));
168 section->Add(fig);
169 }
170 }
171 }
172 }
173 }
174
175 f->Close();
176}
177
178std::vector<std::string> QaReportGenerator::GetCanvasPaths(TDirectory* dir, const std::string& prefix)
179{
180 std::vector<std::string> paths;
181 if (!dir) return paths;
182
183 TIter nextKey(dir->GetListOfKeys());
184 while (auto* key = static_cast<TKey*>(nextKey())) {
185 const std::string keyName = key->GetName();
186
187
188 auto* obj = key->ReadObj();
189 auto* subDir = dynamic_cast<TDirectory*>(obj);
190
191 if (subDir) {
192
193
194 TIter subIter(subDir->GetListOfKeys());
195 while (auto* subKey = static_cast<TKey*>(subIter())) {
196 auto* canvas = dynamic_cast<TCanvas*>(subKey->ReadObj());
197 if (canvas) {
198 std::string canvasPath = prefix + keyName + "/" + subKey->GetName();
199 LOG(info) << "Found canvas: " << canvasPath;
200 paths.push_back(canvasPath);
201 }
202 }
203
204 const auto subPaths = GetCanvasPaths(subDir, prefix + keyName + "/");
205 paths.insert(paths.end(), subPaths.begin(), subPaths.end());
206 }
207 }
208 return paths;
209}
210
211// ---------------------------------------------------------------------
212std::string QaReportGenerator::EscapeLatex(const std::string& text)
213{
214 std::string escaped = text;
215 const std::string specialChars = "#$%&_";
216 for (char c : specialChars) {
217 std::string toReplace(1, c);
218 std::string replacement = "\\" + toReplace;
219 size_t pos = 0;
220 while ((pos = escaped.find(toReplace, pos)) != std::string::npos) {
221 escaped.replace(pos, toReplace.length(), replacement);
222 pos += replacement.length();
223 }
224 }
225 return escaped;
226}
227
228
229void QaReportGenerator::GetResults(TDirectory* dir, const std::string& currentPath, const std::string& taskName)
230{
231
232
233 TIter next(dir->GetListOfKeys());
234 while (TKey* key = static_cast<TKey*>(next())) {
235 TObject* obj = key->ReadObj();
236 std::string name = key->GetName();
237 std::string fullPath = currentPath + "/" + name;
238 // LOG(info) << "Processing object: " << fullPath;
239 //LOG(info) << "Object type: " << obj->ClassName();
240 if (obj->InheritsFrom("TDirectory")) {
241 auto subDir = static_cast<TDirectory*>(obj);
242 GetResults(subDir, fullPath, taskName);
243 }
244 else if (auto* concreteObj = dynamic_cast<Result*>(obj)) {
245 Result res(*concreteObj);
246 LOG(info) << "Found Result object: " << name;
247 LOG(info) << "full Path: " << fullPath;
248 std::string groupName;
249 std::string taskPrefix = taskName + "/"; // = "/CbmCaOutputQa/"
250 LOG(info) << "Task prefix: " << taskPrefix;
251 size_t pos = fullPath.find(taskPrefix);
252
253 if (pos != std::string::npos) {
254 size_t start = pos + taskPrefix.length(); // Start of group: just after "CbmCaOutputQa/"
255 size_t end = fullPath.rfind('/'); // End of group: before last component
256 if (end != std::string::npos && end > start) {
257 groupName = fullPath.substr(start, end - start); // Extract "sec_mu/reco_tx"
258 }
259 }
260 std::string versionLabel;
261 std::string prefix = "result_";
262 size_t posVer = fullPath.rfind(prefix);
263 if (posVer != std::string::npos) {
264 versionLabel = fullPath.substr(posVer + prefix.length());
265 LOG(info) << "Extracted version label: " << versionLabel;
266 }
267
268 LOG(info) << "Group name: " << groupName;
269 fObjlist.push_back(groupName);
270 fResults.at(groupName).emplace_back(res);
271 if (std::find(fVersionLabel.begin(), fVersionLabel.end(), versionLabel) == fVersionLabel.end()) {
272 fVersionLabel.push_back(versionLabel);
273 }
274
275 LOG(info) << "Added result to group: " << groupName << " | Total results: " << fResults.at(groupName).size();
276 }
277 }
278}
280{
281
282 switch (inf) {
283 case ECmpInference::StronglyEqual: return "Same";
284 case ECmpInference::WeaklyEqual: return "Consistent";
285 case ECmpInference::Different: return "Different";
286 default: return "Unknown";
287 }
288}
LaTeX Beamer engine for the cbm::qa::report (header)
Base class for the report figure (header)
HTML document engine for the cbm::qa::report (source)
LaTeX Beamer engine for the cbm::qa::report (source)
Base class for the report section (header)
Base class for the report table (header)
void Process(const std::string &yamlFile)
Process the YAML results file.
std::vector< std::string > fVersionLabel
version labels for comparison
std::string fOutDir
output directory
std::string fSetup
setup tag
std::unique_ptr< TFile > fFile
input ROOT file
bool fIgnoreSame
ignore "Same" results in comparison tables
static std::vector< std::string > GetCanvasPaths(TDirectory *dir, const std::string &prefix="")
static std::string CmpInferenceToString(cbm::qa::checker::ECmpInference inf)
EngineType fEngine
chosen engine
EngineType
Supported output engines.
static std::string EscapeLatex(const std::string &text)
std::unordered_map< std::string, std::vector< cbm::qa::checker::Result > > fResults
results map
std::unique_ptr< cbm::qa::report::Builder > fReport
report builder
QaReportGenerator(const std::string &qaFile, const std::string &setup, const std::string &outDir, EngineType engine=EngineType::kLatex, const std::string &figExt="png", bool ignoreSame=false)
void Generate()
Run the complete report generation pipeline.
void GetResults(TDirectory *dir, const std::string &currentPath, const std::string &taskName)
std::vector< std::string > fObjlist
list of objects in the file
A storable result of the QA-checker comparison routine.
Plain LaTeX document engine.
Plain LaTeX document engine.
ECmpInference
The object comparison inference.