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()) json[QStringLiteral("mne_cpp_version")] = mneCppVersion;
199 if (!qtVersion.isEmpty()) json[QStringLiteral("qt_version")] = qtVersion;
200 if (!compilerInfo.isEmpty()) json[QStringLiteral("compiler_info")] = compilerInfo;
201 if (!osInfo.isEmpty()) json[QStringLiteral("os_info")] = osInfo;
202 if (!hostName.isEmpty()) json[QStringLiteral("host_name")] = hostName;
203 if (!externalToolVersion.isEmpty()) json[QStringLiteral("external_tool_version")] = externalToolVersion;
204
205 if (startedAt.isValid()) json[QStringLiteral("started_at")] = startedAt.toString(Qt::ISODateWithMs);
206 if (finishedAt.isValid()) json[QStringLiteral("finished_at")] = finishedAt.toString(Qt::ISODateWithMs);
207 if (wallTimeMs > 0) json[QStringLiteral("wall_time_ms")] = wallTimeMs;
208 if (peakMemoryBytes > 0) json[QStringLiteral("peak_memory_bytes")] = peakMemoryBytes;
209 if (randomSeed >= 0) json[QStringLiteral("random_seed")] = randomSeed;
210
211 return json;
212}
213
214//=============================================================================================================
215
217{
219
220 const QJsonObject hashObj = json.value(QStringLiteral("input_hashes")).toObject();
221 for (auto it = hashObj.constBegin(); it != hashObj.constEnd(); ++it) {
222 p.inputHashes.insert(it.key(), it.value().toString());
223 }
224
225 const QJsonObject attrObj = json.value(QStringLiteral("resolved_attributes")).toObject();
226 for (auto it = attrObj.constBegin(); it != attrObj.constEnd(); ++it) {
227 p.resolvedAttributes.insert(it.key(), it.value().toVariant());
228 }
229
230 p.mneCppVersion = json.value(QStringLiteral("mne_cpp_version")).toString();
231 p.qtVersion = json.value(QStringLiteral("qt_version")).toString();
232 p.compilerInfo = json.value(QStringLiteral("compiler_info")).toString();
233 p.osInfo = json.value(QStringLiteral("os_info")).toString();
234 p.hostName = json.value(QStringLiteral("host_name")).toString();
235 p.externalToolVersion = json.value(QStringLiteral("external_tool_version")).toString();
236
237 p.startedAt = QDateTime::fromString(json.value(QStringLiteral("started_at")).toString(), Qt::ISODateWithMs);
238 p.finishedAt = QDateTime::fromString(json.value(QStringLiteral("finished_at")).toString(), Qt::ISODateWithMs);
239 p.wallTimeMs = static_cast<qint64>(json.value(QStringLiteral("wall_time_ms")).toDouble(0));
240 p.peakMemoryBytes = static_cast<qint64>(json.value(QStringLiteral("peak_memory_bytes")).toDouble(0));
241 p.randomSeed = static_cast<qint64>(json.value(QStringLiteral("random_seed")).toDouble(-1));
242
243 return p;
244}
245
246//=============================================================================================================
247
248QCborMap MnaProvenance::toCbor() const
249{
250 QCborMap cbor;
251
252 if (!inputHashes.isEmpty()) {
253 QCborMap hashMap;
254 for (auto it = inputHashes.constBegin(); it != inputHashes.constEnd(); ++it) {
255 hashMap.insert(it.key(), it.value());
256 }
257 cbor.insert(QStringLiteral("input_hashes"), hashMap);
258 }
259
260 if (!resolvedAttributes.isEmpty()) {
261 QCborMap attrMap;
262 for (auto it = resolvedAttributes.constBegin(); it != resolvedAttributes.constEnd(); ++it) {
263 attrMap.insert(it.key(), QCborValue::fromVariant(it.value()));
264 }
265 cbor.insert(QStringLiteral("resolved_attributes"), attrMap);
266 }
267
268 if (!mneCppVersion.isEmpty()) cbor.insert(QStringLiteral("mne_cpp_version"), mneCppVersion);
269 if (!qtVersion.isEmpty()) cbor.insert(QStringLiteral("qt_version"), qtVersion);
270 if (!compilerInfo.isEmpty()) cbor.insert(QStringLiteral("compiler_info"), compilerInfo);
271 if (!osInfo.isEmpty()) cbor.insert(QStringLiteral("os_info"), osInfo);
272 if (!hostName.isEmpty()) cbor.insert(QStringLiteral("host_name"), hostName);
273 if (!externalToolVersion.isEmpty()) cbor.insert(QStringLiteral("external_tool_version"), externalToolVersion);
274
275 if (startedAt.isValid()) cbor.insert(QStringLiteral("started_at"), startedAt.toString(Qt::ISODateWithMs));
276 if (finishedAt.isValid()) cbor.insert(QStringLiteral("finished_at"), finishedAt.toString(Qt::ISODateWithMs));
277 if (wallTimeMs > 0) cbor.insert(QStringLiteral("wall_time_ms"), wallTimeMs);
278 if (peakMemoryBytes > 0) cbor.insert(QStringLiteral("peak_memory_bytes"), peakMemoryBytes);
279 if (randomSeed >= 0) cbor.insert(QStringLiteral("random_seed"), randomSeed);
280
281 return cbor;
282}
283
284//=============================================================================================================
285
287{
289
290 const QCborMap hashMap = cbor.value(QStringLiteral("input_hashes")).toMap();
291 for (auto it = hashMap.constBegin(); it != hashMap.constEnd(); ++it) {
292 p.inputHashes.insert(it.key().toString(), it.value().toString());
293 }
294
295 const QCborMap attrMap = cbor.value(QStringLiteral("resolved_attributes")).toMap();
296 for (auto it = attrMap.constBegin(); it != attrMap.constEnd(); ++it) {
297 p.resolvedAttributes.insert(it.key().toString(), it.value().toVariant());
298 }
299
300 p.mneCppVersion = cbor.value(QStringLiteral("mne_cpp_version")).toString();
301 p.qtVersion = cbor.value(QStringLiteral("qt_version")).toString();
302 p.compilerInfo = cbor.value(QStringLiteral("compiler_info")).toString();
303 p.osInfo = cbor.value(QStringLiteral("os_info")).toString();
304 p.hostName = cbor.value(QStringLiteral("host_name")).toString();
305 p.externalToolVersion = cbor.value(QStringLiteral("external_tool_version")).toString();
306
307 p.startedAt = QDateTime::fromString(cbor.value(QStringLiteral("started_at")).toString(), Qt::ISODateWithMs);
308 p.finishedAt = QDateTime::fromString(cbor.value(QStringLiteral("finished_at")).toString(), Qt::ISODateWithMs);
309 p.wallTimeMs = cbor.value(QStringLiteral("wall_time_ms")).toInteger(0);
310 p.peakMemoryBytes = cbor.value(QStringLiteral("peak_memory_bytes")).toInteger(0);
311 p.randomSeed = cbor.value(QStringLiteral("random_seed")).toInteger(-1);
312
313 return p;
314}
315
316//=============================================================================================================
317// MnaVerification
318//=============================================================================================================
319
320QJsonObject MnaVerification::toJson() const
321{
322 QJsonObject json;
323
324 if (!explanation.isEmpty()) {
325 json[QStringLiteral("explanation")] = explanation;
326 }
327
328 if (!checks.isEmpty()) {
329 QJsonArray arr;
330 for (const MnaVerificationCheck& c : checks) {
331 arr.append(c.toJson());
332 }
333 json[QStringLiteral("checks")] = arr;
334 }
335
336 if (!preResults.isEmpty()) {
337 QJsonArray arr;
338 for (const MnaVerificationResult& r : preResults) {
339 arr.append(r.toJson());
340 }
341 json[QStringLiteral("pre_results")] = arr;
342 }
343
344 if (!postResults.isEmpty()) {
345 QJsonArray arr;
346 for (const MnaVerificationResult& r : postResults) {
347 arr.append(r.toJson());
348 }
349 json[QStringLiteral("post_results")] = arr;
350 }
351
352 QJsonObject provObj = provenance.toJson();
353 if (!provObj.isEmpty()) {
354 json[QStringLiteral("provenance")] = provObj;
355 }
356
357 return json;
358}
359
360//=============================================================================================================
361
363{
365
366 v.explanation = json.value(QStringLiteral("explanation")).toString();
367
368 const QJsonArray checksArr = json.value(QStringLiteral("checks")).toArray();
369 for (const QJsonValue& val : checksArr) {
370 v.checks.append(MnaVerificationCheck::fromJson(val.toObject()));
371 }
372
373 const QJsonArray preArr = json.value(QStringLiteral("pre_results")).toArray();
374 for (const QJsonValue& val : preArr) {
375 v.preResults.append(MnaVerificationResult::fromJson(val.toObject()));
376 }
377
378 const QJsonArray postArr = json.value(QStringLiteral("post_results")).toArray();
379 for (const QJsonValue& val : postArr) {
380 v.postResults.append(MnaVerificationResult::fromJson(val.toObject()));
381 }
382
383 if (json.contains(QStringLiteral("provenance"))) {
384 v.provenance = MnaProvenance::fromJson(json.value(QStringLiteral("provenance")).toObject());
385 }
386
387 return v;
388}
389
390//=============================================================================================================
391
393{
394 QCborMap cbor;
395
396 if (!explanation.isEmpty()) {
397 cbor.insert(QStringLiteral("explanation"), explanation);
398 }
399
400 if (!checks.isEmpty()) {
401 QCborArray arr;
402 for (const MnaVerificationCheck& c : checks) {
403 arr.append(c.toCbor());
404 }
405 cbor.insert(QStringLiteral("checks"), arr);
406 }
407
408 if (!preResults.isEmpty()) {
409 QCborArray arr;
410 for (const MnaVerificationResult& r : preResults) {
411 arr.append(r.toCbor());
412 }
413 cbor.insert(QStringLiteral("pre_results"), arr);
414 }
415
416 if (!postResults.isEmpty()) {
417 QCborArray arr;
418 for (const MnaVerificationResult& r : postResults) {
419 arr.append(r.toCbor());
420 }
421 cbor.insert(QStringLiteral("post_results"), arr);
422 }
423
424 QCborMap provMap = provenance.toCbor();
425 if (!provMap.isEmpty()) {
426 cbor.insert(QStringLiteral("provenance"), provMap);
427 }
428
429 return cbor;
430}
431
432//=============================================================================================================
433
435{
437
438 v.explanation = cbor.value(QStringLiteral("explanation")).toString();
439
440 const QCborArray checksArr = cbor.value(QStringLiteral("checks")).toArray();
441 for (const QCborValue& val : checksArr) {
442 v.checks.append(MnaVerificationCheck::fromCbor(val.toMap()));
443 }
444
445 const QCborArray preArr = cbor.value(QStringLiteral("pre_results")).toArray();
446 for (const QCborValue& val : preArr) {
447 v.preResults.append(MnaVerificationResult::fromCbor(val.toMap()));
448 }
449
450 const QCborArray postArr = cbor.value(QStringLiteral("post_results")).toArray();
451 for (const QCborValue& val : postArr) {
452 v.postResults.append(MnaVerificationResult::fromCbor(val.toMap()));
453 }
454
455 if (cbor.contains(QStringLiteral("provenance"))) {
456 v.provenance = MnaProvenance::fromCbor(cbor.value(QStringLiteral("provenance")).toMap());
457 }
458
459 return v;
460}
Verification, declarative checks, evaluation results and provenance snapshot attached to every MnaNod...
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)