@hestia-earth/ui-components 0.42.9 → 0.42.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -9018,6 +9018,8 @@ const parseLookup = ({ column, termid, 'term.id': _termid, [missingLookupPrefix]
|
|
|
9018
9018
|
});
|
|
9019
9019
|
const hasValue = (value) => !!value && value !== noValue;
|
|
9020
9020
|
const parseFilename = (filepath) => {
|
|
9021
|
+
if (!filepath)
|
|
9022
|
+
return '';
|
|
9021
9023
|
const [filename] = filepath.split('.');
|
|
9022
9024
|
const ext = termTypes.includes(filename) ? SupportedExtensions.xlsx : SupportedExtensions.csv;
|
|
9023
9025
|
return fileToExt(filename, ext);
|
|
@@ -9240,9 +9242,15 @@ const jLogEntryToLog = (entry) => {
|
|
|
9240
9242
|
const { model, orchestrator, should_run, run_required, methodTier, model_key, is_not_relevant, missingLookup, ...rest } = entry;
|
|
9241
9243
|
const logs = pickFields(rest, key => modelLogKeys.includes(key));
|
|
9242
9244
|
const requirements = pickFields(rest, key => !modelLogKeys.includes(key));
|
|
9243
|
-
// the `.jlog` lists missing lookups per entry
|
|
9244
|
-
// "Data missing (might be optional)"
|
|
9245
|
-
const missingLookups = Array.isArray(missingLookup)
|
|
9245
|
+
// the `.jlog` lists missing lookups per entry (`{ lookup, row, row_value, column }`); map them to the
|
|
9246
|
+
// shared "Data missing (might be optional)" shape, reusing the legacy filename parser
|
|
9247
|
+
const missingLookups = Array.isArray(missingLookup)
|
|
9248
|
+
? missingLookup.map(({ lookup, row_value, column }) => ({
|
|
9249
|
+
filename: parseFilename(lookup),
|
|
9250
|
+
termId: row_value,
|
|
9251
|
+
column
|
|
9252
|
+
}))
|
|
9253
|
+
: [];
|
|
9246
9254
|
return {
|
|
9247
9255
|
// keep the untouched `.jlog` entry so the popover can show the raw JSON
|
|
9248
9256
|
raw: entry,
|
|
@@ -9354,6 +9362,10 @@ const identitySpec = (type, nodeType, nodeKey) => {
|
|
|
9354
9362
|
const properties = [...new Set([...(def.properties || []), ...parentFields])].filter((field) => !excludedIdentityFields.includes(field) && !relations.includes(field.split('.')[0]));
|
|
9355
9363
|
return { def, properties, relations };
|
|
9356
9364
|
};
|
|
9365
|
+
// some node keys distinguish blank nodes by their recorded `model` (e.g. an Indicator's `methodModel.@id`
|
|
9366
|
+
// is a uniqueness field): models that ran in parallel for the same term's value are then separate rows
|
|
9367
|
+
// (one per model), not extra model columns on a single row
|
|
9368
|
+
const modelIsUniquenessField = (spec) => spec.properties.includes('methodModel.@id');
|
|
9357
9369
|
const isPresent = (value) => value !== undefined && value !== null && value !== '';
|
|
9358
9370
|
// the joined uniqueness-field ids of each item in a relation (e.g. an emission's `inputs[].@id`)
|
|
9359
9371
|
const relationItemIds = (items, fields) => Array.isArray(items)
|
|
@@ -9663,7 +9675,10 @@ const mapValueModel = (models, valueModelId, condition, update) => models.map(mo
|
|
|
9663
9675
|
* When the value came from the original (no model, no config), the row shows no model.
|
|
9664
9676
|
*/
|
|
9665
9677
|
// eslint-disable-next-line complexity -- orchestrates the value-model resolution (delegated to helpers)
|
|
9666
|
-
const valueModelRuns = (entry, value, originalValue, nodeType, termId, nodeKey, config
|
|
9678
|
+
const valueModelRuns = (entry, value, originalValue, nodeType, termId, nodeKey, config,
|
|
9679
|
+
// when the recorded `model` is a uniqueness field, parallel siblings are their own rows (see
|
|
9680
|
+
// `parallelSiblingRows`), so they are dropped from this value row's models
|
|
9681
|
+
splitParallelModels = false) => {
|
|
9667
9682
|
const valueModelId = blankNodeModelId(value);
|
|
9668
9683
|
const isRecalculated = isRecalculatedValue(value, originalValue);
|
|
9669
9684
|
const hasOriginal = !!originalValue && typeof originalValue.value !== 'undefined';
|
|
@@ -9677,9 +9692,21 @@ const valueModelRuns = (entry, value, originalValue, nodeType, termId, nodeKey,
|
|
|
9677
9692
|
const isValueLog = (log) => !isBackgroundLog(log) && !isSubRowOrchestrator(log?.orchestrator?.value, subRowValues);
|
|
9678
9693
|
const valueLog = selectValueLog(logs, valueModelId, isValueLog, isRecalculated);
|
|
9679
9694
|
const valueOrchestrator = valueLog?.orchestrator?.value;
|
|
9680
|
-
const
|
|
9681
|
-
|
|
9682
|
-
|
|
9695
|
+
const matchesValueOrchestrator = (log) => log?.orchestrator?.value === valueOrchestrator && !isBackgroundLog(log);
|
|
9696
|
+
// keep the parallel grouping (a nested `logs` array): parallel siblings are independent, so a model
|
|
9697
|
+
// that did not run failed rather than being "skipped" by a sibling that ran (a higher-tier hierarchy
|
|
9698
|
+
// model). `jLogModelColumns` applies the pre-group success state to each parallel sibling; flatten the
|
|
9699
|
+
// resulting columns back to the flat run list this function returns
|
|
9700
|
+
const valueLogSteps = (entry?.logs || [])
|
|
9701
|
+
.map((step) => Array.isArray(step) ? step.filter(matchesValueOrchestrator) : matchesValueOrchestrator(step) ? step : undefined)
|
|
9702
|
+
.filter((step) => (Array.isArray(step) ? step.length > 0 : !!step));
|
|
9703
|
+
const allLogModels = valueLog ? flattenColumns(jLogModelColumns(valueLogSteps, nodeType, termId, false)) : [];
|
|
9704
|
+
// when models are split into their own rows, this value row keeps only its own model (parallel siblings
|
|
9705
|
+
// become separate rows); otherwise parallel siblings stay as fallback/sibling columns on this row
|
|
9706
|
+
const siblingIds = splitParallelModels
|
|
9707
|
+
? new Set(parallelSiblingLogs(entry, valueModelId).map((log) => log.model))
|
|
9708
|
+
: new Set();
|
|
9709
|
+
const rawLogModels = allLogModels.filter(model => !siblingIds.has(model.methodId));
|
|
9683
9710
|
// the recorded value model (the blank node `methodModel`, e.g. `hestiaAggregatedData`) can differ from
|
|
9684
9711
|
// the single orchestrator model in the logs (e.g. `linkedImpactAssessment`); relabel that run to the
|
|
9685
9712
|
// recorded model so it shows under the expected name, but keep the orchestrator run's logs and its
|
|
@@ -9770,6 +9797,22 @@ const allSubRows = (entry, value, originalValue, nodeType, type, termId, getTerm
|
|
|
9770
9797
|
];
|
|
9771
9798
|
};
|
|
9772
9799
|
const flattenColumns = (columns) => columns.flatMap(column => (Array.isArray(column) ? column : [column]));
|
|
9800
|
+
// the distinct models (other than the value's own `model`) that ran in parallel with it - a nested
|
|
9801
|
+
// `logs` group holding more than one model. When `model` is a uniqueness field each becomes its own row,
|
|
9802
|
+
// so they are split off the value row (a sibling that did not win is its own failed/separate run, not a
|
|
9803
|
+
// fallback column). Keeps the first log per model.
|
|
9804
|
+
const parallelSiblingLogs = (entry, valueModelId) => {
|
|
9805
|
+
const parallelGroups = (entry?.logs || []).filter((step) => Array.isArray(step) && new Set(step.filter((e) => !!e?.model).map((e) => e.model)).size > 1);
|
|
9806
|
+
const byModel = new Map();
|
|
9807
|
+
parallelGroups
|
|
9808
|
+
.flat()
|
|
9809
|
+
.filter((log) => !!log?.model && log.model !== valueModelId)
|
|
9810
|
+
.forEach((log) => {
|
|
9811
|
+
if (!byModel.has(log.model))
|
|
9812
|
+
byModel.set(log.model, log);
|
|
9813
|
+
});
|
|
9814
|
+
return [...byModel.values()];
|
|
9815
|
+
};
|
|
9773
9816
|
// a `<key>-failed` entry has no value: its identity/sub-rows are empty and the models that tried are
|
|
9774
9817
|
// errors; parallel models (a nested `logs` array) are kept grouped (`modelColumns`) so they stack in one
|
|
9775
9818
|
// column, while `models` stays flat for the status checks
|
|
@@ -9799,7 +9842,7 @@ const valueBlankNodeContent = (entry, value, originalValue, termId, { nodeType,
|
|
|
9799
9842
|
subRows: allSubRows(entry, value, originalValue, nodeType, type, termId, getTerm || defaultGetTerm),
|
|
9800
9843
|
isRecalculated: isRecalculatedValue(value, originalValue),
|
|
9801
9844
|
recalculated: value ? [value] : [],
|
|
9802
|
-
models: valueModelRuns(entry, value, originalValue, nodeType, termId, nodeKey, config)
|
|
9845
|
+
models: valueModelRuns(entry, value, originalValue, nodeType, termId, nodeKey, config, modelIsUniquenessField(spec))
|
|
9803
9846
|
};
|
|
9804
9847
|
};
|
|
9805
9848
|
const buildBlankNode = (entry, value, originalValue, index, term, isFailed, context) => {
|
|
@@ -9821,6 +9864,42 @@ const buildBlankNode = (entry, value, originalValue, index, term, isFailed, cont
|
|
|
9821
9864
|
: valueBlankNodeContent(entry, value, originalValue, termId, context))
|
|
9822
9865
|
};
|
|
9823
9866
|
};
|
|
9867
|
+
// a parallel model that ran for a term's value but did not produce its own blank node (e.g. it failed,
|
|
9868
|
+
// while a sibling set the value). When `model` is a uniqueness field it is shown as its own row -
|
|
9869
|
+
// distinguished by its `methodModel`, with no value and a single model column carrying its status
|
|
9870
|
+
const buildParallelSiblingRow = (log, term, index, context) => {
|
|
9871
|
+
const termId = term['@id'];
|
|
9872
|
+
const run = toModelRun(log, jLogModelStatus(log, false, false), context.nodeType, termId);
|
|
9873
|
+
// carry the model as a `methodModel` Term so the row is distinguished by a methodModel link (like a
|
|
9874
|
+
// value row), resolved through the same `getTerm` the component uses for names; it has no `value`, so
|
|
9875
|
+
// it contributes nothing to the group's combined value
|
|
9876
|
+
const methodModel = context.getTerm?.(log.model) ?? { '@type': NodeType.Term, '@id': log.model };
|
|
9877
|
+
const syntheticValue = { '@type': context.type, term, methodModel };
|
|
9878
|
+
return {
|
|
9879
|
+
index,
|
|
9880
|
+
term,
|
|
9881
|
+
termId,
|
|
9882
|
+
type: context.type,
|
|
9883
|
+
originalValue: null,
|
|
9884
|
+
isOriginal: false,
|
|
9885
|
+
isRequired: true,
|
|
9886
|
+
isFailed: false,
|
|
9887
|
+
original: [],
|
|
9888
|
+
recalculatedValue: null,
|
|
9889
|
+
identity: { 'methodModel.@id': log.model },
|
|
9890
|
+
identityLabel: '',
|
|
9891
|
+
identitySegments: [],
|
|
9892
|
+
isRecalculated: false,
|
|
9893
|
+
recalculated: [syntheticValue],
|
|
9894
|
+
subRows: [],
|
|
9895
|
+
models: [run],
|
|
9896
|
+
modelColumns: [run],
|
|
9897
|
+
// `allSucceeded` is recomputed by `withDisplayValues` at the end of the pipeline
|
|
9898
|
+
allSucceeded: false,
|
|
9899
|
+
canOpen: false,
|
|
9900
|
+
isOpen: false
|
|
9901
|
+
};
|
|
9902
|
+
};
|
|
9824
9903
|
/**
|
|
9825
9904
|
* Group the `.jlog` logs for one node key into rows, one per term, keeping every blank node distinct.
|
|
9826
9905
|
*
|
|
@@ -9845,15 +9924,36 @@ const groupJLogByTerm = (jlog, nodeKey, recalculatedValues = [], originalValues
|
|
|
9845
9924
|
const key = identityKey(blankNodeIdentity(value, spec));
|
|
9846
9925
|
return (originalValues || []).find(original => original.term?.['@id'] === value.term?.['@id'] && identityKey(blankNodeIdentity(original, spec)) === key);
|
|
9847
9926
|
};
|
|
9927
|
+
// each recalculated blank node, attaching its `.jlog` logs when present (a node may exist without logs,
|
|
9928
|
+
// e.g. a value provided in the original)
|
|
9929
|
+
const valueEntries = (recalculatedValues || [])
|
|
9930
|
+
.map((value, index) => ({ value, index, term: value?.term }))
|
|
9931
|
+
.filter(({ term }) => !!term?.['@id']);
|
|
9932
|
+
const valueNodes = valueEntries.map(({ value, index, term }) => buildBlankNode(keyLogs[index] || {}, value, matchOriginal(value), index, term, false, context));
|
|
9933
|
+
// when the recorded `model` is a uniqueness field, models that ran in parallel for a term's value but
|
|
9934
|
+
// produced no blank node (e.g. a sibling that failed while another set the value) are shown as their
|
|
9935
|
+
// own rows - skipping any model already represented by a value row for that term (avoiding duplicates)
|
|
9936
|
+
const seenModelsByTerm = new Map();
|
|
9937
|
+
valueEntries.forEach(({ value, term }) => {
|
|
9938
|
+
const set = seenModelsByTerm.get(term['@id']) ?? new Set();
|
|
9939
|
+
const modelId = blankNodeModelId(value);
|
|
9940
|
+
if (modelId)
|
|
9941
|
+
set.add(modelId);
|
|
9942
|
+
seenModelsByTerm.set(term['@id'], set);
|
|
9943
|
+
});
|
|
9944
|
+
let siblingIndex = recalculatedValues?.length ?? 0;
|
|
9945
|
+
const siblingNodes = modelIsUniquenessField(spec)
|
|
9946
|
+
? valueEntries.flatMap(({ value, index, term }) => parallelSiblingLogs(keyLogs[index] || {}, blankNodeModelId(value)).flatMap(log => {
|
|
9947
|
+
const seen = seenModelsByTerm.get(term['@id']);
|
|
9948
|
+
if (seen?.has(log.model))
|
|
9949
|
+
return [];
|
|
9950
|
+
seen?.add(log.model);
|
|
9951
|
+
return [buildParallelSiblingRow(log, term, siblingIndex++, context)];
|
|
9952
|
+
}))
|
|
9953
|
+
: [];
|
|
9848
9954
|
const blankNodes = [
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
...(recalculatedValues || []).map((value, index) => {
|
|
9852
|
-
const term = value?.term;
|
|
9853
|
-
return term?.['@id']
|
|
9854
|
-
? buildBlankNode(keyLogs[index] || {}, value, matchOriginal(value), index, term, false, context)
|
|
9855
|
-
: null;
|
|
9856
|
-
}),
|
|
9955
|
+
...valueNodes,
|
|
9956
|
+
...siblingNodes,
|
|
9857
9957
|
...Object.entries(failedLogs).map(([termId, entry], i) => buildBlankNode(entry, undefined, undefined, -1 - i, getTerm(termId), true, context))
|
|
9858
9958
|
].filter(Boolean);
|
|
9859
9959
|
return orderGroups(groupBlankNodesByTerm(blankNodes).map(withCollapsible).map(withRowLabels).map(withDisplayValues), nodeKey);
|
|
@@ -10081,7 +10181,9 @@ class NodeJLogModelsComponent {
|
|
|
10081
10181
|
if (!this.isBlankNodes()) {
|
|
10082
10182
|
return groupJLogByField(this.scopedJlog(), this.nodeKey(), this.originalValues(), this.recalculatedValues(), this.nodeType());
|
|
10083
10183
|
}
|
|
10084
|
-
const groups = groupJLogByTerm(this.scopedJlog(), this.nodeKey(), this.recalculatedValues(), this.originalValues(), this.nodeType(), id => this.failedTerms()[id] ||
|
|
10184
|
+
const groups = groupJLogByTerm(this.scopedJlog(), this.nodeKey(), this.recalculatedValues(), this.originalValues(), this.nodeType(), id => this.failedTerms()[id] ||
|
|
10185
|
+
this.methodsById()?.[id] ||
|
|
10186
|
+
{ '@type': NodeType.Term, '@id': id, name: termTypeLabel(id) }, this.config());
|
|
10085
10187
|
// a combined node key (e.g. `emissionsResourceUse`) holds several term types; keep only the requested
|
|
10086
10188
|
// ones (like the legacy `computeTerms`), keeping terms whose type is not yet resolved
|
|
10087
10189
|
const termTypes = this.filterTermTypes();
|