@hestia-earth/ui-components 0.43.15 → 0.43.16
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.
- package/fesm2022/hestia-earth-ui-components-file-errors.mjs +6 -1
- package/fesm2022/hestia-earth-ui-components-file-errors.mjs.map +1 -1
- package/fesm2022/hestia-earth-ui-components.mjs +109 -14
- package/fesm2022/hestia-earth-ui-components.mjs.map +1 -1
- package/package.json +1 -1
- package/types/hestia-earth-ui-components.d.ts +24 -1
|
@@ -21,6 +21,8 @@ import modelGroups from '@hestia-earth/glossary/resources/modelGroups.json';
|
|
|
21
21
|
import inHestiaDefaultSystemBoundary from '@hestia-earth/glossary/resources/inHestiaDefaultSystemBoundary.json';
|
|
22
22
|
import plantationIds from '@hestia-earth/glossary/resources/isPlantation.json';
|
|
23
23
|
import backgroundEmissionTermsByModel from '@hestia-earth/glossary/resources/backgroundEmissionTermsByModel.json';
|
|
24
|
+
import sumIs100Group from '@hestia-earth/glossary/resources/sumIs100Group.json';
|
|
25
|
+
import sumMax100Group from '@hestia-earth/glossary/resources/sumMax100Group.json';
|
|
24
26
|
import isEqual$1 from 'lodash.isequal';
|
|
25
27
|
import { DataState, filenameWithoutExt, nodeTypeToParam, allowedDataStates, SupportedExtensions, fileToExt, fileExt, maxFileSizeMb } from '@hestia-earth/api';
|
|
26
28
|
import { models as models$1, loadConfig, getFormulas, renderFormula, getMaxStage } from '@hestia-earth/engine-models';
|
|
@@ -935,6 +937,14 @@ const backgroundEmissionModels = Object.entries(backgroundEmissionTermsByModel).
|
|
|
935
937
|
* `model-links.json` links those per Term.
|
|
936
938
|
*/
|
|
937
939
|
const getBackgroundEmissionModels = (id) => backgroundEmissionModels[id] ?? [];
|
|
940
|
+
// both resources map a Term to the group it is rescaled within, and no Term is in both
|
|
941
|
+
const sumGroups = [sumIs100Group, sumMax100Group];
|
|
942
|
+
/**
|
|
943
|
+
* The group the Term's value is rescaled within, e.g. `land-cover` or `crop-residue`.
|
|
944
|
+
*
|
|
945
|
+
* The aggregation reads whichever of the two lookups the Term carries, so both answer here.
|
|
946
|
+
*/
|
|
947
|
+
const getSumGroup = (id) => sumGroups.map(lookup => lookup[id]).find(group => !!group);
|
|
938
948
|
|
|
939
949
|
const maxAreaSize = 5000;
|
|
940
950
|
const siteTooBig = ({ area }) => area && area / 100 > maxAreaSize;
|
|
@@ -8213,8 +8223,8 @@ var LogStatus;
|
|
|
8213
8223
|
LogStatus["dataProvided"] = "not run (user provided data retained)";
|
|
8214
8224
|
LogStatus["notRequired"] = "not relevant";
|
|
8215
8225
|
})(LogStatus || (LogStatus = {}));
|
|
8216
|
-
const hasLogs = (logs) => !!logs?.requirements || !!logs?.logs || logs?.missingLookups?.length > 0;
|
|
8217
|
-
const hasLogDetails = (status, logs) => [LogStatus.success, LogStatus.error, LogStatus.dataProvided].includes(status) && hasLogs(logs);
|
|
8226
|
+
const hasLogs$1 = (logs) => !!logs?.requirements || !!logs?.logs || logs?.missingLookups?.length > 0;
|
|
8227
|
+
const hasLogDetails = (status, logs) => [LogStatus.success, LogStatus.error, LogStatus.dataProvided].includes(status) && hasLogs$1(logs);
|
|
8218
8228
|
const isRecalculatedByModel = (data, model) => 'recalculated' in data && data.recalculated?.some(value => blankNodeModelId$1(value) === model.methodId);
|
|
8219
8229
|
const logStatus = (data, logs, model, hasPreviousSuccess = false) => {
|
|
8220
8230
|
const termLogs = logs?.[model.methodId];
|
|
@@ -11850,6 +11860,18 @@ const WEIGHT_SYMBOLS = ['\\hat{w}_i'];
|
|
|
11850
11860
|
const formulaGroup = (formula) => formula.bindings.some(({ key, symbol }) => WEIGHT_KEYS.includes(key ?? '') || WEIGHT_SYMBOLS.includes(symbol ?? ''))
|
|
11851
11861
|
? 'weights'
|
|
11852
11862
|
: 'value';
|
|
11863
|
+
// The documentation writes each mean twice, and only one of them ran for a given value: the one
|
|
11864
|
+
// counting contributors binds a `<table>_count`, the one weighting them reads a table's `weight`
|
|
11865
|
+
// column - which is what keeps the economic value share rescale, also a division by a total
|
|
11866
|
+
// weight, out of the pair.
|
|
11867
|
+
const COUNT_KEYS = ['weights_count', 'reporting_count', 'zero_filled_count'];
|
|
11868
|
+
const WEIGHT_COLUMN = 'weight';
|
|
11869
|
+
const isSimpleMean = (formula) => formula.bindings.some(({ key }) => COUNT_KEYS.includes(key ?? ''));
|
|
11870
|
+
const isWeightedMean = (formula) => formula.bindings.some(({ column }) => column === WEIGHT_COLUMN);
|
|
11871
|
+
const isMeanThatRan = (formula, weighted) => weighted ? !isSimpleMean(formula) : !isWeightedMean(formula);
|
|
11872
|
+
// dividing a shared weight between the Cycles that share it, which is only a step where the
|
|
11873
|
+
// contributors carry weights at all
|
|
11874
|
+
const isWeightNormalisation = (formula) => formula.bindings.some(({ symbol }) => WEIGHT_SYMBOLS.includes(symbol ?? ''));
|
|
11853
11875
|
/**
|
|
11854
11876
|
* A symbol that should carry a value: the result and the inputs, but never a fixed constant or a
|
|
11855
11877
|
* quantity the aggregation deliberately does not store.
|
|
@@ -11969,6 +11991,51 @@ const contributorsTable = (values, formulas) => {
|
|
|
11969
11991
|
}));
|
|
11970
11992
|
return { columns, rows, count: tableCount(values, key) };
|
|
11971
11993
|
};
|
|
11994
|
+
// the completeness area a term belongs to, as the aggregation recorded it. A term covered by
|
|
11995
|
+
// several areas records them joined, and all of them must be complete
|
|
11996
|
+
const COMPLETENESS_KEY = 'completeness_key';
|
|
11997
|
+
const COMPLETENESS_JOIN = '-';
|
|
11998
|
+
// the glossaries whose terms are zero-filled from the glossary defaults rather than from a group:
|
|
11999
|
+
// a Cycle that does not report `Organic` is not organic, rather than silent about it
|
|
12000
|
+
const DEFAULT_ZERO_TERM_TYPES = [TermTermType.system, TermTermType.standardsLabels];
|
|
12001
|
+
const completenessAreas = (values) => {
|
|
12002
|
+
const key = values[COMPLETENESS_KEY];
|
|
12003
|
+
return typeof key === 'string' && key ? key.split(COMPLETENESS_JOIN) : [];
|
|
12004
|
+
};
|
|
12005
|
+
/**
|
|
12006
|
+
* Whether the aggregation recorded anything for this data item. Without it, "no completeness area"
|
|
12007
|
+
* cannot be told apart from "the aggregation logged nothing", so only the rules a Term answers on
|
|
12008
|
+
* its own are stated.
|
|
12009
|
+
*/
|
|
12010
|
+
const hasLogs = (values) => !isMissing(values['value']);
|
|
12011
|
+
const completenessRule = (areas) => ({
|
|
12012
|
+
included: `Only Cycles with ${areas.map(area => `completeness.${area}`).join(' and ')} = TRUE.`,
|
|
12013
|
+
blank: `A Cycle complete for ${areas.join(' and ')} that does not report this term counts as 0.`
|
|
12014
|
+
});
|
|
12015
|
+
const groupRule = (group) => ({
|
|
12016
|
+
included: `Only Cycles where the terms of the ${group} group sum to 100.`,
|
|
12017
|
+
blank: 'A term of that group a Cycle does not report counts as 0.'
|
|
12018
|
+
});
|
|
12019
|
+
const ZERO_FILLED_RULE = {
|
|
12020
|
+
included: 'Every Cycle is included.',
|
|
12021
|
+
blank: 'A Cycle that does not report this term counts as 0.'
|
|
12022
|
+
};
|
|
12023
|
+
const SKIPPED_RULE = {
|
|
12024
|
+
included: 'Only Cycles that report this term.',
|
|
12025
|
+
blank: 'A Cycle that does not report it is skipped, rather than counted as 0.'
|
|
12026
|
+
};
|
|
12027
|
+
// the rules a Term answers on its own, so they hold for an aggregation that logged nothing: the
|
|
12028
|
+
// group its value is rescaled within, then the glossaries zero-filled from their defaults
|
|
12029
|
+
const termRule = (term) => {
|
|
12030
|
+
const group = getSumGroup(term?.['@id'] ?? '');
|
|
12031
|
+
const zeroFilled = DEFAULT_ZERO_TERM_TYPES.includes(term?.termType ?? '');
|
|
12032
|
+
return group ? groupRule(group) : zeroFilled ? ZERO_FILLED_RULE : undefined;
|
|
12033
|
+
};
|
|
12034
|
+
const inclusionRule = (term, values) => {
|
|
12035
|
+
const areas = completenessAreas(values);
|
|
12036
|
+
const fallback = hasLogs(values) ? SKIPPED_RULE : undefined;
|
|
12037
|
+
return areas.length ? completenessRule(areas) : (termRule(term) ?? fallback);
|
|
12038
|
+
};
|
|
11972
12039
|
|
|
11973
12040
|
// The page every aggregation follows, whatever the product.
|
|
11974
12041
|
const GENERAL_PAGE = 'general-process';
|
|
@@ -12029,6 +12096,11 @@ class NodeAggregatedFormulasComponent {
|
|
|
12029
12096
|
* every rule; set it and only the rules that apply to that kind of data item are kept.
|
|
12030
12097
|
*/
|
|
12031
12098
|
this.nodeKey = input(...(ngDevMode ? [undefined, { debugName: "nodeKey" }] : []));
|
|
12099
|
+
/**
|
|
12100
|
+
* The Term of the data item shown, which decides how a Cycle reporting no value for it was
|
|
12101
|
+
* counted: some are zero-filled, others skipped.
|
|
12102
|
+
*/
|
|
12103
|
+
this.term = input(...(ngDevMode ? [undefined, { debugName: "term" }] : []));
|
|
12032
12104
|
/**
|
|
12033
12105
|
* The quantities the formulas bind to, for this data item. Supplied by the caller from the
|
|
12034
12106
|
* node's `.jlog` entries recorded by the aggregation (`model: "aggregation"`). Leave it empty
|
|
@@ -12057,6 +12129,15 @@ class NodeAggregatedFormulasComponent {
|
|
|
12057
12129
|
const productPage = PRODUCT_PAGES[this.termType() ?? ''];
|
|
12058
12130
|
return [GENERAL_PAGE, ...(productPage ? [productPage] : [])];
|
|
12059
12131
|
}, ...(ngDevMode ? [{ debugName: "pages" }] : []));
|
|
12132
|
+
/**
|
|
12133
|
+
* Whether the values were combined by a weighting structure rather than counted equally, which
|
|
12134
|
+
* decides which of the two means is shown. Only `crop` aggregations are split into sub-systems, so
|
|
12135
|
+
* every other product type combines its Cycles unweighted however it was built.
|
|
12136
|
+
*/
|
|
12137
|
+
this.weighted = computed(() => this.worldAggregation() ||
|
|
12138
|
+
isPlantation(this.termId() ?? '') ||
|
|
12139
|
+
(this.termType() === TermTermType.crop && !this.subAggregation()), ...(ngDevMode ? [{ debugName: "weighted" }] : []));
|
|
12140
|
+
this.inclusion = computed(() => inclusionRule(this.term(), this.values()), ...(ngDevMode ? [{ debugName: "inclusion" }] : []));
|
|
12060
12141
|
// whether any symbol actually resolves; when none does, the substituted view would be identical
|
|
12061
12142
|
// to the symbolic one, so the toggle is disabled rather than silently doing nothing
|
|
12062
12143
|
this.hasSubstitutions = computed(() => {
|
|
@@ -12130,7 +12211,8 @@ class NodeAggregatedFormulasComponent {
|
|
|
12130
12211
|
const stages = [
|
|
12131
12212
|
{ applies: isWorldWeight, ran: () => this.worldAggregation() },
|
|
12132
12213
|
{ applies: isSubSystemWeight, ran: () => this.subAggregation() },
|
|
12133
|
-
{ applies: isPlantationWeight, ran: () => isPlantation(this.termId() ?? '') }
|
|
12214
|
+
{ applies: isPlantationWeight, ran: () => isPlantation(this.termId() ?? '') },
|
|
12215
|
+
{ applies: isWeightNormalisation, ran: () => this.weighted() }
|
|
12134
12216
|
];
|
|
12135
12217
|
return stages.find(({ applies }) => applies(formula))?.ran() ?? true;
|
|
12136
12218
|
}
|
|
@@ -12149,10 +12231,12 @@ class NodeAggregatedFormulasComponent {
|
|
|
12149
12231
|
return true;
|
|
12150
12232
|
}
|
|
12151
12233
|
applies(formula) {
|
|
12152
|
-
return this.ranForAggregation(formula) &&
|
|
12234
|
+
return (this.ranForAggregation(formula) &&
|
|
12235
|
+
this.appliesToNodeKey(formula) &&
|
|
12236
|
+
isMeanThatRan(formula, this.weighted()));
|
|
12153
12237
|
}
|
|
12154
12238
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregatedFormulasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
12155
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregatedFormulasComponent, isStandalone: true, selector: "he-node-aggregated-formulas", inputs: { termType: { classPropertyName: "termType", publicName: "termType", isSignal: true, isRequired: false, transformFunction: null }, termId: { classPropertyName: "termId", publicName: "termId", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, worldAggregation: { classPropertyName: "worldAggregation", publicName: "worldAggregation", isSignal: true, isRequired: false, transformFunction: null }, subAggregation: { classPropertyName: "subAggregation", publicName: "subAggregation", isSignal: true, isRequired: false, transformFunction: null }, substituted: { classPropertyName: "substituted", publicName: "substituted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { substituted: "substitutedChange" }, ngImport: i0, template: "@if (renderedSections().length) {\n <div class=\"aggregated-formulas\">\n <p class=\"is-size-7 is-mb-2 | formula-intro\">\n Aggregated values are calculated from the underlying {{ contributor().group }}, not measured. These are the rules\n that produced this one, in the order they are applied.\n </p>\n\n <div class=\"is-pt-2 is-pr-1 is-overflow-y-auto | aggregated-formulas-rules\">\n @for (section of renderedSections(); track section.id; let first = $first) {\n <he-formula-block\n [formulas]=\"section.formulas\"\n [heading]=\"section.heading\"\n [collapsible]=\"true\"\n [showToggle]=\"first\"\n [hasSubstitutions]=\"hasSubstitutions()\"\n [(substituted)]=\"substituted\"\n emptyTitle=\"No recorded values to substitute\" />\n\n <!-- the contributors sit between the two calculations: what went in, above how a weight is derived -->\n @if (section.id === 'value' && contributors(); as table) {\n <he-collapsible-block [heading]=\"contributor().heading\">\n @if (table.rows.length) {\n <table class=\"table is-narrow is-size-7 w-100 | contributors-table\">\n <thead>\n <tr>\n <th>{{ contributor().row }}</th>\n @for (column of table.columns; track column.symbol) {\n <th class=\"has-text-right\">\n <span [heKatex]=\"column.symbol\" [heKatexInline]=\"true\"></span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of table.rows; track row.id) {\n <tr>\n <td [attr.title]=\"row.id\">\n <he-node-link [node]=\"row.node\" linkClass=\"has-text-white\">\n <span class=\"break-word\">{{ row.label }}</span>\n </he-node-link>\n </td>\n @for (value of row.values; track $index) {\n <td class=\"has-text-right is-nowrap\">{{ value | precision: 4 | default: '-' }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <p class=\"is-size-7\">\n Combined from {{ table.count }} {{ contributor().group }}, too many for the aggregation to record one by\n one.\n </p>\n }\n </he-collapsible-block>\n }\n }\n </div>\n </div>\n}\n", styles: [".formula-intro{opacity:.85}.contributors-table{background-color:transparent;color:inherit}.contributors-table th,.contributors-table td{border-color:#fff3;color:inherit;padding:.15rem .5rem .15rem 0;vertical-align:top}.contributors-table th{opacity:.7}.contributors-table ::ng-deep .katex{font-size:1em}.aggregated-formulas-rules{max-height:50vh}\n"], dependencies: [{ kind: "directive", type: KatexDirective, selector: "[heKatex]", inputs: ["heKatex", "heKatexInline"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: FormulaBlockComponent, selector: "he-formula-block", inputs: ["formulas", "hasSubstitutions", "emptyTitle", "heading", "note", "showToggle", "collapsible", "open", "substituted"], outputs: ["openChange", "substitutedChange"] }, { kind: "component", type: CollapsibleBlockComponent, selector: "he-collapsible-block", inputs: ["heading", "collapsible", "open"], outputs: ["openChange"] }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12239
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregatedFormulasComponent, isStandalone: true, selector: "he-node-aggregated-formulas", inputs: { termType: { classPropertyName: "termType", publicName: "termType", isSignal: true, isRequired: false, transformFunction: null }, termId: { classPropertyName: "termId", publicName: "termId", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, term: { classPropertyName: "term", publicName: "term", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, worldAggregation: { classPropertyName: "worldAggregation", publicName: "worldAggregation", isSignal: true, isRequired: false, transformFunction: null }, subAggregation: { classPropertyName: "subAggregation", publicName: "subAggregation", isSignal: true, isRequired: false, transformFunction: null }, substituted: { classPropertyName: "substituted", publicName: "substituted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { substituted: "substitutedChange" }, ngImport: i0, template: "@if (renderedSections().length) {\n <div class=\"aggregated-formulas\">\n <p class=\"is-size-7 is-mb-2 | formula-intro\">\n Aggregated values are calculated from the underlying {{ contributor().group }}, not measured. These are the rules\n that produced this one, in the order they are applied.\n </p>\n\n <div class=\"is-pt-2 is-pr-1 is-overflow-y-auto | aggregated-formulas-rules\">\n <!-- what went in, before the rules that combine it: the formulas cannot show this -->\n @if (inclusion(); as rule) {\n <he-collapsible-block heading=\"Which Cycles are included\">\n <p class=\"is-size-7\">{{ rule.included }}</p>\n <p class=\"is-size-7\">{{ rule.blank }}</p>\n </he-collapsible-block>\n }\n\n @for (section of renderedSections(); track section.id; let first = $first) {\n <he-formula-block\n [formulas]=\"section.formulas\"\n [heading]=\"section.heading\"\n [collapsible]=\"true\"\n [showToggle]=\"first\"\n [hasSubstitutions]=\"hasSubstitutions()\"\n [(substituted)]=\"substituted\"\n emptyTitle=\"No recorded values to substitute\" />\n\n <!-- the contributors sit between the two calculations: what went in, above how a weight is derived -->\n @if (section.id === 'value' && contributors(); as table) {\n <he-collapsible-block [heading]=\"contributor().heading\">\n @if (table.rows.length) {\n <table class=\"table is-narrow is-size-7 w-100 | contributors-table\">\n <thead>\n <tr>\n <th>{{ contributor().row }}</th>\n @for (column of table.columns; track column.symbol) {\n <th class=\"has-text-right\">\n <span [heKatex]=\"column.symbol\" [heKatexInline]=\"true\"></span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of table.rows; track row.id) {\n <tr>\n <td [attr.title]=\"row.id\">\n <he-node-link [node]=\"row.node\" linkClass=\"has-text-white\">\n <span class=\"break-word\">{{ row.label }}</span>\n </he-node-link>\n </td>\n @for (value of row.values; track $index) {\n <td class=\"has-text-right is-nowrap\">{{ value | precision: 4 | default: '-' }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <p class=\"is-size-7\">\n Combined from {{ table.count }} {{ contributor().group }}, too many for the aggregation to record one by\n one.\n </p>\n }\n </he-collapsible-block>\n }\n }\n </div>\n </div>\n}\n", styles: [".formula-intro{opacity:.85}.contributors-table{background-color:transparent;color:inherit}.contributors-table th,.contributors-table td{border-color:#fff3;color:inherit;padding:.15rem .5rem .15rem 0;vertical-align:top}.contributors-table th{opacity:.7}.contributors-table ::ng-deep .katex{font-size:1em}.aggregated-formulas-rules{max-height:50vh}\n"], dependencies: [{ kind: "directive", type: KatexDirective, selector: "[heKatex]", inputs: ["heKatex", "heKatexInline"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: FormulaBlockComponent, selector: "he-formula-block", inputs: ["formulas", "hasSubstitutions", "emptyTitle", "heading", "note", "showToggle", "collapsible", "open", "substituted"], outputs: ["openChange", "substitutedChange"] }, { kind: "component", type: CollapsibleBlockComponent, selector: "he-collapsible-block", inputs: ["heading", "collapsible", "open"], outputs: ["openChange"] }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12156
12240
|
}
|
|
12157
12241
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregatedFormulasComponent, decorators: [{
|
|
12158
12242
|
type: Component$1,
|
|
@@ -12163,8 +12247,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
12163
12247
|
NodeLinkComponent,
|
|
12164
12248
|
FormulaBlockComponent,
|
|
12165
12249
|
CollapsibleBlockComponent
|
|
12166
|
-
], template: "@if (renderedSections().length) {\n <div class=\"aggregated-formulas\">\n <p class=\"is-size-7 is-mb-2 | formula-intro\">\n Aggregated values are calculated from the underlying {{ contributor().group }}, not measured. These are the rules\n that produced this one, in the order they are applied.\n </p>\n\n <div class=\"is-pt-2 is-pr-1 is-overflow-y-auto | aggregated-formulas-rules\">\n @for (section of renderedSections(); track section.id; let first = $first) {\n <he-formula-block\n [formulas]=\"section.formulas\"\n [heading]=\"section.heading\"\n [collapsible]=\"true\"\n [showToggle]=\"first\"\n [hasSubstitutions]=\"hasSubstitutions()\"\n [(substituted)]=\"substituted\"\n emptyTitle=\"No recorded values to substitute\" />\n\n <!-- the contributors sit between the two calculations: what went in, above how a weight is derived -->\n @if (section.id === 'value' && contributors(); as table) {\n <he-collapsible-block [heading]=\"contributor().heading\">\n @if (table.rows.length) {\n <table class=\"table is-narrow is-size-7 w-100 | contributors-table\">\n <thead>\n <tr>\n <th>{{ contributor().row }}</th>\n @for (column of table.columns; track column.symbol) {\n <th class=\"has-text-right\">\n <span [heKatex]=\"column.symbol\" [heKatexInline]=\"true\"></span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of table.rows; track row.id) {\n <tr>\n <td [attr.title]=\"row.id\">\n <he-node-link [node]=\"row.node\" linkClass=\"has-text-white\">\n <span class=\"break-word\">{{ row.label }}</span>\n </he-node-link>\n </td>\n @for (value of row.values; track $index) {\n <td class=\"has-text-right is-nowrap\">{{ value | precision: 4 | default: '-' }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <p class=\"is-size-7\">\n Combined from {{ table.count }} {{ contributor().group }}, too many for the aggregation to record one by\n one.\n </p>\n }\n </he-collapsible-block>\n }\n }\n </div>\n </div>\n}\n", styles: [".formula-intro{opacity:.85}.contributors-table{background-color:transparent;color:inherit}.contributors-table th,.contributors-table td{border-color:#fff3;color:inherit;padding:.15rem .5rem .15rem 0;vertical-align:top}.contributors-table th{opacity:.7}.contributors-table ::ng-deep .katex{font-size:1em}.aggregated-formulas-rules{max-height:50vh}\n"] }]
|
|
12167
|
-
}], propDecorators: { termType: [{ type: i0.Input, args: [{ isSignal: true, alias: "termType", required: false }] }], termId: [{ type: i0.Input, args: [{ isSignal: true, alias: "termId", required: false }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }], worldAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "worldAggregation", required: false }] }], subAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "subAggregation", required: false }] }], substituted: [{ type: i0.Input, args: [{ isSignal: true, alias: "substituted", required: false }] }, { type: i0.Output, args: ["substitutedChange"] }] } });
|
|
12250
|
+
], template: "@if (renderedSections().length) {\n <div class=\"aggregated-formulas\">\n <p class=\"is-size-7 is-mb-2 | formula-intro\">\n Aggregated values are calculated from the underlying {{ contributor().group }}, not measured. These are the rules\n that produced this one, in the order they are applied.\n </p>\n\n <div class=\"is-pt-2 is-pr-1 is-overflow-y-auto | aggregated-formulas-rules\">\n <!-- what went in, before the rules that combine it: the formulas cannot show this -->\n @if (inclusion(); as rule) {\n <he-collapsible-block heading=\"Which Cycles are included\">\n <p class=\"is-size-7\">{{ rule.included }}</p>\n <p class=\"is-size-7\">{{ rule.blank }}</p>\n </he-collapsible-block>\n }\n\n @for (section of renderedSections(); track section.id; let first = $first) {\n <he-formula-block\n [formulas]=\"section.formulas\"\n [heading]=\"section.heading\"\n [collapsible]=\"true\"\n [showToggle]=\"first\"\n [hasSubstitutions]=\"hasSubstitutions()\"\n [(substituted)]=\"substituted\"\n emptyTitle=\"No recorded values to substitute\" />\n\n <!-- the contributors sit between the two calculations: what went in, above how a weight is derived -->\n @if (section.id === 'value' && contributors(); as table) {\n <he-collapsible-block [heading]=\"contributor().heading\">\n @if (table.rows.length) {\n <table class=\"table is-narrow is-size-7 w-100 | contributors-table\">\n <thead>\n <tr>\n <th>{{ contributor().row }}</th>\n @for (column of table.columns; track column.symbol) {\n <th class=\"has-text-right\">\n <span [heKatex]=\"column.symbol\" [heKatexInline]=\"true\"></span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of table.rows; track row.id) {\n <tr>\n <td [attr.title]=\"row.id\">\n <he-node-link [node]=\"row.node\" linkClass=\"has-text-white\">\n <span class=\"break-word\">{{ row.label }}</span>\n </he-node-link>\n </td>\n @for (value of row.values; track $index) {\n <td class=\"has-text-right is-nowrap\">{{ value | precision: 4 | default: '-' }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <p class=\"is-size-7\">\n Combined from {{ table.count }} {{ contributor().group }}, too many for the aggregation to record one by\n one.\n </p>\n }\n </he-collapsible-block>\n }\n }\n </div>\n </div>\n}\n", styles: [".formula-intro{opacity:.85}.contributors-table{background-color:transparent;color:inherit}.contributors-table th,.contributors-table td{border-color:#fff3;color:inherit;padding:.15rem .5rem .15rem 0;vertical-align:top}.contributors-table th{opacity:.7}.contributors-table ::ng-deep .katex{font-size:1em}.aggregated-formulas-rules{max-height:50vh}\n"] }]
|
|
12251
|
+
}], propDecorators: { termType: [{ type: i0.Input, args: [{ isSignal: true, alias: "termType", required: false }] }], termId: [{ type: i0.Input, args: [{ isSignal: true, alias: "termId", required: false }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], term: [{ type: i0.Input, args: [{ isSignal: true, alias: "term", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }], worldAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "worldAggregation", required: false }] }], subAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "subAggregation", required: false }] }], substituted: [{ type: i0.Input, args: [{ isSignal: true, alias: "substituted", required: false }] }, { type: i0.Output, args: ["substitutedChange"] }] } });
|
|
12168
12252
|
|
|
12169
12253
|
// the `.jlog` entries the aggregation records are tagged with its own model name
|
|
12170
12254
|
const JLOG_MODEL = 'aggregation';
|
|
@@ -12234,17 +12318,27 @@ const nodeValues = (jlog) => {
|
|
|
12234
12318
|
? Object.fromEntries(Object.entries(values).filter(([key]) => key !== 'weight'))
|
|
12235
12319
|
: values;
|
|
12236
12320
|
};
|
|
12321
|
+
// A sub-aggregation is named for the sub-system it covers - `<product>-<country>-<organic |
|
|
12322
|
+
// conventional>-<irrigated | non-irrigated>-<period>-<run date>` - which the Cycle they are combined
|
|
12323
|
+
// into leaves out. Both segments are required: a product named "organic..." would otherwise match.
|
|
12324
|
+
const ORGANIC_SEGMENTS = ['organic', 'conventional'];
|
|
12325
|
+
const IRRIGATION_SEGMENT = 'irrigated';
|
|
12326
|
+
const isSubAggregationId = (id) => {
|
|
12327
|
+
const segments = (id ?? '').split('-');
|
|
12328
|
+
return segments.some(segment => ORGANIC_SEGMENTS.includes(segment)) && segments.includes(IRRIGATION_SEGMENT);
|
|
12329
|
+
};
|
|
12237
12330
|
/**
|
|
12238
12331
|
* Whether the sub-system stage is one of the stages that produced this Cycle's values, which its
|
|
12239
12332
|
* own node-level entry says. Everywhere else its symbols hold one value per sub-system rather than
|
|
12240
12333
|
* the single one the formula binds, so it describes another Cycle rather than this one.
|
|
12241
12334
|
*
|
|
12242
|
-
* With nothing logged at all
|
|
12243
|
-
*
|
|
12335
|
+
* With nothing logged at all - which is every aggregation that did not opt into the logs - the id
|
|
12336
|
+
* answers instead: it names the sub-system a sub-aggregation covers, and the aggregation logs are
|
|
12337
|
+
* opt-in, so guessing "sub-aggregation" there would show the wrong mean on every public page.
|
|
12244
12338
|
*/
|
|
12245
|
-
const isSubAggregation = (jlog) => {
|
|
12339
|
+
const isSubAggregation = (jlog, id) => {
|
|
12246
12340
|
const values = nodeValues(jlog);
|
|
12247
|
-
return isEmpty(values)
|
|
12341
|
+
return isEmpty(values) ? isSubAggregationId(id) : SUB_SYSTEM_KEY in values;
|
|
12248
12342
|
};
|
|
12249
12343
|
// the ids of a packed contributors table, in the order they were recorded
|
|
12250
12344
|
const tableIds = (packed) => typeof packed === 'string'
|
|
@@ -12369,7 +12463,7 @@ class NodeAggregationLogsComponent {
|
|
|
12369
12463
|
// aggregation records once at the top of the `.jlog`
|
|
12370
12464
|
this.nodeValues = computed(() => nodeValues(this.scopedJlog()), ...(ngDevMode ? [{ debugName: "nodeValues" }] : []));
|
|
12371
12465
|
// whether the sub-system weighting is one of the stages that produced these values
|
|
12372
|
-
this.subAggregation = computed(() => isSubAggregation(this.scopedJlog()), ...(ngDevMode ? [{ debugName: "subAggregation" }] : []));
|
|
12466
|
+
this.subAggregation = computed(() => isSubAggregation(this.scopedJlog(), this.node()?.['@id']), ...(ngDevMode ? [{ debugName: "subAggregation" }] : []));
|
|
12373
12467
|
/**
|
|
12374
12468
|
* The `.jlog` of every sub-aggregation this Cycle was combined from, for the country shares the
|
|
12375
12469
|
* aggregation only records on them. Best effort: a reader without access to a sub-aggregation
|
|
@@ -12399,6 +12493,7 @@ class NodeAggregationLogsComponent {
|
|
|
12399
12493
|
return groupBlankNodesByTermIdentity(blankNodes, this.nodeType(), this.nodeKey()).map(group => {
|
|
12400
12494
|
const rows = group.rows.map(row => ({
|
|
12401
12495
|
...row,
|
|
12496
|
+
term: group.term,
|
|
12402
12497
|
values: { ...shared, ...loggedValues(logs[row.index]) },
|
|
12403
12498
|
displayValue: propertyValue$1(row.value?.value, group.termId),
|
|
12404
12499
|
observations: row.value?.observations
|
|
@@ -12421,7 +12516,7 @@ class NodeAggregationLogsComponent {
|
|
|
12421
12516
|
return `${row.index}-${row.label}`;
|
|
12422
12517
|
}
|
|
12423
12518
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregationLogsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
12424
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregationLogsComponent, isStandalone: true, selector: "he-node-aggregation-logs", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, termType: { classPropertyName: "termType", publicName: "termType", isSignal: true, isRequired: false, transformFunction: null }, termId: { classPropertyName: "termId", publicName: "termId", isSignal: true, isRequired: false, transformFunction: null }, jlogParentKey: { classPropertyName: "jlogParentKey", publicName: "jlogParentKey", isSignal: true, isRequired: false, transformFunction: null }, jlogParentIndex: { classPropertyName: "jlogParentIndex", publicName: "jlogParentIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n</div>\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\"><span>Units</span></th>\n <th class=\"has-border-right\"><span>Aggregated</span></th>\n <th class=\"has-border-right\">\n <span title=\"The number of Cycles the value was aggregated from\">Observations</span>\n </th>\n <th class=\"has-border-right\"><span>Model</span></th>\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">No aggregated data to show.</p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n </div>\n </td>\n\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0], term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row: group.rows[0] }\" />\n } @else {\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"group.term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n @if (group.valueFormula) {\n <span\n class=\"has-formula\"\n [ngbPopover]=\"group.valueFormula\"\n popoverClass=\"is-narrow\"\n triggers=\"click\"\n container=\"body\">\n {{ group.value | precision: 3 | default: '-' }}\n </span>\n } @else {\n {{ group.value | precision: 3 | default: '-' }}\n }\n </td>\n <td class=\"has-border-right is-nowrap\">{{ group.observations | default: '-' }}</td>\n <ng-container *ngTemplateOutlet=\"modelCell\" />\n }\n </tr>\n\n @if (!single && group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <he-blank-node-identity\n [segments]=\"row.segments\"\n [scalars]=\"row.scalars\"\n [type]=\"group.type\"\n [fallback]=\"'entry ' + (row.index + 1)\" />\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row, term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row }\" />\n </tr>\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<ng-template #valueCells let-row=\"row\" let-term=\"term\">\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.displayValue | precision: 3 | default: '-' }}\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.observations | default: '-' }}\n </td>\n</ng-template>\n\n<!-- `row` is left out on a term group: the rules are the same for every entry it holds, so the guide\n is shown there too, but the values to substitute belong to one entry - those stay on the sub-rows -->\n<ng-template #modelCell let-row=\"row\">\n <td class=\"has-border-right\">\n <div class=\"is-flex is-align-self-stretch is-align-items-center is-gap-8\">\n <span class=\"is-flex-grow-1 is-nowrap\">{{ modelName }}</span>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (row) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"aggregationRules\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ values: row.values })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n } @else {\n <span class=\"has-text-grey is-nowrap\">Expand for logs</span>\n }\n\n <div class=\"vertical-divider\"></div>\n\n @if (guideEnabled) {\n <he-guide-overlay [pageId]=\"guidePage()\" [width]=\"500\" />\n } @else {\n <a [href]=\"guideHref()\" target=\"_blank\" rel=\"noopener\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </div>\n </div>\n </td>\n</ng-template>\n\n<ng-template #aggregationRules let-values=\"values\">\n <he-node-aggregated-formulas\n [termType]=\"termType()\"\n [termId]=\"termId()\"\n [nodeKey]=\"nodeKey()\"\n [values]=\"values\"\n [worldAggregation]=\"worldAggregation()\"\n [subAggregation]=\"subAggregation()\" />\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .has-formula{cursor:help;border-bottom:1px dotted currentColor}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table th:last-child,::ng-deep .table td:last-child{min-width:12rem}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeIdentityComponent, selector: "he-blank-node-identity", inputs: ["segments", "scalars", "type", "fallback"] }, { kind: "component", type: GuideOverlayComponent, selector: "he-guide-overlay", inputs: ["pageId", "width", "height", "positions"], outputs: ["widthChange", "heightChange"] }, { kind: "component", type: NodeAggregatedFormulasComponent, selector: "he-node-aggregated-formulas", inputs: ["termType", "termId", "nodeKey", "values", "worldAggregation", "subAggregation", "substituted"], outputs: ["substitutedChange"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12519
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregationLogsComponent, isStandalone: true, selector: "he-node-aggregation-logs", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, termType: { classPropertyName: "termType", publicName: "termType", isSignal: true, isRequired: false, transformFunction: null }, termId: { classPropertyName: "termId", publicName: "termId", isSignal: true, isRequired: false, transformFunction: null }, jlogParentKey: { classPropertyName: "jlogParentKey", publicName: "jlogParentKey", isSignal: true, isRequired: false, transformFunction: null }, jlogParentIndex: { classPropertyName: "jlogParentIndex", publicName: "jlogParentIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n</div>\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\"><span>Units</span></th>\n <th class=\"has-border-right\"><span>Aggregated</span></th>\n <th class=\"has-border-right\">\n <span title=\"The number of Cycles the value was aggregated from\">Observations</span>\n </th>\n <th class=\"has-border-right\"><span>Model</span></th>\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">No aggregated data to show.</p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n </div>\n </td>\n\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0], term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row: group.rows[0] }\" />\n } @else {\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"group.term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n @if (group.valueFormula) {\n <span\n class=\"has-formula\"\n [ngbPopover]=\"group.valueFormula\"\n popoverClass=\"is-narrow\"\n triggers=\"click\"\n container=\"body\">\n {{ group.value | precision: 3 | default: '-' }}\n </span>\n } @else {\n {{ group.value | precision: 3 | default: '-' }}\n }\n </td>\n <td class=\"has-border-right is-nowrap\">{{ group.observations | default: '-' }}</td>\n <ng-container *ngTemplateOutlet=\"modelCell\" />\n }\n </tr>\n\n @if (!single && group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <he-blank-node-identity\n [segments]=\"row.segments\"\n [scalars]=\"row.scalars\"\n [type]=\"group.type\"\n [fallback]=\"'entry ' + (row.index + 1)\" />\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row, term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row }\" />\n </tr>\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<ng-template #valueCells let-row=\"row\" let-term=\"term\">\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.displayValue | precision: 3 | default: '-' }}\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.observations | default: '-' }}\n </td>\n</ng-template>\n\n<!-- `row` is left out on a term group: the rules are the same for every entry it holds, so the guide\n is shown there too, but the values to substitute belong to one entry - those stay on the sub-rows -->\n<ng-template #modelCell let-row=\"row\">\n <td class=\"has-border-right\">\n <div class=\"is-flex is-align-self-stretch is-align-items-center is-gap-8\">\n <span class=\"is-flex-grow-1 is-nowrap\">{{ modelName }}</span>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (row) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"aggregationRules\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ values: row.values, term: row.term })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n } @else {\n <span class=\"has-text-grey is-nowrap\">Expand for logs</span>\n }\n\n <div class=\"vertical-divider\"></div>\n\n @if (guideEnabled) {\n <he-guide-overlay [pageId]=\"guidePage()\" [width]=\"500\" />\n } @else {\n <a [href]=\"guideHref()\" target=\"_blank\" rel=\"noopener\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </div>\n </div>\n </td>\n</ng-template>\n\n<ng-template #aggregationRules let-values=\"values\" let-term=\"term\">\n <he-node-aggregated-formulas\n [termType]=\"termType()\"\n [termId]=\"termId()\"\n [nodeKey]=\"nodeKey()\"\n [term]=\"term\"\n [values]=\"values\"\n [worldAggregation]=\"worldAggregation()\"\n [subAggregation]=\"subAggregation()\" />\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .has-formula{cursor:help;border-bottom:1px dotted currentColor}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table th:last-child,::ng-deep .table td:last-child{min-width:12rem}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeIdentityComponent, selector: "he-blank-node-identity", inputs: ["segments", "scalars", "type", "fallback"] }, { kind: "component", type: GuideOverlayComponent, selector: "he-guide-overlay", inputs: ["pageId", "width", "height", "positions"], outputs: ["widthChange", "heightChange"] }, { kind: "component", type: NodeAggregatedFormulasComponent, selector: "he-node-aggregated-formulas", inputs: ["termType", "termId", "nodeKey", "term", "values", "worldAggregation", "subAggregation", "substituted"], outputs: ["substitutedChange"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12425
12520
|
}
|
|
12426
12521
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregationLogsComponent, decorators: [{
|
|
12427
12522
|
type: Component$1,
|
|
@@ -12437,7 +12532,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
12437
12532
|
BlankNodeIdentityComponent,
|
|
12438
12533
|
GuideOverlayComponent,
|
|
12439
12534
|
NodeAggregatedFormulasComponent
|
|
12440
|
-
], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n</div>\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\"><span>Units</span></th>\n <th class=\"has-border-right\"><span>Aggregated</span></th>\n <th class=\"has-border-right\">\n <span title=\"The number of Cycles the value was aggregated from\">Observations</span>\n </th>\n <th class=\"has-border-right\"><span>Model</span></th>\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">No aggregated data to show.</p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n </div>\n </td>\n\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0], term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row: group.rows[0] }\" />\n } @else {\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"group.term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n @if (group.valueFormula) {\n <span\n class=\"has-formula\"\n [ngbPopover]=\"group.valueFormula\"\n popoverClass=\"is-narrow\"\n triggers=\"click\"\n container=\"body\">\n {{ group.value | precision: 3 | default: '-' }}\n </span>\n } @else {\n {{ group.value | precision: 3 | default: '-' }}\n }\n </td>\n <td class=\"has-border-right is-nowrap\">{{ group.observations | default: '-' }}</td>\n <ng-container *ngTemplateOutlet=\"modelCell\" />\n }\n </tr>\n\n @if (!single && group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <he-blank-node-identity\n [segments]=\"row.segments\"\n [scalars]=\"row.scalars\"\n [type]=\"group.type\"\n [fallback]=\"'entry ' + (row.index + 1)\" />\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row, term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row }\" />\n </tr>\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<ng-template #valueCells let-row=\"row\" let-term=\"term\">\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.displayValue | precision: 3 | default: '-' }}\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.observations | default: '-' }}\n </td>\n</ng-template>\n\n<!-- `row` is left out on a term group: the rules are the same for every entry it holds, so the guide\n is shown there too, but the values to substitute belong to one entry - those stay on the sub-rows -->\n<ng-template #modelCell let-row=\"row\">\n <td class=\"has-border-right\">\n <div class=\"is-flex is-align-self-stretch is-align-items-center is-gap-8\">\n <span class=\"is-flex-grow-1 is-nowrap\">{{ modelName }}</span>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (row) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"aggregationRules\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ values: row.values })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n } @else {\n <span class=\"has-text-grey is-nowrap\">Expand for logs</span>\n }\n\n <div class=\"vertical-divider\"></div>\n\n @if (guideEnabled) {\n <he-guide-overlay [pageId]=\"guidePage()\" [width]=\"500\" />\n } @else {\n <a [href]=\"guideHref()\" target=\"_blank\" rel=\"noopener\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </div>\n </div>\n </td>\n</ng-template>\n\n<ng-template #aggregationRules let-values=\"values\">\n <he-node-aggregated-formulas\n [termType]=\"termType()\"\n [termId]=\"termId()\"\n [nodeKey]=\"nodeKey()\"\n [values]=\"values\"\n [worldAggregation]=\"worldAggregation()\"\n [subAggregation]=\"subAggregation()\" />\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .has-formula{cursor:help;border-bottom:1px dotted currentColor}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table th:last-child,::ng-deep .table td:last-child{min-width:12rem}\n"] }]
|
|
12535
|
+
], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n</div>\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\"><span>Units</span></th>\n <th class=\"has-border-right\"><span>Aggregated</span></th>\n <th class=\"has-border-right\">\n <span title=\"The number of Cycles the value was aggregated from\">Observations</span>\n </th>\n <th class=\"has-border-right\"><span>Model</span></th>\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">No aggregated data to show.</p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n </div>\n </td>\n\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0], term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row: group.rows[0] }\" />\n } @else {\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"group.term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n @if (group.valueFormula) {\n <span\n class=\"has-formula\"\n [ngbPopover]=\"group.valueFormula\"\n popoverClass=\"is-narrow\"\n triggers=\"click\"\n container=\"body\">\n {{ group.value | precision: 3 | default: '-' }}\n </span>\n } @else {\n {{ group.value | precision: 3 | default: '-' }}\n }\n </td>\n <td class=\"has-border-right is-nowrap\">{{ group.observations | default: '-' }}</td>\n <ng-container *ngTemplateOutlet=\"modelCell\" />\n }\n </tr>\n\n @if (!single && group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <he-blank-node-identity\n [segments]=\"row.segments\"\n [scalars]=\"row.scalars\"\n [type]=\"group.type\"\n [fallback]=\"'entry ' + (row.index + 1)\" />\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row, term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row }\" />\n </tr>\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<ng-template #valueCells let-row=\"row\" let-term=\"term\">\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.displayValue | precision: 3 | default: '-' }}\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.observations | default: '-' }}\n </td>\n</ng-template>\n\n<!-- `row` is left out on a term group: the rules are the same for every entry it holds, so the guide\n is shown there too, but the values to substitute belong to one entry - those stay on the sub-rows -->\n<ng-template #modelCell let-row=\"row\">\n <td class=\"has-border-right\">\n <div class=\"is-flex is-align-self-stretch is-align-items-center is-gap-8\">\n <span class=\"is-flex-grow-1 is-nowrap\">{{ modelName }}</span>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (row) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"aggregationRules\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ values: row.values, term: row.term })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n } @else {\n <span class=\"has-text-grey is-nowrap\">Expand for logs</span>\n }\n\n <div class=\"vertical-divider\"></div>\n\n @if (guideEnabled) {\n <he-guide-overlay [pageId]=\"guidePage()\" [width]=\"500\" />\n } @else {\n <a [href]=\"guideHref()\" target=\"_blank\" rel=\"noopener\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </div>\n </div>\n </td>\n</ng-template>\n\n<ng-template #aggregationRules let-values=\"values\" let-term=\"term\">\n <he-node-aggregated-formulas\n [termType]=\"termType()\"\n [termId]=\"termId()\"\n [nodeKey]=\"nodeKey()\"\n [term]=\"term\"\n [values]=\"values\"\n [worldAggregation]=\"worldAggregation()\"\n [subAggregation]=\"subAggregation()\" />\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .has-formula{cursor:help;border-bottom:1px dotted currentColor}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table th:last-child,::ng-deep .table td:last-child{min-width:12rem}\n"] }]
|
|
12441
12536
|
}], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: false }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], termType: [{ type: i0.Input, args: [{ isSignal: true, alias: "termType", required: false }] }], termId: [{ type: i0.Input, args: [{ isSignal: true, alias: "termId", required: false }] }], jlogParentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlogParentKey", required: false }] }], jlogParentIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlogParentIndex", required: false }] }] } });
|
|
12442
12537
|
|
|
12443
12538
|
const isValidDate = (date) => (date || '').trim().length === 10;
|