v2.0.0
Loading...
Searching...
No Matches
mna_verification.cpp
Go to the documentation of this file.
1//=============================================================================================================
34
35//=============================================================================================================
36// INCLUDES
37//=============================================================================================================
38
39#include "mna_verification.h"
40
41#include <QJsonArray>
42#include <QCborArray>
43#include <QSysInfo>
44
45//=============================================================================================================
46// USED NAMESPACES
47//=============================================================================================================
48
49using namespace MNALIB;
50
51//=============================================================================================================
52// MnaVerificationCheck
53//=============================================================================================================
54
56{
57 QJsonObject json;
58 json[QStringLiteral("id")] = id;
59 json[QStringLiteral("description")] = description;
60 json[QStringLiteral("phase")] = phase;
61 json[QStringLiteral("expression")] = expression;
62 if (!script.code.isEmpty()) {
63 json[QStringLiteral("script")] = script.toJson();
64 }
65 json[QStringLiteral("severity")] = severity;
66 if (!onFail.isEmpty()) {
67 json[QStringLiteral("on_fail")] = onFail;
68 }
69 return json;
70}
71
72//=============================================================================================================
73
75{
77 c.id = json.value(QStringLiteral("id")).toString();
78 c.description = json.value(QStringLiteral("description")).toString();
79 c.phase = json.value(QStringLiteral("phase")).toString();
80 c.expression = json.value(QStringLiteral("expression")).toString();
81 if (json.contains(QStringLiteral("script"))) {
82 c.script = MnaScript::fromJson(json.value(QStringLiteral("script")).toObject());
83 }
84 c.severity = json.value(QStringLiteral("severity")).toString();
85 c.onFail = json.value(QStringLiteral("on_fail")).toString();
86 return c;
87}
88
89//=============================================================================================================
90
92{
93 QCborMap cbor;
94 cbor.insert(QStringLiteral("id"), id);
95 cbor.insert(QStringLiteral("description"), description);
96 cbor.insert(QStringLiteral("phase"), phase);
97 cbor.insert(QStringLiteral("expression"), expression);
98 if (!script.code.isEmpty()) {
99 cbor.insert(QStringLiteral("script"), script.toCbor());
100 }
101 cbor.insert(QStringLiteral("severity"), severity);
102 if (!onFail.isEmpty()) {
103 cbor.insert(QStringLiteral("on_fail"), onFail);
104 }
105 return cbor;
106}
107
108//=============================================================================================================
109
111{
113 c.id = cbor.value(QStringLiteral("id")).toString();
114 c.description = cbor.value(QStringLiteral("description")).toString();
115 c.phase = cbor.value(QStringLiteral("phase")).toString();
116 c.expression = cbor.value(QStringLiteral("expression")).toString();
117 if (cbor.contains(QStringLiteral("script"))) {
118 c.script = MnaScript::fromCbor(cbor.value(QStringLiteral("script")).toMap());
119 }
120 c.severity = cbor.value(QStringLiteral("severity")).toString();
121 c.onFail = cbor.value(QStringLiteral("on_fail")).toString();
122 return c;
123}
124
125//=============================================================================================================
126// MnaVerificationResult
127//=============================================================================================================
128
130{
131 QJsonObject json;
132 json[QStringLiteral("check_id")] = checkId;
133 json[QStringLiteral("passed")] = passed;
134 json[QStringLiteral("severity")] = severity;
135 json[QStringLiteral("message")] = message;
136 json[QStringLiteral("actual_value")] = QJsonValue::fromVariant(actualValue);
137 if (evaluatedAt.isValid()) {
138 json[QStringLiteral("evaluated_at")] = evaluatedAt.toString(Qt::ISODateWithMs);
139 }
140 return json;
141}
142
143//=============================================================================================================
144
146{
148 r.checkId = json.value(QStringLiteral("check_id")).toString();
149 r.passed = json.value(QStringLiteral("passed")).toBool(false);
150 r.severity = json.value(QStringLiteral("severity")).toString();
151 r.message = json.value(QStringLiteral("message")).toString();
152 r.actualValue = json.value(QStringLiteral("actual_value")).toVariant();
153 r.evaluatedAt = QDateTime::fromString(json.value(QStringLiteral("evaluated_at")).toString(), Qt::ISODateWithMs);
154 return r;
155}
156
157//=============================================================================================================
158
160{
161 QCborMap cbor;
162 cbor.insert(QStringLiteral("check_id"), checkId);
163 cbor.insert(QStringLiteral("passed"), passed);
164 cbor.insert(QStringLiteral("severity"), severity);
165 cbor.insert(QStringLiteral("message"), message);
166 cbor.insert(QStringLiteral("actual_value"), QCborValue::fromVariant(actualValue));
167 if (evaluatedAt.isValid()) {
168 cbor.insert(QStringLiteral("evaluated_at"), evaluatedAt.toString(Qt::ISODateWithMs));
169 }
170 return cbor;
171}
172
173//=============================================================================================================
174
176{
178 r.checkId = cbor.value(QStringLiteral("check_id")).toString();
179 r.passed = cbor.value(QStringLiteral("passed")).toBool();
180 r.severity = cbor.value(QStringLiteral("severity")).toString();
181 r.message = cbor.value(QStringLiteral("message")).toString();
182 r.actualValue = cbor.value(QStringLiteral("actual_value")).toVariant();
183 r.evaluatedAt = QDateTime::fromString(cbor.value(QStringLiteral("evaluated_at")).toString(), Qt::ISODateWithMs);
184 return r;
185}
186
187//=============================================================================================================
188// MnaProvenance
189//=============================================================================================================
190
191QJsonObject MnaProvenance::toJson() const
192{
193 QJsonObject json;
194
195 if (!inputHashes.isEmpty()) {
196 QJsonObject hashObj;
197 for (auto it = inputHashes.constBegin(); it != inputHashes.constEnd(); ++it) {
198 hashObj.insert(it.key(), it.value());
199 }
200 json[QStringLiteral("input_hashes")] = hashObj;
201 }
202
203 if (!resolvedAttributes.isEmpty()) {
204 QJsonObject attrObj;
205 for (auto it = resolvedAttributes.constBegin(); it != resolvedAttributes.constEnd(); ++it) {
206 attrObj.insert(it.key(), QJsonValue::fromVariant(it.value()));
207 }
208 json[QStringLiteral("resolved_attributes")] = attrObj;
209 }
210
211 if (!mneCppVersion.isEmpty()) json[QStringLiteral("mne_cpp_version")] = mneCppVersion;
212 if (!qtVersion.isEmpty()) json[QStringLiteral("qt_version")] = qtVersion;
213 if (!compilerInfo.isEmpty()) json[QStringLiteral("compiler_info")] = compilerInfo;
214 if (!osInfo.isEmpty()) json[QStringLiteral("os_info")] = osInfo;
215 if (!hostName.isEmpty()) json[QStringLiteral("host_name")] = hostName;
216 if (!externalToolVersion.isEmpty()) json[QStringLiteral("external_tool_version")] = externalToolVersion;
217
218 if (startedAt.isValid()) json[QStringLiteral("started_at")] = startedAt.toString(Qt::ISODateWithMs);
219 if (finishedAt.isValid()) json[QStringLiteral("finished_at")] = finishedAt.toString(Qt::ISODateWithMs);
220 if (wallTimeMs > 0) json[QStringLiteral("wall_time_ms")] = wallTimeMs;
221 if (peakMemoryBytes > 0) json[QStringLiteral("peak_memory_bytes")] = peakMemoryBytes;
222 if (randomSeed >= 0) json[QStringLiteral("random_seed")] = randomSeed;
223
224 return json;
225}
226
227//=============================================================================================================
228
230{
232
233 const QJsonObject hashObj = json.value(QStringLiteral("input_hashes")).toObject();
234 for (auto it = hashObj.constBegin(); it != hashObj.constEnd(); ++it) {
235 p.inputHashes.insert(it.key(), it.value().toString());
236 }
237
238 const QJsonObject attrObj = json.value(QStringLiteral("resolved_attributes")).toObject();
239 for (auto it = attrObj.constBegin(); it != attrObj.constEnd(); ++it) {
240 p.resolvedAttributes.insert(it.key(), it.value().toVariant());
241 }
242
243 p.mneCppVersion = json.value(QStringLiteral("mne_cpp_version")).toString();
244 p.qtVersion = json.value(QStringLiteral("qt_version")).toString();
245 p.compilerInfo = json.value(QStringLiteral("compiler_info")).toString();
246 p.osInfo = json.value(QStringLiteral("os_info")).toString();
247 p.hostName = json.value(QStringLiteral("host_name")).toString();
248 p.externalToolVersion = json.value(QStringLiteral("external_tool_version")).toString();
249
250 p.startedAt = QDateTime::fromString(json.value(QStringLiteral("started_at")).toString(), Qt::ISODateWithMs);
251 p.finishedAt = QDateTime::fromString(json.value(QStringLiteral("finished_at")).toString(), Qt::ISODateWithMs);
252 p.wallTimeMs = static_cast<qint64>(json.value(QStringLiteral("wall_time_ms")).toDouble(0));
253 p.peakMemoryBytes = static_cast<qint64>(json.value(QStringLiteral("peak_memory_bytes")).toDouble(0));
254 p.randomSeed = static_cast<qint64>(json.value(QStringLiteral("random_seed")).toDouble(-1));
255
256 return p;
257}
258
259//=============================================================================================================
260
261QCborMap MnaProvenance::toCbor() const
262{
263 QCborMap cbor;
264
265 if (!inputHashes.isEmpty()) {
266 QCborMap hashMap;
267 for (auto it = inputHashes.constBegin(); it != inputHashes.constEnd(); ++it) {
268 hashMap.insert(it.key(), it.value());
269 }
270 cbor.insert(QStringLiteral("input_hashes"), hashMap);
271 }
272
273 if (!resolvedAttributes.isEmpty()) {
274 QCborMap attrMap;
275 for (auto it = resolvedAttributes.constBegin(); it != resolvedAttributes.constEnd(); ++it) {
276 attrMap.insert(it.key(), QCborValue::fromVariant(it.value()));
277 }
278 cbor.insert(QStringLiteral("resolved_attributes"), attrMap);
279 }
280
281 if (!mneCppVersion.isEmpty()) cbor.insert(QStringLiteral("mne_cpp_version"), mneCppVersion);
282 if (!qtVersion.isEmpty()) cbor.insert(QStringLiteral("qt_version"), qtVersion);
283 if (!compilerInfo.isEmpty()) cbor.insert(QStringLiteral("compiler_info"), compilerInfo);
284 if (!osInfo.isEmpty()) cbor.insert(QStringLiteral("os_info"), osInfo);
285 if (!hostName.isEmpty()) cbor.insert(QStringLiteral("host_name"), hostName);
286 if (!externalToolVersion.isEmpty()) cbor.insert(QStringLiteral("external_tool_version"), externalToolVersion);
287
288 if (startedAt.isValid()) cbor.insert(QStringLiteral("started_at"), startedAt.toString(Qt::ISODateWithMs));
289 if (finishedAt.isValid()) cbor.insert(QStringLiteral("finished_at"), finishedAt.toString(Qt::ISODateWithMs));
290 if (wallTimeMs > 0) cbor.insert(QStringLiteral("wall_time_ms"), wallTimeMs);
291 if (peakMemoryBytes > 0) cbor.insert(QStringLiteral("peak_memory_bytes"), peakMemoryBytes);
292 if (randomSeed >= 0) cbor.insert(QStringLiteral("random_seed"), randomSeed);
293
294 return cbor;
295}
296
297//=============================================================================================================
298
300{
302
303 const QCborMap hashMap = cbor.value(QStringLiteral("input_hashes")).toMap();
304 for (auto it = hashMap.constBegin(); it != hashMap.constEnd(); ++it) {
305 p.inputHashes.insert(it.key().toString(), it.value().toString());
306 }
307
308 const QCborMap attrMap = cbor.value(QStringLiteral("resolved_attributes")).toMap();
309 for (auto it = attrMap.constBegin(); it != attrMap.constEnd(); ++it) {
310 p.resolvedAttributes.insert(it.key().toString(), it.value().toVariant());
311 }
312
313 p.mneCppVersion = cbor.value(QStringLiteral("mne_cpp_version")).toString();
314 p.qtVersion = cbor.value(QStringLiteral("qt_version")).toString();
315 p.compilerInfo = cbor.value(QStringLiteral("compiler_info")).toString();
316 p.osInfo = cbor.value(QStringLiteral("os_info")).toString();
317 p.hostName = cbor.value(QStringLiteral("host_name")).toString();
318 p.externalToolVersion = cbor.value(QStringLiteral("external_tool_version")).toString();
319
320 p.startedAt = QDateTime::fromString(cbor.value(QStringLiteral("started_at")).toString(), Qt::ISODateWithMs);
321 p.finishedAt = QDateTime::fromString(cbor.value(QStringLiteral("finished_at")).toString(), Qt::ISODateWithMs);
322 p.wallTimeMs = cbor.value(QStringLiteral("wall_time_ms")).toInteger(0);
323 p.peakMemoryBytes = cbor.value(QStringLiteral("peak_memory_bytes")).toInteger(0);
324 p.randomSeed = cbor.value(QStringLiteral("random_seed")).toInteger(-1);
325
326 return p;
327}
328
329//=============================================================================================================
330// MnaVerification
331//=============================================================================================================
332
333QJsonObject MnaVerification::toJson() const
334{
335 QJsonObject json;
336
337 if (!explanation.isEmpty()) {
338 json[QStringLiteral("explanation")] = explanation;
339 }
340
341 if (!checks.isEmpty()) {
342 QJsonArray arr;
343 for (const MnaVerificationCheck& c : checks) {
344 arr.append(c.toJson());
345 }
346 json[QStringLiteral("checks")] = arr;
347 }
348
349 if (!preResults.isEmpty()) {
350 QJsonArray arr;
351 for (const MnaVerificationResult& r : preResults) {
352 arr.append(r.toJson());
353 }
354 json[QStringLiteral("pre_results")] = arr;
355 }
356
357 if (!postResults.isEmpty()) {
358 QJsonArray arr;
359 for (const MnaVerificationResult& r : postResults) {
360 arr.append(r.toJson());
361 }
362 json[QStringLiteral("post_results")] = arr;
363 }
364
365 QJsonObject provObj = provenance.toJson();
366 if (!provObj.isEmpty()) {
367 json[QStringLiteral("provenance")] = provObj;
368 }
369
370 return json;
371}
372
373//=============================================================================================================
374
376{
378
379 v.explanation = json.value(QStringLiteral("explanation")).toString();
380
381 const QJsonArray checksArr = json.value(QStringLiteral("checks")).toArray();
382 for (const QJsonValue& val : checksArr) {
383 v.checks.append(MnaVerificationCheck::fromJson(val.toObject()));
384 }
385
386 const QJsonArray preArr = json.value(QStringLiteral("pre_results")).toArray();
387 for (const QJsonValue& val : preArr) {
388 v.preResults.append(MnaVerificationResult::fromJson(val.toObject()));
389 }
390
391 const QJsonArray postArr = json.value(QStringLiteral("post_results")).toArray();
392 for (const QJsonValue& val : postArr) {
393 v.postResults.append(MnaVerificationResult::fromJson(val.toObject()));
394 }
395
396 if (json.contains(QStringLiteral("provenance"))) {
397 v.provenance = MnaProvenance::fromJson(json.value(QStringLiteral("provenance")).toObject());
398 }
399
400 return v;
401}
402
403//=============================================================================================================
404
406{
407 QCborMap cbor;
408
409 if (!explanation.isEmpty()) {
410 cbor.insert(QStringLiteral("explanation"), explanation);
411 }
412
413 if (!checks.isEmpty()) {
414 QCborArray arr;
415 for (const MnaVerificationCheck& c : checks) {
416 arr.append(c.toCbor());
417 }
418 cbor.insert(QStringLiteral("checks"), arr);
419 }
420
421 if (!preResults.isEmpty()) {
422 QCborArray arr;
423 for (const MnaVerificationResult& r : preResults) {
424 arr.append(r.toCbor());
425 }
426 cbor.insert(QStringLiteral("pre_results"), arr);
427 }
428
429 if (!postResults.isEmpty()) {
430 QCborArray arr;
431 for (const MnaVerificationResult& r : postResults) {
432 arr.append(r.toCbor());
433 }
434 cbor.insert(QStringLiteral("post_results"), arr);
435 }
436
437 QCborMap provMap = provenance.toCbor();
438 if (!provMap.isEmpty()) {
439 cbor.insert(QStringLiteral("provenance"), provMap);
440 }
441
442 return cbor;
443}
444
445//=============================================================================================================
446
448{
450
451 v.explanation = cbor.value(QStringLiteral("explanation")).toString();
452
453 const QCborArray checksArr = cbor.value(QStringLiteral("checks")).toArray();
454 for (const QCborValue& val : checksArr) {
455 v.checks.append(MnaVerificationCheck::fromCbor(val.toMap()));
456 }
457
458 const QCborArray preArr = cbor.value(QStringLiteral("pre_results")).toArray();
459 for (const QCborValue& val : preArr) {
460 v.preResults.append(MnaVerificationResult::fromCbor(val.toMap()));
461 }
462
463 const QCborArray postArr = cbor.value(QStringLiteral("post_results")).toArray();
464 for (const QCborValue& val : postArr) {
465 v.postResults.append(MnaVerificationResult::fromCbor(val.toMap()));
466 }
467
468 if (cbor.contains(QStringLiteral("provenance"))) {
469 v.provenance = MnaProvenance::fromCbor(cbor.value(QStringLiteral("provenance")).toMap());
470 }
471
472 return v;
473}
MnaVerification, MnaVerificationCheck, MnaVerificationResult, MnaProvenance declarations.
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
Simple evaluable expression: "rank(covariance) > 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)