v2.0.0
Loading...
Searching...
No Matches
mna_verification.cpp
Go to the documentation of this file.
1//=============================================================================================================
21
22//=============================================================================================================
23// INCLUDES
24//=============================================================================================================
25
26#include "mna_verification.h"
27
28#include <QJsonArray>
29#include <QCborArray>
30#include <QSysInfo>
31
32//=============================================================================================================
33// USED NAMESPACES
34//=============================================================================================================
35
36using namespace MNALIB;
37
38//=============================================================================================================
39// MnaVerificationCheck
40//=============================================================================================================
41
43{
44 QJsonObject json;
45 json[QStringLiteral("id")] = id;
46 json[QStringLiteral("description")] = description;
47 json[QStringLiteral("phase")] = phase;
48 json[QStringLiteral("expression")] = expression;
49 if (!script.code.isEmpty()) {
50 json[QStringLiteral("script")] = script.toJson();
51 }
52 json[QStringLiteral("severity")] = severity;
53 if (!onFail.isEmpty()) {
54 json[QStringLiteral("on_fail")] = onFail;
55 }
56 return json;
57}
58
59//=============================================================================================================
60
62{
64 c.id = json.value(QStringLiteral("id")).toString();
65 c.description = json.value(QStringLiteral("description")).toString();
66 c.phase = json.value(QStringLiteral("phase")).toString();
67 c.expression = json.value(QStringLiteral("expression")).toString();
68 if (json.contains(QStringLiteral("script"))) {
69 c.script = MnaScript::fromJson(json.value(QStringLiteral("script")).toObject());
70 }
71 c.severity = json.value(QStringLiteral("severity")).toString();
72 c.onFail = json.value(QStringLiteral("on_fail")).toString();
73 return c;
74}
75
76//=============================================================================================================
77
79{
80 QCborMap cbor;
81 cbor.insert(QStringLiteral("id"), id);
82 cbor.insert(QStringLiteral("description"), description);
83 cbor.insert(QStringLiteral("phase"), phase);
84 cbor.insert(QStringLiteral("expression"), expression);
85 if (!script.code.isEmpty()) {
86 cbor.insert(QStringLiteral("script"), script.toCbor());
87 }
88 cbor.insert(QStringLiteral("severity"), severity);
89 if (!onFail.isEmpty()) {
90 cbor.insert(QStringLiteral("on_fail"), onFail);
91 }
92 return cbor;
93}
94
95//=============================================================================================================
96
98{
100 c.id = cbor.value(QStringLiteral("id")).toString();
101 c.description = cbor.value(QStringLiteral("description")).toString();
102 c.phase = cbor.value(QStringLiteral("phase")).toString();
103 c.expression = cbor.value(QStringLiteral("expression")).toString();
104 if (cbor.contains(QStringLiteral("script"))) {
105 c.script = MnaScript::fromCbor(cbor.value(QStringLiteral("script")).toMap());
106 }
107 c.severity = cbor.value(QStringLiteral("severity")).toString();
108 c.onFail = cbor.value(QStringLiteral("on_fail")).toString();
109 return c;
110}
111
112//=============================================================================================================
113// MnaVerificationResult
114//=============================================================================================================
115
117{
118 QJsonObject json;
119 json[QStringLiteral("check_id")] = checkId;
120 json[QStringLiteral("passed")] = passed;
121 json[QStringLiteral("severity")] = severity;
122 json[QStringLiteral("message")] = message;
123 json[QStringLiteral("actual_value")] = QJsonValue::fromVariant(actualValue);
124 if (evaluatedAt.isValid()) {
125 json[QStringLiteral("evaluated_at")] = evaluatedAt.toString(Qt::ISODateWithMs);
126 }
127 return json;
128}
129
130//=============================================================================================================
131
133{
135 r.checkId = json.value(QStringLiteral("check_id")).toString();
136 r.passed = json.value(QStringLiteral("passed")).toBool(false);
137 r.severity = json.value(QStringLiteral("severity")).toString();
138 r.message = json.value(QStringLiteral("message")).toString();
139 r.actualValue = json.value(QStringLiteral("actual_value")).toVariant();
140 r.evaluatedAt = QDateTime::fromString(json.value(QStringLiteral("evaluated_at")).toString(), Qt::ISODateWithMs);
141 return r;
142}
143
144//=============================================================================================================
145
147{
148 QCborMap cbor;
149 cbor.insert(QStringLiteral("check_id"), checkId);
150 cbor.insert(QStringLiteral("passed"), passed);
151 cbor.insert(QStringLiteral("severity"), severity);
152 cbor.insert(QStringLiteral("message"), message);
153 cbor.insert(QStringLiteral("actual_value"), QCborValue::fromVariant(actualValue));
154 if (evaluatedAt.isValid()) {
155 cbor.insert(QStringLiteral("evaluated_at"), evaluatedAt.toString(Qt::ISODateWithMs));
156 }
157 return cbor;
158}
159
160//=============================================================================================================
161
163{
165 r.checkId = cbor.value(QStringLiteral("check_id")).toString();
166 r.passed = cbor.value(QStringLiteral("passed")).toBool();
167 r.severity = cbor.value(QStringLiteral("severity")).toString();
168 r.message = cbor.value(QStringLiteral("message")).toString();
169 r.actualValue = cbor.value(QStringLiteral("actual_value")).toVariant();
170 r.evaluatedAt = QDateTime::fromString(cbor.value(QStringLiteral("evaluated_at")).toString(), Qt::ISODateWithMs);
171 return r;
172}
173
174//=============================================================================================================
175// MnaProvenance
176//=============================================================================================================
177
178QJsonObject MnaProvenance::toJson() const
179{
180 QJsonObject json;
181
182 if (!inputHashes.isEmpty()) {
183 QJsonObject hashObj;
184 for (auto it = inputHashes.constBegin(); it != inputHashes.constEnd(); ++it) {
185 hashObj.insert(it.key(), it.value());
186 }
187 json[QStringLiteral("input_hashes")] = hashObj;
188 }
189
190 if (!resolvedAttributes.isEmpty()) {
191 QJsonObject attrObj;
192 for (auto it = resolvedAttributes.constBegin(); it != resolvedAttributes.constEnd(); ++it) {
193 attrObj.insert(it.key(), QJsonValue::fromVariant(it.value()));
194 }
195 json[QStringLiteral("resolved_attributes")] = attrObj;
196 }
197
198 if (!mneCppVersion.isEmpty())
199 json[QStringLiteral("mne_cpp_version")] = mneCppVersion;
200 if (!qtVersion.isEmpty())
201 json[QStringLiteral("qt_version")] = qtVersion;
202 if (!compilerInfo.isEmpty())
203 json[QStringLiteral("compiler_info")] = compilerInfo;
204 if (!osInfo.isEmpty())
205 json[QStringLiteral("os_info")] = osInfo;
206 if (!hostName.isEmpty())
207 json[QStringLiteral("host_name")] = hostName;
208 if (!externalToolVersion.isEmpty())
209 json[QStringLiteral("external_tool_version")] = externalToolVersion;
210
211 if (startedAt.isValid())
212 json[QStringLiteral("started_at")] = startedAt.toString(Qt::ISODateWithMs);
213 if (finishedAt.isValid())
214 json[QStringLiteral("finished_at")] = finishedAt.toString(Qt::ISODateWithMs);
215 if (wallTimeMs > 0)
216 json[QStringLiteral("wall_time_ms")] = wallTimeMs;
217 if (peakMemoryBytes > 0)
218 json[QStringLiteral("peak_memory_bytes")] = peakMemoryBytes;
219 if (randomSeed >= 0)
220 json[QStringLiteral("random_seed")] = randomSeed;
221
222 return json;
223}
224
225//=============================================================================================================
226
228{
230
231 const QJsonObject hashObj = json.value(QStringLiteral("input_hashes")).toObject();
232 for (auto it = hashObj.constBegin(); it != hashObj.constEnd(); ++it) {
233 p.inputHashes.insert(it.key(), it.value().toString());
234 }
235
236 const QJsonObject attrObj = json.value(QStringLiteral("resolved_attributes")).toObject();
237 for (auto it = attrObj.constBegin(); it != attrObj.constEnd(); ++it) {
238 p.resolvedAttributes.insert(it.key(), it.value().toVariant());
239 }
240
241 p.mneCppVersion = json.value(QStringLiteral("mne_cpp_version")).toString();
242 p.qtVersion = json.value(QStringLiteral("qt_version")).toString();
243 p.compilerInfo = json.value(QStringLiteral("compiler_info")).toString();
244 p.osInfo = json.value(QStringLiteral("os_info")).toString();
245 p.hostName = json.value(QStringLiteral("host_name")).toString();
246 p.externalToolVersion = json.value(QStringLiteral("external_tool_version")).toString();
247
248 p.startedAt = QDateTime::fromString(json.value(QStringLiteral("started_at")).toString(), Qt::ISODateWithMs);
249 p.finishedAt = QDateTime::fromString(json.value(QStringLiteral("finished_at")).toString(), Qt::ISODateWithMs);
250 p.wallTimeMs = static_cast<qint64>(json.value(QStringLiteral("wall_time_ms")).toDouble(0));
251 p.peakMemoryBytes = static_cast<qint64>(json.value(QStringLiteral("peak_memory_bytes")).toDouble(0));
252 p.randomSeed = static_cast<qint64>(json.value(QStringLiteral("random_seed")).toDouble(-1));
253
254 return p;
255}
256
257//=============================================================================================================
258
259QCborMap MnaProvenance::toCbor() const
260{
261 QCborMap cbor;
262
263 if (!inputHashes.isEmpty()) {
264 QCborMap hashMap;
265 for (auto it = inputHashes.constBegin(); it != inputHashes.constEnd(); ++it) {
266 hashMap.insert(it.key(), it.value());
267 }
268 cbor.insert(QStringLiteral("input_hashes"), hashMap);
269 }
270
271 if (!resolvedAttributes.isEmpty()) {
272 QCborMap attrMap;
273 for (auto it = resolvedAttributes.constBegin(); it != resolvedAttributes.constEnd(); ++it) {
274 attrMap.insert(it.key(), QCborValue::fromVariant(it.value()));
275 }
276 cbor.insert(QStringLiteral("resolved_attributes"), attrMap);
277 }
278
279 if (!mneCppVersion.isEmpty())
280 cbor.insert(QStringLiteral("mne_cpp_version"), mneCppVersion);
281 if (!qtVersion.isEmpty())
282 cbor.insert(QStringLiteral("qt_version"), qtVersion);
283 if (!compilerInfo.isEmpty())
284 cbor.insert(QStringLiteral("compiler_info"), compilerInfo);
285 if (!osInfo.isEmpty())
286 cbor.insert(QStringLiteral("os_info"), osInfo);
287 if (!hostName.isEmpty())
288 cbor.insert(QStringLiteral("host_name"), hostName);
289 if (!externalToolVersion.isEmpty())
290 cbor.insert(QStringLiteral("external_tool_version"), externalToolVersion);
291
292 if (startedAt.isValid())
293 cbor.insert(QStringLiteral("started_at"), startedAt.toString(Qt::ISODateWithMs));
294 if (finishedAt.isValid())
295 cbor.insert(QStringLiteral("finished_at"), finishedAt.toString(Qt::ISODateWithMs));
296 if (wallTimeMs > 0)
297 cbor.insert(QStringLiteral("wall_time_ms"), wallTimeMs);
298 if (peakMemoryBytes > 0)
299 cbor.insert(QStringLiteral("peak_memory_bytes"), peakMemoryBytes);
300 if (randomSeed >= 0)
301 cbor.insert(QStringLiteral("random_seed"), randomSeed);
302
303 return cbor;
304}
305
306//=============================================================================================================
307
309{
311
312 const QCborMap hashMap = cbor.value(QStringLiteral("input_hashes")).toMap();
313 for (auto it = hashMap.constBegin(); it != hashMap.constEnd(); ++it) {
314 p.inputHashes.insert(it.key().toString(), it.value().toString());
315 }
316
317 const QCborMap attrMap = cbor.value(QStringLiteral("resolved_attributes")).toMap();
318 for (auto it = attrMap.constBegin(); it != attrMap.constEnd(); ++it) {
319 p.resolvedAttributes.insert(it.key().toString(), it.value().toVariant());
320 }
321
322 p.mneCppVersion = cbor.value(QStringLiteral("mne_cpp_version")).toString();
323 p.qtVersion = cbor.value(QStringLiteral("qt_version")).toString();
324 p.compilerInfo = cbor.value(QStringLiteral("compiler_info")).toString();
325 p.osInfo = cbor.value(QStringLiteral("os_info")).toString();
326 p.hostName = cbor.value(QStringLiteral("host_name")).toString();
327 p.externalToolVersion = cbor.value(QStringLiteral("external_tool_version")).toString();
328
329 p.startedAt = QDateTime::fromString(cbor.value(QStringLiteral("started_at")).toString(), Qt::ISODateWithMs);
330 p.finishedAt = QDateTime::fromString(cbor.value(QStringLiteral("finished_at")).toString(), Qt::ISODateWithMs);
331 p.wallTimeMs = cbor.value(QStringLiteral("wall_time_ms")).toInteger(0);
332 p.peakMemoryBytes = cbor.value(QStringLiteral("peak_memory_bytes")).toInteger(0);
333 p.randomSeed = cbor.value(QStringLiteral("random_seed")).toInteger(-1);
334
335 return p;
336}
337
338//=============================================================================================================
339// MnaVerification
340//=============================================================================================================
341
342QJsonObject MnaVerification::toJson() const
343{
344 QJsonObject json;
345
346 if (!explanation.isEmpty()) {
347 json[QStringLiteral("explanation")] = explanation;
348 }
349
350 if (!checks.isEmpty()) {
351 QJsonArray arr;
352 for (const MnaVerificationCheck& c : checks) {
353 arr.append(c.toJson());
354 }
355 json[QStringLiteral("checks")] = arr;
356 }
357
358 if (!preResults.isEmpty()) {
359 QJsonArray arr;
360 for (const MnaVerificationResult& r : preResults) {
361 arr.append(r.toJson());
362 }
363 json[QStringLiteral("pre_results")] = arr;
364 }
365
366 if (!postResults.isEmpty()) {
367 QJsonArray arr;
368 for (const MnaVerificationResult& r : postResults) {
369 arr.append(r.toJson());
370 }
371 json[QStringLiteral("post_results")] = arr;
372 }
373
374 QJsonObject provObj = provenance.toJson();
375 if (!provObj.isEmpty()) {
376 json[QStringLiteral("provenance")] = provObj;
377 }
378
379 return json;
380}
381
382//=============================================================================================================
383
385{
387
388 v.explanation = json.value(QStringLiteral("explanation")).toString();
389
390 const QJsonArray checksArr = json.value(QStringLiteral("checks")).toArray();
391 for (const QJsonValue& val : checksArr) {
392 v.checks.append(MnaVerificationCheck::fromJson(val.toObject()));
393 }
394
395 const QJsonArray preArr = json.value(QStringLiteral("pre_results")).toArray();
396 for (const QJsonValue& val : preArr) {
397 v.preResults.append(MnaVerificationResult::fromJson(val.toObject()));
398 }
399
400 const QJsonArray postArr = json.value(QStringLiteral("post_results")).toArray();
401 for (const QJsonValue& val : postArr) {
402 v.postResults.append(MnaVerificationResult::fromJson(val.toObject()));
403 }
404
405 if (json.contains(QStringLiteral("provenance"))) {
406 v.provenance = MnaProvenance::fromJson(json.value(QStringLiteral("provenance")).toObject());
407 }
408
409 return v;
410}
411
412//=============================================================================================================
413
415{
416 QCborMap cbor;
417
418 if (!explanation.isEmpty()) {
419 cbor.insert(QStringLiteral("explanation"), explanation);
420 }
421
422 if (!checks.isEmpty()) {
423 QCborArray arr;
424 for (const MnaVerificationCheck& c : checks) {
425 arr.append(c.toCbor());
426 }
427 cbor.insert(QStringLiteral("checks"), arr);
428 }
429
430 if (!preResults.isEmpty()) {
431 QCborArray arr;
432 for (const MnaVerificationResult& r : preResults) {
433 arr.append(r.toCbor());
434 }
435 cbor.insert(QStringLiteral("pre_results"), arr);
436 }
437
438 if (!postResults.isEmpty()) {
439 QCborArray arr;
440 for (const MnaVerificationResult& r : postResults) {
441 arr.append(r.toCbor());
442 }
443 cbor.insert(QStringLiteral("post_results"), arr);
444 }
445
446 QCborMap provMap = provenance.toCbor();
447 if (!provMap.isEmpty()) {
448 cbor.insert(QStringLiteral("provenance"), provMap);
449 }
450
451 return cbor;
452}
453
454//=============================================================================================================
455
457{
459
460 v.explanation = cbor.value(QStringLiteral("explanation")).toString();
461
462 const QCborArray checksArr = cbor.value(QStringLiteral("checks")).toArray();
463 for (const QCborValue& val : checksArr) {
464 v.checks.append(MnaVerificationCheck::fromCbor(val.toMap()));
465 }
466
467 const QCborArray preArr = cbor.value(QStringLiteral("pre_results")).toArray();
468 for (const QCborValue& val : preArr) {
469 v.preResults.append(MnaVerificationResult::fromCbor(val.toMap()));
470 }
471
472 const QCborArray postArr = cbor.value(QStringLiteral("post_results")).toArray();
473 for (const QCborValue& val : postArr) {
474 v.postResults.append(MnaVerificationResult::fromCbor(val.toMap()));
475 }
476
477 if (cbor.contains(QStringLiteral("provenance"))) {
478 v.provenance = MnaProvenance::fromCbor(cbor.value(QStringLiteral("provenance")).toMap());
479 }
480
481 return v;
482}
Verification, declarative checks, evaluation results and provenance snapshot attached to every MNALIB...
MNE Analysis Container Format (mna/mnx).
static MnaScript fromJson(const QJsonObject &json)
static MnaScript fromCbor(const QCborMap &cbor)
Verification check for a graph node.
QString severity
"error" (abort), "warning" (log + continue), "info" (always continue)
QString expression
MnaParamTree expression over attributes, inputs and (post) outputs: "rank > 0".
QString phase
"pre" (before execution) or "post" (after execution)
QString description
Human-readable: "Covariance matrix must be positive-definite".
static MnaVerificationCheck fromCbor(const QCborMap &cbor)
QString onFail
Optional remediation hint.
static MnaVerificationCheck fromJson(const QJsonObject &json)
QString id
Unique check identifier within the node (e.g. "cov_posdef").
Check evaluation result.
static MnaVerificationResult fromCbor(const QCborMap &cbor)
QString message
Formatted message: "PASS: ..." or "FAIL [error]: ...".
QString checkId
References MnaVerificationCheck::id.
QVariant actualValue
The evaluated expression result.
QString severity
Echoed from the check definition.
QDateTime evaluatedAt
When this check was evaluated.
bool passed
true if the expression evaluated to true
static MnaVerificationResult fromJson(const QJsonObject &json)
Provenance record for reproducibility.
QJsonObject toJson() const
static MnaProvenance fromCbor(const QCborMap &cbor)
QString externalToolVersion
e.g. "FreeSurfer 7.4.1", "Python 3.11.5"
QString osInfo
e.g. "macOS 15.4 arm64"
qint64 peakMemoryBytes
Peak RSS (if measurable), 0 otherwise.
QString qtVersion
e.g. "6.11.0"
QString compilerInfo
e.g. "AppleClang 16.0.0"
qint64 wallTimeMs
Wall-clock duration in milliseconds.
QString hostName
Machine name (for cluster provenance).
qint64 randomSeed
-1 if not applicable
QMap< QString, QString > inputHashes
portName → SHA-256
static MnaProvenance fromJson(const QJsonObject &json)
QString mneCppVersion
e.g. "2.2.0"
Verification, explanation, and provenance for a graph node.
MnaProvenance provenance
Complete provenance snapshot (populated by executor).
QString explanation
Human-readable explanation of what this node does and why.
static MnaVerification fromJson(const QJsonObject &json)
QList< MnaVerificationCheck > checks
Declarative checks (authored by user, evaluated by executor).
QJsonObject toJson() const
QList< MnaVerificationResult > preResults
Results of pre-execution checks (populated by executor).
QList< MnaVerificationResult > postResults
Results of post-execution checks (populated by executor).
static MnaVerification fromCbor(const QCborMap &cbor)