@hestia-earth/ui-components 0.43.10 → 0.43.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.
|
@@ -9192,11 +9192,11 @@ class FormulaBlockComponent {
|
|
|
9192
9192
|
this.substituted.set(!this.substituted());
|
|
9193
9193
|
}
|
|
9194
9194
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormulaBlockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9195
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: FormulaBlockComponent, isStandalone: true, selector: "he-formula-block", inputs: { formulas: { classPropertyName: "formulas", publicName: "formulas", isSignal: true, isRequired: false, transformFunction: null }, hasSubstitutions: { classPropertyName: "hasSubstitutions", publicName: "hasSubstitutions", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, note: { classPropertyName: "note", publicName: "note", isSignal: true, isRequired: false, transformFunction: null }, showToggle: { classPropertyName: "showToggle", publicName: "showToggle", isSignal: true, isRequired: false, transformFunction: null }, substituted: { classPropertyName: "substituted", publicName: "substituted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { substituted: "substitutedChange" }, ngImport: i0, template: "@if (items().length) {\n <div class=\"formula-block is-mb-2\">\n <div class=\"is-flex is-align-items-center is-justify-content-space-between is-gap-8 is-mb-1 is-size-8\">\n <span class=\"is-uppercase has-text-weight-semibold\">\n {{ heading() || 'Formula' + (items().length > 1 ? 's' : '') }}\n </span>\n @if (showToggle()) {\n <div class=\"field\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"toggleId\"\n [checked]=\"substituted()\"\n [disabled]=\"!hasSubstitutions()\"\n (change)=\"toggle()\" />\n <label [for]=\"toggleId\" [title]=\"hasSubstitutions() ? '' : emptyTitle()\">\n <span>{{ substituted() ? 'Substituted' : 'Raw' }}</span>\n </label>\n </div>\n }\n </div>\n\n @if (note()) {\n <p class=\"is-size-8 is-mb-1 | formula-note\">{{ note() }}</p>\n }\n\n @for (item of items(); track $index) {\n @if (item.section) {\n <p class=\"is-size-8 is-uppercase has-text-weight-semibold is-mt-2 | formula-section\">{{ item.section }}</p>\n }\n\n @if (item.contextParts.length) {\n <p class=\"is-size-7 is-mt-1 | formula-context\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: item.contextParts }\" />\n </p>\n }\n\n <div class=\"formula\" [heKatex]=\"item.rendered\"></div>\n\n @if (item.variables.length) {\n <ul class=\"is-size-7 is-mt-1 is-mb-2 is-list-style-disc | formula-variables\">\n @for (variable of item.variables; track $index) {\n <li>\n <div class=\"is-flex is-align-items-baseline is-gap-4\">\n <span class=\"formula-variable-symbol is-nowrap\">\n <span [heKatex]=\"variable.symbol\" [heKatexInline]=\"true\"></span>\n @if (variable.descriptionParts.length) {\n <span>:</span>\n }\n </span>\n @if (variable.descriptionParts.length) {\n <span class=\"is-italic | formula-variable-desc\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: variable.descriptionParts }\" />\n </span>\n }\n @if (variable.missing) {\n <span class=\"has-text-warning\">(missing)</span>\n }\n </div>\n </li>\n }\n </ul>\n }\n }\n </div>\n}\n\n<!-- documentation text: the symbols it names render as math, the rest as markdown -->\n<ng-template #textParts let-parts=\"parts\">\n @for (part of parts; track $index) {\n @if (part.math) {\n <span class=\"formula-inline-math\" [heKatex]=\"part.math\" [heKatexInline]=\"true\"></span>\n } @else {\n <markdown class=\"is-inline-block\" [data]=\"part.text\" />\n }\n }\n</ng-template>\n", styles: [".formula-block{border-bottom:1px solid rgba(255,255,255,.2)}.formula-section{opacity:.7;letter-spacing:.03em}.formula-note{opacity:.7}.formula-context{opacity:.9}.formula-variables{padding-inline-start:1rem}.formula-variable-symbol{flex-shrink:0}.formula-variable-symbol ::ng-deep .katex{font-size:1em}.formula-variable-desc{opacity:.85}.formula-context ::ng-deep markdown,.formula-context ::ng-deep markdown *,.formula-variable-desc ::ng-deep markdown,.formula-variable-desc ::ng-deep markdown *{display:inline;margin:0;white-space:pre-wrap}.formula-context .formula-inline-math ::ng-deep .katex,.formula-variable-desc .formula-inline-math ::ng-deep .katex{font-size:1em}.formula{overflow-x:auto;overflow-y:hidden}.formula ::ng-deep .katex-display{margin:.35rem 0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: KatexDirective, selector: "[heKatex]", inputs: ["heKatex", "heKatexInline"] }, { kind: "component", type: MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9195
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: FormulaBlockComponent, isStandalone: true, selector: "he-formula-block", inputs: { formulas: { classPropertyName: "formulas", publicName: "formulas", isSignal: true, isRequired: false, transformFunction: null }, hasSubstitutions: { classPropertyName: "hasSubstitutions", publicName: "hasSubstitutions", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, note: { classPropertyName: "note", publicName: "note", isSignal: true, isRequired: false, transformFunction: null }, showToggle: { classPropertyName: "showToggle", publicName: "showToggle", isSignal: true, isRequired: false, transformFunction: null }, substituted: { classPropertyName: "substituted", publicName: "substituted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { substituted: "substitutedChange" }, ngImport: i0, template: "@if (items().length) {\n <div class=\"formula-block is-mb-2\">\n <div class=\"is-flex is-align-items-center is-justify-content-space-between is-gap-8 is-mb-1 is-size-8\">\n <span class=\"is-uppercase has-text-weight-semibold\">\n {{ heading() || 'Formula' + (items().length > 1 ? 's' : '') }}\n </span>\n @if (showToggle()) {\n <div class=\"field\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"toggleId\"\n [checked]=\"substituted()\"\n [disabled]=\"!hasSubstitutions()\"\n (change)=\"toggle()\" />\n <label [for]=\"toggleId\" [title]=\"hasSubstitutions() ? '' : emptyTitle()\">\n <span>{{ substituted() ? 'Substituted' : 'Raw' }}</span>\n </label>\n </div>\n }\n </div>\n\n @if (note()) {\n <p class=\"is-size-8 is-mb-1 | formula-note\">{{ note() }}</p>\n }\n\n @for (item of items(); track $index) {\n @if (item.section) {\n <p class=\"is-size-8 is-uppercase has-text-weight-semibold is-mt-2 | formula-section\">{{ item.section }}</p>\n }\n\n @if (item.contextParts.length) {\n <p class=\"is-size-7 is-mt-1 | formula-context\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: item.contextParts }\" />\n </p>\n }\n\n <div class=\"formula\" [heKatex]=\"item.rendered\"></div>\n\n @if (item.variables.length) {\n <ul class=\"is-size-7 is-mt-1 is-mb-2 is-list-style-disc | formula-variables\">\n @for (variable of item.variables; track $index) {\n <li>\n <div class=\"is-flex is-align-items-baseline is-gap-4\">\n <span class=\"formula-variable-symbol is-nowrap\">\n <span [heKatex]=\"variable.symbol\" [heKatexInline]=\"true\"></span>\n @if (variable.descriptionParts.length) {\n <span>:</span>\n }\n </span>\n @if (variable.descriptionParts.length) {\n <span class=\"is-italic | formula-variable-desc\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: variable.descriptionParts }\" />\n </span>\n }\n @if (variable.note) {\n <span class=\"formula-variable-note\">({{ variable.note }})</span>\n } @else if (variable.missing) {\n <span class=\"has-text-warning\">(missing)</span>\n }\n </div>\n </li>\n }\n </ul>\n }\n }\n </div>\n}\n\n<!-- documentation text: the symbols it names render as math, the rest as markdown -->\n<ng-template #textParts let-parts=\"parts\">\n @for (part of parts; track $index) {\n @if (part.math) {\n <span class=\"formula-inline-math\" [heKatex]=\"part.math\" [heKatexInline]=\"true\"></span>\n } @else {\n <markdown class=\"is-inline-block\" [data]=\"part.text\" />\n }\n }\n</ng-template>\n", styles: [".formula-block{border-bottom:1px solid rgba(255,255,255,.2)}.formula-section{opacity:.7;letter-spacing:.03em}.formula-note{opacity:.7}.formula-context{opacity:.9}.formula-variables{padding-inline-start:1rem}.formula-variable-symbol{flex-shrink:0}.formula-variable-symbol ::ng-deep .katex{font-size:1em}.formula-variable-desc{opacity:.85}.formula-variable-note{opacity:.7;white-space:nowrap}.formula-context ::ng-deep markdown,.formula-context ::ng-deep markdown *,.formula-variable-desc ::ng-deep markdown,.formula-variable-desc ::ng-deep markdown *{display:inline;margin:0;white-space:pre-wrap}.formula-context .formula-inline-math ::ng-deep .katex,.formula-variable-desc .formula-inline-math ::ng-deep .katex{font-size:1em}.formula{overflow-x:auto;overflow-y:hidden}.formula ::ng-deep .katex-display{margin:.35rem 0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: KatexDirective, selector: "[heKatex]", inputs: ["heKatex", "heKatexInline"] }, { kind: "component", type: MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9196
9196
|
}
|
|
9197
9197
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormulaBlockComponent, decorators: [{
|
|
9198
9198
|
type: Component$1,
|
|
9199
|
-
args: [{ selector: 'he-formula-block', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, KatexDirective, MarkdownComponent], template: "@if (items().length) {\n <div class=\"formula-block is-mb-2\">\n <div class=\"is-flex is-align-items-center is-justify-content-space-between is-gap-8 is-mb-1 is-size-8\">\n <span class=\"is-uppercase has-text-weight-semibold\">\n {{ heading() || 'Formula' + (items().length > 1 ? 's' : '') }}\n </span>\n @if (showToggle()) {\n <div class=\"field\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"toggleId\"\n [checked]=\"substituted()\"\n [disabled]=\"!hasSubstitutions()\"\n (change)=\"toggle()\" />\n <label [for]=\"toggleId\" [title]=\"hasSubstitutions() ? '' : emptyTitle()\">\n <span>{{ substituted() ? 'Substituted' : 'Raw' }}</span>\n </label>\n </div>\n }\n </div>\n\n @if (note()) {\n <p class=\"is-size-8 is-mb-1 | formula-note\">{{ note() }}</p>\n }\n\n @for (item of items(); track $index) {\n @if (item.section) {\n <p class=\"is-size-8 is-uppercase has-text-weight-semibold is-mt-2 | formula-section\">{{ item.section }}</p>\n }\n\n @if (item.contextParts.length) {\n <p class=\"is-size-7 is-mt-1 | formula-context\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: item.contextParts }\" />\n </p>\n }\n\n <div class=\"formula\" [heKatex]=\"item.rendered\"></div>\n\n @if (item.variables.length) {\n <ul class=\"is-size-7 is-mt-1 is-mb-2 is-list-style-disc | formula-variables\">\n @for (variable of item.variables; track $index) {\n <li>\n <div class=\"is-flex is-align-items-baseline is-gap-4\">\n <span class=\"formula-variable-symbol is-nowrap\">\n <span [heKatex]=\"variable.symbol\" [heKatexInline]=\"true\"></span>\n @if (variable.descriptionParts.length) {\n <span>:</span>\n }\n </span>\n @if (variable.descriptionParts.length) {\n <span class=\"is-italic | formula-variable-desc\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: variable.descriptionParts }\" />\n </span>\n }\n @if (variable.missing) {\n <span class=\"has-text-warning\">(missing)</span>\n }\n </div>\n </li>\n }\n </ul>\n }\n }\n </div>\n}\n\n<!-- documentation text: the symbols it names render as math, the rest as markdown -->\n<ng-template #textParts let-parts=\"parts\">\n @for (part of parts; track $index) {\n @if (part.math) {\n <span class=\"formula-inline-math\" [heKatex]=\"part.math\" [heKatexInline]=\"true\"></span>\n } @else {\n <markdown class=\"is-inline-block\" [data]=\"part.text\" />\n }\n }\n</ng-template>\n", styles: [".formula-block{border-bottom:1px solid rgba(255,255,255,.2)}.formula-section{opacity:.7;letter-spacing:.03em}.formula-note{opacity:.7}.formula-context{opacity:.9}.formula-variables{padding-inline-start:1rem}.formula-variable-symbol{flex-shrink:0}.formula-variable-symbol ::ng-deep .katex{font-size:1em}.formula-variable-desc{opacity:.85}.formula-context ::ng-deep markdown,.formula-context ::ng-deep markdown *,.formula-variable-desc ::ng-deep markdown,.formula-variable-desc ::ng-deep markdown *{display:inline;margin:0;white-space:pre-wrap}.formula-context .formula-inline-math ::ng-deep .katex,.formula-variable-desc .formula-inline-math ::ng-deep .katex{font-size:1em}.formula{overflow-x:auto;overflow-y:hidden}.formula ::ng-deep .katex-display{margin:.35rem 0}\n"] }]
|
|
9199
|
+
args: [{ selector: 'he-formula-block', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, KatexDirective, MarkdownComponent], template: "@if (items().length) {\n <div class=\"formula-block is-mb-2\">\n <div class=\"is-flex is-align-items-center is-justify-content-space-between is-gap-8 is-mb-1 is-size-8\">\n <span class=\"is-uppercase has-text-weight-semibold\">\n {{ heading() || 'Formula' + (items().length > 1 ? 's' : '') }}\n </span>\n @if (showToggle()) {\n <div class=\"field\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"toggleId\"\n [checked]=\"substituted()\"\n [disabled]=\"!hasSubstitutions()\"\n (change)=\"toggle()\" />\n <label [for]=\"toggleId\" [title]=\"hasSubstitutions() ? '' : emptyTitle()\">\n <span>{{ substituted() ? 'Substituted' : 'Raw' }}</span>\n </label>\n </div>\n }\n </div>\n\n @if (note()) {\n <p class=\"is-size-8 is-mb-1 | formula-note\">{{ note() }}</p>\n }\n\n @for (item of items(); track $index) {\n @if (item.section) {\n <p class=\"is-size-8 is-uppercase has-text-weight-semibold is-mt-2 | formula-section\">{{ item.section }}</p>\n }\n\n @if (item.contextParts.length) {\n <p class=\"is-size-7 is-mt-1 | formula-context\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: item.contextParts }\" />\n </p>\n }\n\n <div class=\"formula\" [heKatex]=\"item.rendered\"></div>\n\n @if (item.variables.length) {\n <ul class=\"is-size-7 is-mt-1 is-mb-2 is-list-style-disc | formula-variables\">\n @for (variable of item.variables; track $index) {\n <li>\n <div class=\"is-flex is-align-items-baseline is-gap-4\">\n <span class=\"formula-variable-symbol is-nowrap\">\n <span [heKatex]=\"variable.symbol\" [heKatexInline]=\"true\"></span>\n @if (variable.descriptionParts.length) {\n <span>:</span>\n }\n </span>\n @if (variable.descriptionParts.length) {\n <span class=\"is-italic | formula-variable-desc\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: variable.descriptionParts }\" />\n </span>\n }\n @if (variable.note) {\n <span class=\"formula-variable-note\">({{ variable.note }})</span>\n } @else if (variable.missing) {\n <span class=\"has-text-warning\">(missing)</span>\n }\n </div>\n </li>\n }\n </ul>\n }\n }\n </div>\n}\n\n<!-- documentation text: the symbols it names render as math, the rest as markdown -->\n<ng-template #textParts let-parts=\"parts\">\n @for (part of parts; track $index) {\n @if (part.math) {\n <span class=\"formula-inline-math\" [heKatex]=\"part.math\" [heKatexInline]=\"true\"></span>\n } @else {\n <markdown class=\"is-inline-block\" [data]=\"part.text\" />\n }\n }\n</ng-template>\n", styles: [".formula-block{border-bottom:1px solid rgba(255,255,255,.2)}.formula-section{opacity:.7;letter-spacing:.03em}.formula-note{opacity:.7}.formula-context{opacity:.9}.formula-variables{padding-inline-start:1rem}.formula-variable-symbol{flex-shrink:0}.formula-variable-symbol ::ng-deep .katex{font-size:1em}.formula-variable-desc{opacity:.85}.formula-variable-note{opacity:.7;white-space:nowrap}.formula-context ::ng-deep markdown,.formula-context ::ng-deep markdown *,.formula-variable-desc ::ng-deep markdown,.formula-variable-desc ::ng-deep markdown *{display:inline;margin:0;white-space:pre-wrap}.formula-context .formula-inline-math ::ng-deep .katex,.formula-variable-desc .formula-inline-math ::ng-deep .katex{font-size:1em}.formula{overflow-x:auto;overflow-y:hidden}.formula ::ng-deep .katex-display{margin:.35rem 0}\n"] }]
|
|
9200
9200
|
}], propDecorators: { formulas: [{ type: i0.Input, args: [{ isSignal: true, alias: "formulas", required: false }] }], hasSubstitutions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasSubstitutions", required: false }] }], emptyTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyTitle", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], note: [{ type: i0.Input, args: [{ isSignal: true, alias: "note", required: false }] }], showToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToggle", required: false }] }], substituted: [{ type: i0.Input, args: [{ isSignal: true, alias: "substituted", required: false }] }, { type: i0.Output, args: ["substitutedChange"] }] } });
|
|
9201
9201
|
|
|
9202
9202
|
const isMissing$1 = (value) => value === undefined || value === null || value === '';
|
|
@@ -11565,6 +11565,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
11565
11565
|
], template: "@if (showInline()) {\n @for (key of keys; track key) {\n <he-link-key-value\n [node]=\"node()\"\n [nodeType]=\"nodeType()\"\n [dataState]=\"dataState()\"\n [dataKey]=\"dataKey()\"\n [key]=\"key\" />\n }\n @for (key of additionalKeys; track key) {\n <he-link-key-value\n [node]=\"node()\"\n [nodeType]=\"nodeType()\"\n [dataState]=\"dataState()\"\n [dataKey]=\"dataKey()\"\n [key]=\"key\" />\n }\n\n <ng-container *ngTemplateOutlet=\"showModels\" />\n\n @if (node().distribution?.length) {\n <p>\n <a class=\"is-dark\" [href]=\"schemaBaseUrl + '/' + node['@type'] + '#distribution'\" target=\"_blank\">\n <b>distribution</b>\n </a>\n <span class=\"pr-2\">:</span>\n <ng-container *ngTemplateOutlet=\"distributionContent; context: { node: node() }\" />\n </p>\n }\n} @else {\n <he-link-key-value\n [node]=\"node()\"\n [nodeType]=\"nodeType()\"\n [dataState]=\"dataState()\"\n [dataKey]=\"dataKey()\"\n key=\"term\" />\n\n <ng-container *ngTemplateOutlet=\"showModels\" />\n\n <div class=\"columns is-p-0 is-my-0 is-overflow-visible\">\n <div class=\"column is-p-0 is-my-0\"></div>\n <div class=\"column is-p-0 is-my-0 is-narrow is-overflow-visible\">\n <div ngbDropdown class=\"is-overflow-visible\" autoClose=\"outside\" placement=\"bottom-end\">\n <button\n ngbDropdownToggle\n class=\"button is-small is-ghost has-text-white\"\n type=\"button\"\n aria-controls=\"config-menu\">\n <span>Customise fields</span>\n <span class=\"icon is-small\">\n <he-svg-icon name=\"settings\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <div ngbDropdownMenu id=\"config-menu\">\n <div\n class=\"dropdown-content is-overflow-y-auto\"\n (click)=\"$event.stopPropagation()\"\n cdkDropList\n (cdkDropListDropped)=\"dropTableKey($event)\">\n @for (key of tableKeys(); track key.key; let keyIndex = $index) {\n <div class=\"dropdown-item cdk-drag-item\" cdkDrag>\n <div class=\"field is-relative\">\n <input\n type=\"checkbox\"\n class=\"selector\"\n [id]=\"key.key\"\n [name]=\"key.key\"\n [checked]=\"key.selected\"\n (change)=\"onTableKeyChange(key, keyIndex, $event.target.checked)\" />\n <label class=\"is-pl-2\" [for]=\"key.key\">{{ key.key }}</label>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"table-container is-mt-2\">\n <table class=\"table is-dark is-narrow is-striped\">\n <thead>\n @for (key of visibleTableKeys(); track key) {\n <th>\n <a class=\"is-dark\" [href]=\"schemaBaseUrl + '/' + type() + '#' + key.split('.')[0]\" target=\"_blank\">\n <b>{{ key.includes('.') ? key.split('.')[1] : key }}</b>\n </a>\n </th>\n }\n </thead>\n <tbody>\n @for (node of nodes(); track node) {\n <tr>\n @for (key of visibleTableKeys(); track key) {\n <td>\n @if (key === 'distribution') {\n <ng-container *ngTemplateOutlet=\"distributionContent; context: { node }\" />\n } @else {\n <he-link-key-value\n [node]=\"node\"\n [nodeType]=\"nodeType()\"\n [dataState]=\"dataState()\"\n [dataKey]=\"dataKey()\"\n [key]=\"key\"\n [defaultValue]=\"defaultValue(key)\" />\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n}\n\n<ng-template #distributionContent let-node=\"node\">\n @if (node.distribution?.length) {\n @if (showDistribution() === node) {\n <a class=\"has-text-white\" (click)=\"showDistribution.set(undefined)\">Hide</a>\n } @else {\n <a class=\"has-text-white\" (click)=\"showDistribution.set(node)\">Show</a>\n }\n } @else {\n <span>N/A</span>\n }\n</ng-template>\n\n@if (chartDistribution()) {\n <div class=\"has-background-white is-mt-2 is-p-2 is-rounded | chart-container\">\n <he-distribution-chart\n [distribution]=\"chartDistribution()\"\n [value]=\"chartValue()\"\n [label]=\"chartLabel()\"\n [nbBins]=\"10\"\n [maxPercentile]=\"0.99\" />\n </div>\n}\n\n<ng-template #showModels>\n @if (models().length) {\n <p>\n <span class=\"is-inline-block\">\n <b>possible models used</b>\n </span>\n <span class=\"pr-2\">:</span>\n @for (model of models(); track model; let lastModel = $last) {\n <a class=\"is-dark\" [href]=\"model.link\">{{ model.name }}</a>\n @if (!lastModel) {\n <span class=\"is-pr-1 is-inline-block\">;</span>\n }\n }\n </p>\n }\n</ng-template>\n", styles: ["table{background-color:transparent}table::ng-deep he-link-key-value>a:first-child,table::ng-deep he-link-key-value>a:first-child+span{display:none}.dropdown-content{max-height:300px}.cdk-drag-preview{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;list-style:none}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.cdk-drag-item{cursor:move}.chart-container{border-radius:3px}.chart-container he-distribution-chart{height:250px;min-width:400px}\n"] }]
|
|
11566
11566
|
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], nodeType: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeType", required: true }] }], dataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataState", required: true }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: true }] }], aggregated: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregated", required: false }] }] } });
|
|
11567
11567
|
|
|
11568
|
+
const isMissing = (value) => value === undefined || value === null || value === '';
|
|
11569
|
+
/**
|
|
11570
|
+
* A symbol that should carry a value: the result and the inputs, but never a fixed constant or a
|
|
11571
|
+
* quantity the aggregation deliberately does not store.
|
|
11572
|
+
*/
|
|
11573
|
+
const isSubstitutable = (binding) => !!binding.key && binding.constant === undefined && !binding.display;
|
|
11574
|
+
/**
|
|
11575
|
+
* The number of rows a table would have had, which the aggregation records under `<table>_count`
|
|
11576
|
+
* when there were too many contributors to name every one of them.
|
|
11577
|
+
*/
|
|
11578
|
+
const rowCount = (values, binding) => {
|
|
11579
|
+
const count = binding.column ? values[`${binding.key}_count`] : undefined;
|
|
11580
|
+
return typeof count === 'number' && count > 0 ? count : undefined;
|
|
11581
|
+
};
|
|
11582
|
+
/**
|
|
11583
|
+
* How a symbol that did not resolve is explained: the contributors were not recorded one by one,
|
|
11584
|
+
* only counted - which is a different thing from a value the aggregation never logged.
|
|
11585
|
+
*/
|
|
11586
|
+
const notListedNote = (values, binding) => {
|
|
11587
|
+
const count = rowCount(values, binding);
|
|
11588
|
+
return isSubstitutable(binding) && isMissing(values[binding.key]) && count
|
|
11589
|
+
? `${count} Cycles, not listed`
|
|
11590
|
+
: undefined;
|
|
11591
|
+
};
|
|
11592
|
+
/**
|
|
11593
|
+
* Whether a symbol that should have resolved did not. A symbol whose contributors were counted
|
|
11594
|
+
* rather than listed is not missing: `notListedNote` says how many there were instead.
|
|
11595
|
+
*/
|
|
11596
|
+
const isMissingValue = (values, binding) => isSubstitutable(binding) && isMissing(values[binding.key]) && !rowCount(values, binding);
|
|
11597
|
+
/**
|
|
11598
|
+
* Whether any symbol of these formulas resolves. When none does, the substituted view would be
|
|
11599
|
+
* identical to the symbolic one.
|
|
11600
|
+
*/
|
|
11601
|
+
const hasAnySubstitution = (values, bindings) => bindings.some(binding => isSubstitutable(binding) && !isMissing(values[binding.key]));
|
|
11602
|
+
|
|
11568
11603
|
// The page every aggregation follows, whatever the product.
|
|
11569
11604
|
const GENERAL_PAGE = 'general-process';
|
|
11570
11605
|
// The product-specific page, by the primary product's `termType`. A term type with no page of its
|
|
@@ -11597,16 +11632,16 @@ const isCompletenessZeroFill = (formula) => bindsTo(formula, 'zero_filled_weight
|
|
|
11597
11632
|
// The production-share weight is what combines country aggregations into a World one, so it is only
|
|
11598
11633
|
// part of how a World aggregation was produced - a country aggregation never applies it.
|
|
11599
11634
|
const isWorldWeight = (formula) => bindsTo(formula, 'world_production');
|
|
11635
|
+
// The sub-system weight is the product of one sub-aggregation's organic and irrigation factors, so
|
|
11636
|
+
// it is only part of how that sub-aggregation was produced. On the Cycle they are combined into,
|
|
11637
|
+
// each of its symbols holds one value per sub-system rather than the single one bound here.
|
|
11638
|
+
const isSubSystemWeight = (formula) => bindsTo(formula, 'organic_factor');
|
|
11600
11639
|
// The phase weighting splits a plantation's lifespan between its productive and non-productive
|
|
11601
11640
|
// years, so it only ran where the product is a permanent crop.
|
|
11602
11641
|
const isPlantationWeight = (formula) => bindsTo(formula, 'plantation_lifespan');
|
|
11603
11642
|
// The blank node keys whose terms carry a completeness area.
|
|
11604
11643
|
const COMPLETENESS_KEYS = ['products', 'emissions', 'inputs', 'practices'];
|
|
11605
11644
|
const PRODUCTS_KEY = 'products';
|
|
11606
|
-
const isMissing = (value) => value === undefined || value === null || value === '';
|
|
11607
|
-
// a symbol that should carry a value: the result and the inputs, but never a fixed constant or a
|
|
11608
|
-
// quantity the aggregation deliberately does not store
|
|
11609
|
-
const isSubstitutable = (binding) => !!binding.key && binding.constant === undefined && !binding.display;
|
|
11610
11645
|
class NodeAggregatedFormulasComponent {
|
|
11611
11646
|
constructor() {
|
|
11612
11647
|
/**
|
|
@@ -11636,6 +11671,12 @@ class NodeAggregatedFormulasComponent {
|
|
|
11636
11671
|
* is not part of how its values were produced.
|
|
11637
11672
|
*/
|
|
11638
11673
|
this.worldAggregation = input(false, ...(ngDevMode ? [{ debugName: "worldAggregation" }] : []));
|
|
11674
|
+
/**
|
|
11675
|
+
* Whether the Cycle is one of the sub-aggregations an aggregation is combined from, in which case
|
|
11676
|
+
* the sub-system weighting is one of the stages that produced its values. Defaults to showing the
|
|
11677
|
+
* stage, so a caller that cannot tell keeps the rule documented.
|
|
11678
|
+
*/
|
|
11679
|
+
this.subAggregation = input(true, ...(ngDevMode ? [{ debugName: "subAggregation" }] : []));
|
|
11639
11680
|
// show the substituted formula rather than the symbolic one, when there is anything to substitute
|
|
11640
11681
|
this.substituted = model(false, ...(ngDevMode ? [{ debugName: "substituted" }] : []));
|
|
11641
11682
|
/**
|
|
@@ -11649,7 +11690,7 @@ class NodeAggregatedFormulasComponent {
|
|
|
11649
11690
|
// to the symbolic one, so the toggle is disabled rather than silently doing nothing
|
|
11650
11691
|
this.hasSubstitutions = computed(() => {
|
|
11651
11692
|
const values = this.values();
|
|
11652
|
-
return this.sections().some(section => section.formulas.some(formula => formula.bindings
|
|
11693
|
+
return this.sections().some(section => section.formulas.some(formula => hasAnySubstitution(values, formula.bindings)));
|
|
11653
11694
|
}, ...(ngDevMode ? [{ debugName: "hasSubstitutions" }] : []));
|
|
11654
11695
|
/**
|
|
11655
11696
|
* Each page with its formulas, rendered symbolically or with values substituted, and the
|
|
@@ -11681,7 +11722,10 @@ class NodeAggregatedFormulasComponent {
|
|
|
11681
11722
|
description: binding.description,
|
|
11682
11723
|
// only an input that should have resolved is flagged - a constant, or a quantity the
|
|
11683
11724
|
// aggregation deliberately does not store, is never substitutable rather than missing
|
|
11684
|
-
missing: showValues &&
|
|
11725
|
+
missing: showValues && isMissingValue(values, binding),
|
|
11726
|
+
// a symbol reading from a table of contributors too long to record says how many there
|
|
11727
|
+
// were, which is not the same as a value the aggregation never logged
|
|
11728
|
+
note: showValues ? notListedNote(values, binding) : undefined
|
|
11685
11729
|
}))
|
|
11686
11730
|
};
|
|
11687
11731
|
})
|
|
@@ -11694,11 +11738,14 @@ class NodeAggregatedFormulasComponent {
|
|
|
11694
11738
|
* a World aggregation, and the phase weighting only splits the lifespan of a plantation crop.
|
|
11695
11739
|
*/
|
|
11696
11740
|
ranForAggregation(formula) {
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11741
|
+
// the stages that only run for some aggregations, and whether each ran for this one. A formula
|
|
11742
|
+
// matching none of them is part of every aggregation.
|
|
11743
|
+
const stages = [
|
|
11744
|
+
{ applies: isWorldWeight, ran: () => this.worldAggregation() },
|
|
11745
|
+
{ applies: isSubSystemWeight, ran: () => this.subAggregation() },
|
|
11746
|
+
{ applies: isPlantationWeight, ran: () => isPlantation(this.termId() ?? '') }
|
|
11747
|
+
];
|
|
11748
|
+
return stages.find(({ applies }) => applies(formula))?.ran() ?? true;
|
|
11702
11749
|
}
|
|
11703
11750
|
/**
|
|
11704
11751
|
* Whether the rule applies to the kind of data item shown. With no `nodeKey` every rule is kept,
|
|
@@ -11718,12 +11765,123 @@ class NodeAggregatedFormulasComponent {
|
|
|
11718
11765
|
return this.ranForAggregation(formula) && this.appliesToNodeKey(formula);
|
|
11719
11766
|
}
|
|
11720
11767
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregatedFormulasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11721
|
-
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 }, 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 Cycles, not measured. These are the rules that produced this\n one, in the order they are applied.\n </p>\n\n <div class=\"aggregated-formulas-rules\">\n @for (section of renderedSections(); track section.page; let first = $first) {\n <he-formula-block\n [formulas]=\"section.formulas\"\n [heading]=\"section.heading\"\n [note]=\"section.applies\"\n [showToggle]=\"first\"\n [hasSubstitutions]=\"hasSubstitutions()\"\n [(substituted)]=\"substituted\"\n emptyTitle=\"No recorded values to substitute\" />\n }\n </div>\n </div>\n}\n", styles: [".formula-intro{opacity:.85}.aggregated-formulas-rules{max-height:50vh;overflow-y:auto;padding-right:.25rem}\n"], dependencies: [{ kind: "component", type: FormulaBlockComponent, selector: "he-formula-block", inputs: ["formulas", "hasSubstitutions", "emptyTitle", "heading", "note", "showToggle", "substituted"], outputs: ["substitutedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11768
|
+
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 Cycles, not measured. These are the rules that produced this\n one, in the order they are applied.\n </p>\n\n <div class=\"aggregated-formulas-rules\">\n @for (section of renderedSections(); track section.page; let first = $first) {\n <he-formula-block\n [formulas]=\"section.formulas\"\n [heading]=\"section.heading\"\n [note]=\"section.applies\"\n [showToggle]=\"first\"\n [hasSubstitutions]=\"hasSubstitutions()\"\n [(substituted)]=\"substituted\"\n emptyTitle=\"No recorded values to substitute\" />\n }\n </div>\n </div>\n}\n", styles: [".formula-intro{opacity:.85}.aggregated-formulas-rules{max-height:50vh;overflow-y:auto;padding-right:.25rem}\n"], dependencies: [{ kind: "component", type: FormulaBlockComponent, selector: "he-formula-block", inputs: ["formulas", "hasSubstitutions", "emptyTitle", "heading", "note", "showToggle", "substituted"], outputs: ["substitutedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11722
11769
|
}
|
|
11723
11770
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregatedFormulasComponent, decorators: [{
|
|
11724
11771
|
type: Component$1,
|
|
11725
11772
|
args: [{ selector: 'he-node-aggregated-formulas', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormulaBlockComponent], 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 Cycles, not measured. These are the rules that produced this\n one, in the order they are applied.\n </p>\n\n <div class=\"aggregated-formulas-rules\">\n @for (section of renderedSections(); track section.page; let first = $first) {\n <he-formula-block\n [formulas]=\"section.formulas\"\n [heading]=\"section.heading\"\n [note]=\"section.applies\"\n [showToggle]=\"first\"\n [hasSubstitutions]=\"hasSubstitutions()\"\n [(substituted)]=\"substituted\"\n emptyTitle=\"No recorded values to substitute\" />\n }\n </div>\n </div>\n}\n", styles: [".formula-intro{opacity:.85}.aggregated-formulas-rules{max-height:50vh;overflow-y:auto;padding-right:.25rem}\n"] }]
|
|
11726
|
-
}], 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 }] }], substituted: [{ type: i0.Input, args: [{ isSignal: true, alias: "substituted", required: false }] }, { type: i0.Output, args: ["substitutedChange"] }] } });
|
|
11773
|
+
}], 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"] }] } });
|
|
11774
|
+
|
|
11775
|
+
// the `.jlog` entries the aggregation records are tagged with its own model name
|
|
11776
|
+
const JLOG_MODEL = 'aggregation';
|
|
11777
|
+
const LOGS_KEY = 'logs';
|
|
11778
|
+
// the table of contributors an aggregated value is recorded with, one row per Cycle it was
|
|
11779
|
+
// combined from: `id:<cycle id>_value:<value>_weight:<weight>;...`
|
|
11780
|
+
const WEIGHTS_KEY = 'weights';
|
|
11781
|
+
const ID_COLUMN = 'id:';
|
|
11782
|
+
/**
|
|
11783
|
+
* The share of world production a country aggregation was weighted by. It is only recorded on a
|
|
11784
|
+
* Cycle that a World aggregation combines, so its presence is what tells the two node-level
|
|
11785
|
+
* entries apart.
|
|
11786
|
+
*/
|
|
11787
|
+
const WORLD_STAGE_KEY = 'world_production';
|
|
11788
|
+
// the sub-system stage: the organic and irrigated factors a sub-aggregation's weight is the product of
|
|
11789
|
+
const SUB_SYSTEM_KEY = 'organic_factor';
|
|
11790
|
+
/**
|
|
11791
|
+
* The shares of the country's area an aggregation is weighted by. They come from lookups keyed by
|
|
11792
|
+
* country and period, so every sub-aggregation of the same country and period records the same
|
|
11793
|
+
* values, and any one of them answers for the aggregation as a whole.
|
|
11794
|
+
*/
|
|
11795
|
+
const LOOKUP_KEYS = [
|
|
11796
|
+
'organic_weight',
|
|
11797
|
+
'organic_weight_lookup_value',
|
|
11798
|
+
'irrigated_weight',
|
|
11799
|
+
'irrigated_area',
|
|
11800
|
+
'total_area'
|
|
11801
|
+
];
|
|
11802
|
+
/**
|
|
11803
|
+
* A Cycle combined from sub-aggregations has a handful of contributors (organic or conventional,
|
|
11804
|
+
* irrigated or rainfed); one aggregated straight from source Cycles has hundreds, and those record
|
|
11805
|
+
* no aggregation logs at all - so a longer list means there is nothing to fetch.
|
|
11806
|
+
*/
|
|
11807
|
+
const MAX_CONTRIBUTORS = 4;
|
|
11808
|
+
// the quantities of one `.jlog` entry, dropping the `model` marker that tags them
|
|
11809
|
+
const entryValues = (entry) => (entry?.[LOGS_KEY] ?? [])
|
|
11810
|
+
.filter(log => log?.model === JLOG_MODEL)
|
|
11811
|
+
.reduce((values, { model: _model, ...fields }) => ({ ...values, ...fields }), {});
|
|
11812
|
+
/**
|
|
11813
|
+
* The quantities recorded for one blank node. A field of the blank node (e.g. a product's
|
|
11814
|
+
* `economicValueShare`) is logged one level deeper, the way the models' `.jlog` nests it - merge
|
|
11815
|
+
* those in so a formula resolves whichever of the two it describes, the blank node's own values
|
|
11816
|
+
* winning any collision.
|
|
11817
|
+
*/
|
|
11818
|
+
const loggedValues = (entry) => {
|
|
11819
|
+
const nested = Object.entries(entry ?? {})
|
|
11820
|
+
.filter(([key]) => key !== LOGS_KEY)
|
|
11821
|
+
.reduce((values, [, field]) => ({ ...values, ...entryValues(field) }), {});
|
|
11822
|
+
return { ...nested, ...entryValues(entry) };
|
|
11823
|
+
};
|
|
11824
|
+
/**
|
|
11825
|
+
* The quantities describing the Cycle itself, recorded once at the top of its `.jlog` rather than
|
|
11826
|
+
* on each data item: how it was weighted into the aggregation it belongs to, and the shares that
|
|
11827
|
+
* weight was derived from.
|
|
11828
|
+
*
|
|
11829
|
+
* A country aggregation records its share of world production here, weighted by `weight` - the same
|
|
11830
|
+
* key the sub-system stage binds to. Drop it there, so the world share can never stand in for a
|
|
11831
|
+
* sub-system weight.
|
|
11832
|
+
*/
|
|
11833
|
+
const nodeValues = (jlog) => {
|
|
11834
|
+
const values = entryValues(jlog);
|
|
11835
|
+
return WORLD_STAGE_KEY in values
|
|
11836
|
+
? Object.fromEntries(Object.entries(values).filter(([key]) => key !== 'weight'))
|
|
11837
|
+
: values;
|
|
11838
|
+
};
|
|
11839
|
+
/**
|
|
11840
|
+
* Whether the sub-system stage is one of the stages that produced this Cycle's values, which its
|
|
11841
|
+
* own node-level entry says. Everywhere else its symbols hold one value per sub-system rather than
|
|
11842
|
+
* the single one the formula binds, so it describes another Cycle rather than this one.
|
|
11843
|
+
*
|
|
11844
|
+
* With nothing logged at all the stage is left to render symbolically, as every other rule does,
|
|
11845
|
+
* rather than hidden on a guess.
|
|
11846
|
+
*/
|
|
11847
|
+
const isSubAggregation = (jlog) => {
|
|
11848
|
+
const values = nodeValues(jlog);
|
|
11849
|
+
return isEmpty(values) || SUB_SYSTEM_KEY in values;
|
|
11850
|
+
};
|
|
11851
|
+
// the ids of a packed contributors table, in the order they were recorded
|
|
11852
|
+
const tableIds = (packed) => typeof packed === 'string'
|
|
11853
|
+
? packed
|
|
11854
|
+
.split(';')
|
|
11855
|
+
.map(row => row.split('_')[0])
|
|
11856
|
+
.filter(column => column.startsWith(ID_COLUMN))
|
|
11857
|
+
.map(column => column.slice(ID_COLUMN.length))
|
|
11858
|
+
: [];
|
|
11859
|
+
/**
|
|
11860
|
+
* The Cycles this one was combined from, named in the contributors table of any of its data items -
|
|
11861
|
+
* the only place a sub-aggregation is named, as the Cycle links the source Cycles it covers rather
|
|
11862
|
+
* than the sub-aggregations it was built from.
|
|
11863
|
+
*
|
|
11864
|
+
* Empty when the Cycle already records the country shares itself (nothing to look up), and when
|
|
11865
|
+
* there are more contributors than sub-systems (they are source Cycles, which log nothing).
|
|
11866
|
+
*/
|
|
11867
|
+
const contributorIds = (jlog) => {
|
|
11868
|
+
if (!isEmpty(sharedLookups([jlog])))
|
|
11869
|
+
return [];
|
|
11870
|
+
const tables = Object.values(jlog ?? {})
|
|
11871
|
+
.filter(section => !!section && typeof section === 'object' && !Array.isArray(section))
|
|
11872
|
+
.flatMap(section => Object.values(section).map(entry => entryValues(entry)[WEIGHTS_KEY]));
|
|
11873
|
+
const ids = unique(tableIds(tables.find(table => typeof table === 'string')));
|
|
11874
|
+
return ids.length > MAX_CONTRIBUTORS ? [] : ids;
|
|
11875
|
+
};
|
|
11876
|
+
/**
|
|
11877
|
+
* The country shares, taken from the first contributor that records them. They are the same on
|
|
11878
|
+
* every sub-aggregation of the country and period, so one readable Cycle answers for all of them -
|
|
11879
|
+
* and none being readable leaves those symbols symbolic, as they are today.
|
|
11880
|
+
*/
|
|
11881
|
+
const sharedLookups = (jlogs) => (jlogs ?? [])
|
|
11882
|
+
.map(jlog => Object.entries(nodeValues(jlog)).filter(([key]) => LOOKUP_KEYS.includes(key)))
|
|
11883
|
+
.map(entries => Object.fromEntries(entries))
|
|
11884
|
+
.find(values => !isEmpty(values)) ?? {};
|
|
11727
11885
|
|
|
11728
11886
|
// Aggregation has a single "model" - unlike a recalculation, where each term may be produced by a
|
|
11729
11887
|
// different one - so the column is a constant rather than something resolved per row.
|
|
@@ -11748,25 +11906,6 @@ const isWorldAggregation = (node) => {
|
|
|
11748
11906
|
? country['@id'] === WORLD_COUNTRY_ID
|
|
11749
11907
|
: node?.name?.split(' - ')?.[1]?.trim() === WORLD_COUNTRY_NAME;
|
|
11750
11908
|
};
|
|
11751
|
-
// the `.jlog` entries the aggregation records are tagged with its own model name
|
|
11752
|
-
const JLOG_MODEL = 'aggregation';
|
|
11753
|
-
const LOGS_KEY = 'logs';
|
|
11754
|
-
// the quantities of one `.jlog` entry, dropping the `model` marker that tags them
|
|
11755
|
-
const entryValues = (entry) => (entry?.[LOGS_KEY] ?? [])
|
|
11756
|
-
.filter(log => log?.model === JLOG_MODEL)
|
|
11757
|
-
.reduce((values, { model: _model, ...fields }) => ({ ...values, ...fields }), {});
|
|
11758
|
-
/**
|
|
11759
|
-
* The quantities recorded for one blank node. A field of the blank node (e.g. a product's
|
|
11760
|
-
* `economicValueShare`) is logged one level deeper, the way the models' `.jlog` nests it - merge
|
|
11761
|
-
* those in so a formula resolves whichever of the two it describes, the blank node's own values
|
|
11762
|
-
* winning any collision.
|
|
11763
|
-
*/
|
|
11764
|
-
const loggedValues = (entry) => {
|
|
11765
|
-
const nested = Object.entries(entry ?? {})
|
|
11766
|
-
.filter(([key]) => key !== LOGS_KEY)
|
|
11767
|
-
.reduce((values, [, field]) => ({ ...values, ...entryValues(field) }), {});
|
|
11768
|
-
return { ...nested, ...entryValues(entry) };
|
|
11769
|
-
};
|
|
11770
11909
|
const groupObservations = (rows) => {
|
|
11771
11910
|
const counts = rows.map(({ observations }) => observations).filter(count => typeof count === 'number');
|
|
11772
11911
|
if (!counts.length)
|
|
@@ -11827,6 +11966,23 @@ class NodeAggregationLogsComponent {
|
|
|
11827
11966
|
const index = this.jlogParentIndex();
|
|
11828
11967
|
return key && typeof index === 'number' && index >= 0 ? (this.jlog()?.[key]?.[index] ?? {}) : this.jlog();
|
|
11829
11968
|
}, ...(ngDevMode ? [{ debugName: "scopedJlog" }] : []));
|
|
11969
|
+
// the quantities describing the Cycle itself rather than one of its data items, which the
|
|
11970
|
+
// aggregation records once at the top of the `.jlog`
|
|
11971
|
+
this.nodeValues = computed(() => nodeValues(this.scopedJlog()), ...(ngDevMode ? [{ debugName: "nodeValues" }] : []));
|
|
11972
|
+
// whether the sub-system weighting is one of the stages that produced these values
|
|
11973
|
+
this.subAggregation = computed(() => isSubAggregation(this.scopedJlog()), ...(ngDevMode ? [{ debugName: "subAggregation" }] : []));
|
|
11974
|
+
/**
|
|
11975
|
+
* The `.jlog` of every sub-aggregation this Cycle was combined from, for the country shares the
|
|
11976
|
+
* aggregation only records on them. Best effort: a reader without access to a sub-aggregation
|
|
11977
|
+
* gets nothing back, and those symbols stay symbolic - which is how they render today.
|
|
11978
|
+
*/
|
|
11979
|
+
this.contributorsResource = rxResource({
|
|
11980
|
+
params: () => ({ ids: contributorIds(this.scopedJlog()) }),
|
|
11981
|
+
stream: ({ params: { ids } }) => ids.length
|
|
11982
|
+
? forkJoin(ids.map(id => this.nodeLogsModelsService.getJLog$({ '@type': NodeType.Cycle, '@id': id, aggregated: true })))
|
|
11983
|
+
: of([])
|
|
11984
|
+
});
|
|
11985
|
+
this.sharedLookups = computed(() => sharedLookups(this.contributorsResource.value() ?? []), ...(ngDevMode ? [{ debugName: "sharedLookups" }] : []));
|
|
11830
11986
|
// groups kept open, by term id: expanding is view state, so it survives the rows being rebuilt
|
|
11831
11987
|
this.openGroups = signal(new Set(), ...(ngDevMode ? [{ debugName: "openGroups" }] : []));
|
|
11832
11988
|
/**
|
|
@@ -11838,10 +11994,13 @@ class NodeAggregationLogsComponent {
|
|
|
11838
11994
|
const blankNodes = (this.node()?.[this.nodeKey()] ?? []);
|
|
11839
11995
|
const logs = this.scopedJlog()?.[this.nodeKey()] ?? {};
|
|
11840
11996
|
const open = this.openGroups();
|
|
11997
|
+
// a data item's own quantities win over the ones describing the whole Cycle, which in turn win
|
|
11998
|
+
// over the country shares read from a sub-aggregation
|
|
11999
|
+
const shared = { ...this.sharedLookups(), ...this.nodeValues() };
|
|
11841
12000
|
return groupBlankNodesByTermIdentity(blankNodes, this.nodeType(), this.nodeKey()).map(group => {
|
|
11842
12001
|
const rows = group.rows.map(row => ({
|
|
11843
12002
|
...row,
|
|
11844
|
-
values: loggedValues(logs[row.index]),
|
|
12003
|
+
values: { ...shared, ...loggedValues(logs[row.index]) },
|
|
11845
12004
|
displayValue: propertyValue$1(row.value?.value, group.termId),
|
|
11846
12005
|
observations: row.value?.observations
|
|
11847
12006
|
}));
|
|
@@ -11863,7 +12022,7 @@ class NodeAggregationLogsComponent {
|
|
|
11863
12022
|
return `${row.index}-${row.label}`;
|
|
11864
12023
|
}
|
|
11865
12024
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregationLogsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11866
|
-
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</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", "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 }); }
|
|
12025
|
+
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 }); }
|
|
11867
12026
|
}
|
|
11868
12027
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregationLogsComponent, decorators: [{
|
|
11869
12028
|
type: Component$1,
|
|
@@ -11879,7 +12038,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
11879
12038
|
BlankNodeIdentityComponent,
|
|
11880
12039
|
GuideOverlayComponent,
|
|
11881
12040
|
NodeAggregatedFormulasComponent
|
|
11882
|
-
], 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</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"] }]
|
|
12041
|
+
], 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"] }]
|
|
11883
12042
|
}], 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 }] }] } });
|
|
11884
12043
|
|
|
11885
12044
|
const isValidDate = (date) => (date || '').trim().length === 10;
|
|
@@ -12479,18 +12638,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
12479
12638
|
|
|
12480
12639
|
// default for all product termType, can be overwritten
|
|
12481
12640
|
const defaultMinNbObservations = 50;
|
|
12641
|
+
// the `.jlog` section the aggregation stores the quality score in, and the model tagging its entries
|
|
12642
|
+
const jlogKey = 'aggregatedQualityScore';
|
|
12643
|
+
const jlogModel = 'aggregation';
|
|
12644
|
+
const isTrue = (value) => value === true || value === 'True';
|
|
12645
|
+
/**
|
|
12646
|
+
* Whether a quantity was logged: one that was not found is logged as `None`, which the `.jlog`
|
|
12647
|
+
* carries as it is written.
|
|
12648
|
+
*/
|
|
12649
|
+
const hasLogValue = (value) => !isUndefined(value) && !isNaN(+value);
|
|
12482
12650
|
const isScoreValid = {
|
|
12483
|
-
0: ({ all_included }) => all_included
|
|
12651
|
+
0: ({ all_included }) => isTrue(all_included),
|
|
12484
12652
|
1: ({ nb_observations, min_nb_observations }) => +nb_observations >= (+min_nb_observations || defaultMinNbObservations),
|
|
12485
12653
|
2: ({ delta, delta_min, yield_delta, yield_delta_min }) => isUndefined(delta) ? +yield_delta <= +yield_delta_min : +delta <= +delta_min,
|
|
12486
|
-
3: ({ is_complete }) => is_complete
|
|
12654
|
+
3: ({ is_complete }) => isTrue(is_complete),
|
|
12487
12655
|
4: ({ production_delta, production_delta_min }) => +production_delta >= +production_delta_min
|
|
12488
12656
|
};
|
|
12657
|
+
/**
|
|
12658
|
+
* The quality score recorded in the `.jlog`, merged into a single set of values.
|
|
12659
|
+
* Empty for every aggregation that predates it, which only has the text logs.
|
|
12660
|
+
*/
|
|
12661
|
+
const jlogScore = (jlog) => (jlog?.[jlogKey]?.logs ?? [])
|
|
12662
|
+
.filter(({ model }) => model === jlogModel)
|
|
12663
|
+
.reduce((values, { model: _model, ...fields }) => ({ ...values, ...fields }), {});
|
|
12664
|
+
const toValidScores = (logs) => Object.fromEntries(Object.entries(isScoreValid).map(([key, isValid]) => [key, isValid(logs)]));
|
|
12665
|
+
|
|
12489
12666
|
const getCountryName = (node) => node?.site?.country?.name || node.name?.split(' - ')?.[1]?.trim();
|
|
12490
12667
|
class NodeAggregatedQualityScoreComponent {
|
|
12491
12668
|
constructor() {
|
|
12492
12669
|
this.searchService = inject(HeSearchService);
|
|
12493
12670
|
this.nodeService = inject(HeNodeService);
|
|
12671
|
+
this.nodeLogsModelsService = inject(NodeLogsModelsService);
|
|
12494
12672
|
this.node = input.required(...(ngDevMode ? [{ debugName: "node" }] : []));
|
|
12495
12673
|
this.country = input(...(ngDevMode ? [undefined, { debugName: "country" }] : []));
|
|
12496
12674
|
/**
|
|
@@ -12530,22 +12708,34 @@ class NodeAggregatedQualityScoreComponent {
|
|
|
12530
12708
|
: of(undefined)
|
|
12531
12709
|
});
|
|
12532
12710
|
this.countryId = computed(() => this.countryResource.value()?.['@id'], ...(ngDevMode ? [{ debugName: "countryId" }] : []));
|
|
12533
|
-
|
|
12711
|
+
// the score as recorded in the `.jlog`, which is where every new aggregation logs it
|
|
12712
|
+
this.jlogResource = rxResource({
|
|
12534
12713
|
params: () => ({
|
|
12535
12714
|
showInfo: this.showInfo(),
|
|
12536
12715
|
node: this.node()
|
|
12537
12716
|
}),
|
|
12538
|
-
stream: ({ params: { showInfo, node } }) => showInfo
|
|
12539
|
-
|
|
12717
|
+
stream: ({ params: { showInfo, node } }) => showInfo ? this.nodeLogsModelsService.getJLog$(node) : of({})
|
|
12718
|
+
});
|
|
12719
|
+
this.jlogLogs = computed(() => jlogScore(this.jlogResource.value() ?? {}), ...(ngDevMode ? [{ debugName: "jlogLogs" }] : []));
|
|
12720
|
+
this.loadingJLog = computed(() => !this.jlogResource.hasValue() || this.jlogResource.isLoading(), ...(ngDevMode ? [{ debugName: "loadingJLog" }] : []));
|
|
12721
|
+
this.useJLog = computed(() => !isEmpty(this.jlogLogs()), ...(ngDevMode ? [{ debugName: "useJLog" }] : []));
|
|
12722
|
+
// only fetch the legacy text logs once we know the `.jlog` does not carry the score
|
|
12723
|
+
this.logsResource = rxResource({
|
|
12724
|
+
params: () => ({
|
|
12725
|
+
skip: !this.showInfo() || this.loadingJLog() || this.useJLog(),
|
|
12726
|
+
node: this.node()
|
|
12727
|
+
}),
|
|
12728
|
+
stream: ({ params: { skip, node } }) => skip
|
|
12729
|
+
? of({})
|
|
12730
|
+
: this.nodeService
|
|
12540
12731
|
.getLog$({
|
|
12541
12732
|
'@type': node['@type'],
|
|
12542
12733
|
'@id': node['@id']
|
|
12543
12734
|
})
|
|
12544
12735
|
.pipe(map(value => (value ? parseLines(value) : [])), mergeAll(), filter(({ data: { message } }) => !!message), map(({ data: { message } }) => parseMessage(message)), filter(({ id }) => id === this.node()['@id']), reduce((a, b) => ({ ...a, ...b }), {}))
|
|
12545
|
-
: of({})
|
|
12546
12736
|
});
|
|
12547
|
-
this.logs = computed(() => this.logsResource.value() ?? {}, ...(ngDevMode ? [{ debugName: "logs" }] : []));
|
|
12548
|
-
this.validScores = computed(() =>
|
|
12737
|
+
this.logs = computed(() => this.useJLog() ? this.jlogLogs() : (this.logsResource.value() ?? {}), ...(ngDevMode ? [{ debugName: "logs" }] : []));
|
|
12738
|
+
this.validScores = computed(() => toValidScores(this.logs()), ...(ngDevMode ? [{ debugName: "validScores" }] : []));
|
|
12549
12739
|
this.minObservations = computed(() => +this.logs().min_nb_observations || defaultMinNbObservations, ...(ngDevMode ? [{ debugName: "minObservations" }] : []));
|
|
12550
12740
|
this.observations = computed(() => +this.logs().nb_observations, ...(ngDevMode ? [{ debugName: "observations" }] : []));
|
|
12551
12741
|
this.missingEmissionIds = computed(() => this.logs().missing_emissions?.split(';') ?? [], ...(ngDevMode ? [{ debugName: "missingEmissionIds" }] : []));
|
|
@@ -12591,10 +12781,16 @@ class NodeAggregatedQualityScoreComponent {
|
|
|
12591
12781
|
this.isGlobal = computed(() => this.countryId()?.startsWith('region-'), ...(ngDevMode ? [{ debugName: "isGlobal" }] : []));
|
|
12592
12782
|
this.schemaBaseUrl = computed(() => [schemaBaseUrl(), this.node()?.['@type']].join('/'), ...(ngDevMode ? [{ debugName: "schemaBaseUrl" }] : []));
|
|
12593
12783
|
this.schemaUrl = computed(() => `${this.schemaBaseUrl()}#aggregatedQualityScore`, ...(ngDevMode ? [{ debugName: "schemaUrl" }] : []));
|
|
12594
|
-
this.
|
|
12784
|
+
this.hasFaostatYield = computed(() => hasLogValue(this.logs()?.faostat_yield), ...(ngDevMode ? [{ debugName: "hasFaostatYield" }] : []));
|
|
12785
|
+
this.hasProductionQuantity = computed(() => hasLogValue(this.logs()?.region_production_quantity), ...(ngDevMode ? [{ debugName: "hasProductionQuantity" }] : []));
|
|
12595
12786
|
this.regionProductionQuantity = computed(() => +this.logs()?.region_production_quantity, ...(ngDevMode ? [{ debugName: "regionProductionQuantity" }] : []));
|
|
12596
12787
|
this.countriesProductionQuantity = computed(() => +this.logs()?.countries_production_quantity, ...(ngDevMode ? [{ debugName: "countriesProductionQuantity" }] : []));
|
|
12597
|
-
this.loading = computed(() => [
|
|
12788
|
+
this.loading = computed(() => [
|
|
12789
|
+
this.jlogResource.isLoading(),
|
|
12790
|
+
this.logsResource.isLoading(),
|
|
12791
|
+
this.countryResource.isLoading(),
|
|
12792
|
+
this.missingEmissionsResource.isLoading()
|
|
12793
|
+
].some(Boolean), ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
12598
12794
|
}
|
|
12599
12795
|
get hidden() {
|
|
12600
12796
|
return isUndefined(this.score());
|
|
@@ -12606,7 +12802,7 @@ class NodeAggregatedQualityScoreComponent {
|
|
|
12606
12802
|
return term['@id'];
|
|
12607
12803
|
}
|
|
12608
12804
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregatedQualityScoreComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
12609
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregatedQualityScoreComponent, isStandalone: true, selector: "he-node-aggregated-quality-score", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, country: { classPropertyName: "country", publicName: "country", isSignal: true, isRequired: false, transformFunction: null }, showInfo: { classPropertyName: "showInfo", publicName: "showInfo", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-none": "this.hidden" } }, ngImport: i0, template: "@if (score() >= 0) {\n @switch (mode()) {\n @case ('tag') {\n <div class=\"tags has-addons\">\n <a class=\"tag\" [href]=\"schemaUrl()\" target=\"_blank\">Data Quality</a>\n <ng-container *ngTemplateOutlet=\"quality\" />\n @if (showInfo()) {\n <span\n class=\"tag pointer\"\n [ngbTooltip]=\"logsTooltip\"\n [placement]=\"placement() + ' auto'\"\n triggers=\"click\"\n container=\"body\"\n autoClose=\"outside\"\n tooltipClass=\"quality-tooltip\">\n @if (loading()) {\n <he-svg-icon name=\"loading\" animation=\"spin\" />\n } @else if (logs()) {\n <he-svg-icon name=\"help-circle\" />\n }\n </span>\n }\n </div>\n }\n @default {\n <ng-container *ngTemplateOutlet=\"quality\" />\n }\n }\n}\n\n<ng-template #quality>\n <span\n [ngStyle]=\"{\n 'background-color': scoreColor()\n }\"\n class=\"tag-score | tag is-primary has-text-white has-text-weight-semibold\">\n <span>{{ scoreLevel() | uppercase }}</span>\n </span>\n</ng-template>\n\n<ng-template #logsTooltip>\n <div>\n <p class=\"mb-2\">\n Up to {{ scoreMax() }} points are awarded for the following criteria, where \"low\" is 0 to\n {{ scoreMax() - 3 }} points, \"medium\" is {{ scoreMax() - 2 }} to {{ scoreMax() - 1 }} points, and \"high\" is\n {{ scoreMax() }} points:\n </p>\n @if (validScores()) {\n <div class=\"table-container\">\n <table class=\"table is-fullwidth has-text-white has-background-secondary\">\n <thead>\n <tr>\n <th class=\"has-text-white\">Condition</th>\n <th class=\"has-text-white\">Met</th>\n @if (isCycle()) {\n <th class=\"has-text-white\">Details</th>\n }\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>No aggregated emissions included in the system boundary are missing</td>\n <td [class.has-text-success]=\"validScores()[0]\" [class.has-text-danger]=\"!validScores()[0]\">\n {{ validScores()[0] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (missingEmissions()?.length) {\n <span>{{ missingEmissions().length }}</span>\n <span class=\"is-pl-1\">{{ 'emission' | pluralize: missingEmissions().length }} are missing:</span>\n <div class=\"is-mt-2 | missing-emissions\">\n <ul class=\"is-list-style-disc is-pl-4\">\n @for (term of missingEmissions(); track trackByTerm($index, term); let l = $last) {\n <li>\n <he-node-link class=\"is-inline-block is-pl-1\" linkClass=\"is-dark\" [node]=\"term\">\n <span class=\"is-nowrap has-text-ellipsis\">{{ term.name }}</span>\n </he-node-link>\n </li>\n }\n </ul>\n </div>\n } @else if (logs()?.included_emissions) {\n <span>\n All {{ logs().included_emissions }} {{ 'emission' | pluralize: +logs().included_emissions }} for\n this product are included.\n </span>\n }\n </td>\n }\n </tr>\n <tr>\n <td>The aggregation is based on data from over {{ minObservations() }} Cycles</td>\n <td [class.has-text-success]=\"validScores()[1]\" [class.has-text-danger]=\"!validScores()[1]\">\n {{ validScores()[1] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n <span>Based on {{ observations() }} {{ 'Cycle' | pluralize: observations() }}.</span>\n </td>\n }\n </tr>\n @if (isCrop()) {\n <tr>\n <td>\n The difference between yield per hectare here and FAOSTAT is less than \u00B1{{\n logs().yield_delta_min || logs().delta_min | number\n }}%\n </td>\n <td [class.has-text-success]=\"validScores()[2]\" [class.has-text-danger]=\"!validScores()[2]\">\n {{ validScores()[2] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (
|
|
12805
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregatedQualityScoreComponent, isStandalone: true, selector: "he-node-aggregated-quality-score", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, country: { classPropertyName: "country", publicName: "country", isSignal: true, isRequired: false, transformFunction: null }, showInfo: { classPropertyName: "showInfo", publicName: "showInfo", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-none": "this.hidden" } }, ngImport: i0, template: "@if (score() >= 0) {\n @switch (mode()) {\n @case ('tag') {\n <div class=\"tags has-addons\">\n <a class=\"tag\" [href]=\"schemaUrl()\" target=\"_blank\">Data Quality</a>\n <ng-container *ngTemplateOutlet=\"quality\" />\n @if (showInfo()) {\n <span\n class=\"tag pointer\"\n [ngbTooltip]=\"logsTooltip\"\n [placement]=\"placement() + ' auto'\"\n triggers=\"click\"\n container=\"body\"\n autoClose=\"outside\"\n tooltipClass=\"quality-tooltip\">\n @if (loading()) {\n <he-svg-icon name=\"loading\" animation=\"spin\" />\n } @else if (logs()) {\n <he-svg-icon name=\"help-circle\" />\n }\n </span>\n }\n </div>\n }\n @default {\n <ng-container *ngTemplateOutlet=\"quality\" />\n }\n }\n}\n\n<ng-template #quality>\n <span\n [ngStyle]=\"{\n 'background-color': scoreColor()\n }\"\n class=\"tag-score | tag is-primary has-text-white has-text-weight-semibold\">\n <span>{{ scoreLevel() | uppercase }}</span>\n </span>\n</ng-template>\n\n<ng-template #logsTooltip>\n <div>\n <p class=\"mb-2\">\n Up to {{ scoreMax() }} points are awarded for the following criteria, where \"low\" is 0 to\n {{ scoreMax() - 3 }} points, \"medium\" is {{ scoreMax() - 2 }} to {{ scoreMax() - 1 }} points, and \"high\" is\n {{ scoreMax() }} points:\n </p>\n @if (validScores()) {\n <div class=\"table-container\">\n <table class=\"table is-fullwidth has-text-white has-background-secondary\">\n <thead>\n <tr>\n <th class=\"has-text-white\">Condition</th>\n <th class=\"has-text-white\">Met</th>\n @if (isCycle()) {\n <th class=\"has-text-white\">Details</th>\n }\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>No aggregated emissions included in the system boundary are missing</td>\n <td [class.has-text-success]=\"validScores()[0]\" [class.has-text-danger]=\"!validScores()[0]\">\n {{ validScores()[0] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (missingEmissions()?.length) {\n <span>{{ missingEmissions().length }}</span>\n <span class=\"is-pl-1\">{{ 'emission' | pluralize: missingEmissions().length }} are missing:</span>\n <div class=\"is-mt-2 | missing-emissions\">\n <ul class=\"is-list-style-disc is-pl-4\">\n @for (term of missingEmissions(); track trackByTerm($index, term); let l = $last) {\n <li>\n <he-node-link class=\"is-inline-block is-pl-1\" linkClass=\"is-dark\" [node]=\"term\">\n <span class=\"is-nowrap has-text-ellipsis\">{{ term.name }}</span>\n </he-node-link>\n </li>\n }\n </ul>\n </div>\n } @else if (logs()?.included_emissions) {\n <span>\n All {{ logs().included_emissions }} {{ 'emission' | pluralize: +logs().included_emissions }} for\n this product are included.\n </span>\n }\n </td>\n }\n </tr>\n <tr>\n <td>The aggregation is based on data from over {{ minObservations() }} Cycles</td>\n <td [class.has-text-success]=\"validScores()[1]\" [class.has-text-danger]=\"!validScores()[1]\">\n {{ validScores()[1] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n <span>Based on {{ observations() }} {{ 'Cycle' | pluralize: observations() }}.</span>\n </td>\n }\n </tr>\n @if (isCrop()) {\n <tr>\n <td>\n The difference between yield per hectare here and FAOSTAT is less than \u00B1{{\n logs().yield_delta_min || logs().delta_min | number\n }}%\n </td>\n <td [class.has-text-success]=\"validScores()[2]\" [class.has-text-danger]=\"!validScores()[2]\">\n {{ validScores()[2] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (hasFaostatYield()) {\n <span>FAOSTAT yield =</span>\n <span class=\"is-pl-1\">{{ logs()?.faostat_yield | precision: 3 }}</span>\n <span class=\"is-pl-1\">kg/ha;</span>\n <span class=\"is-pl-1\">Aggregated data yield =</span>\n <span class=\"is-pl-1\">{{ logs()?.product_yield | precision: 3 }}</span>\n <span class=\"is-pl-1\">kg/ha.</span>\n } @else {\n No yield found on FAOSTAT for this product.\n }\n </td>\n }\n </tr>\n }\n <tr>\n <td>\n Data completeness is\n <code class=\"is-p-1\">true</code>\n for all priority areas\n </td>\n <td [class.has-text-success]=\"validScores()[3]\" [class.has-text-danger]=\"!validScores()[3]\">\n {{ validScores()[3] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (missingCompletenessFields()?.length) {\n <span>{{ missingCompletenessFields().length }}</span>\n <span class=\"is-pl-1\">completeness areas missing data:</span>\n @for (field of missingCompletenessFields(); track field; let l = $last) {\n <a class=\"is-inline-block is-pl-1 is-dark\" [href]=\"completenessUrl(field)\" target=\"_blank\">\n {{ field }}\n </a>\n <span>{{ l ? '.' : ',' }}</span>\n }\n }\n </td>\n }\n </tr>\n @if (isCrop() && isGlobal()) {\n <tr>\n <td>Aggregated countries represent at least 75% of {{ countryName() }} production</td>\n <td [class.has-text-success]=\"validScores()[4]\" [class.has-text-danger]=\"!validScores()[4]\">\n {{ validScores()[4] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (hasProductionQuantity()) {\n <span>{{ countryName() }} production quantity =</span>\n <span class=\"is-pl-1\">{{ regionProductionQuantity() / 1000000 | number: '1.0-0' }}</span>\n <span class=\"is-pl-1\">million tonnes;</span>\n <span class=\"is-pl-1\">Production of countries included in the aggregation =</span>\n <span class=\"is-pl-1\">{{ countriesProductionQuantity() / 1000000 | number: '1.0-0' }}</span>\n <span class=\"is-pl-1\">million tonnes.</span>\n } @else {\n No production quantity found on FAOSTAT for this product.\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n</ng-template>\n", styles: [":host{display:inline-block}::ng-deep .quality-tooltip{min-width:500px}.missing-emissions{max-height:250px;overflow-y:auto}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgbTooltipModule }, { kind: "directive", type: i1$1.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "pipe", type: PluralizePipe, name: "pluralize" }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }, { kind: "pipe", type: UpperCasePipe, name: "uppercase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12610
12806
|
}
|
|
12611
12807
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregatedQualityScoreComponent, decorators: [{
|
|
12612
12808
|
type: Component$1,
|
|
@@ -12620,7 +12816,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
12620
12816
|
NgTemplateOutlet,
|
|
12621
12817
|
NgbTooltipModule,
|
|
12622
12818
|
HESvgIconComponent
|
|
12623
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (score() >= 0) {\n @switch (mode()) {\n @case ('tag') {\n <div class=\"tags has-addons\">\n <a class=\"tag\" [href]=\"schemaUrl()\" target=\"_blank\">Data Quality</a>\n <ng-container *ngTemplateOutlet=\"quality\" />\n @if (showInfo()) {\n <span\n class=\"tag pointer\"\n [ngbTooltip]=\"logsTooltip\"\n [placement]=\"placement() + ' auto'\"\n triggers=\"click\"\n container=\"body\"\n autoClose=\"outside\"\n tooltipClass=\"quality-tooltip\">\n @if (loading()) {\n <he-svg-icon name=\"loading\" animation=\"spin\" />\n } @else if (logs()) {\n <he-svg-icon name=\"help-circle\" />\n }\n </span>\n }\n </div>\n }\n @default {\n <ng-container *ngTemplateOutlet=\"quality\" />\n }\n }\n}\n\n<ng-template #quality>\n <span\n [ngStyle]=\"{\n 'background-color': scoreColor()\n }\"\n class=\"tag-score | tag is-primary has-text-white has-text-weight-semibold\">\n <span>{{ scoreLevel() | uppercase }}</span>\n </span>\n</ng-template>\n\n<ng-template #logsTooltip>\n <div>\n <p class=\"mb-2\">\n Up to {{ scoreMax() }} points are awarded for the following criteria, where \"low\" is 0 to\n {{ scoreMax() - 3 }} points, \"medium\" is {{ scoreMax() - 2 }} to {{ scoreMax() - 1 }} points, and \"high\" is\n {{ scoreMax() }} points:\n </p>\n @if (validScores()) {\n <div class=\"table-container\">\n <table class=\"table is-fullwidth has-text-white has-background-secondary\">\n <thead>\n <tr>\n <th class=\"has-text-white\">Condition</th>\n <th class=\"has-text-white\">Met</th>\n @if (isCycle()) {\n <th class=\"has-text-white\">Details</th>\n }\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>No aggregated emissions included in the system boundary are missing</td>\n <td [class.has-text-success]=\"validScores()[0]\" [class.has-text-danger]=\"!validScores()[0]\">\n {{ validScores()[0] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (missingEmissions()?.length) {\n <span>{{ missingEmissions().length }}</span>\n <span class=\"is-pl-1\">{{ 'emission' | pluralize: missingEmissions().length }} are missing:</span>\n <div class=\"is-mt-2 | missing-emissions\">\n <ul class=\"is-list-style-disc is-pl-4\">\n @for (term of missingEmissions(); track trackByTerm($index, term); let l = $last) {\n <li>\n <he-node-link class=\"is-inline-block is-pl-1\" linkClass=\"is-dark\" [node]=\"term\">\n <span class=\"is-nowrap has-text-ellipsis\">{{ term.name }}</span>\n </he-node-link>\n </li>\n }\n </ul>\n </div>\n } @else if (logs()?.included_emissions) {\n <span>\n All {{ logs().included_emissions }} {{ 'emission' | pluralize: +logs().included_emissions }} for\n this product are included.\n </span>\n }\n </td>\n }\n </tr>\n <tr>\n <td>The aggregation is based on data from over {{ minObservations() }} Cycles</td>\n <td [class.has-text-success]=\"validScores()[1]\" [class.has-text-danger]=\"!validScores()[1]\">\n {{ validScores()[1] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n <span>Based on {{ observations() }} {{ 'Cycle' | pluralize: observations() }}.</span>\n </td>\n }\n </tr>\n @if (isCrop()) {\n <tr>\n <td>\n The difference between yield per hectare here and FAOSTAT is less than \u00B1{{\n logs().yield_delta_min || logs().delta_min | number\n }}%\n </td>\n <td [class.has-text-success]=\"validScores()[2]\" [class.has-text-danger]=\"!validScores()[2]\">\n {{ validScores()[2] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (
|
|
12819
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (score() >= 0) {\n @switch (mode()) {\n @case ('tag') {\n <div class=\"tags has-addons\">\n <a class=\"tag\" [href]=\"schemaUrl()\" target=\"_blank\">Data Quality</a>\n <ng-container *ngTemplateOutlet=\"quality\" />\n @if (showInfo()) {\n <span\n class=\"tag pointer\"\n [ngbTooltip]=\"logsTooltip\"\n [placement]=\"placement() + ' auto'\"\n triggers=\"click\"\n container=\"body\"\n autoClose=\"outside\"\n tooltipClass=\"quality-tooltip\">\n @if (loading()) {\n <he-svg-icon name=\"loading\" animation=\"spin\" />\n } @else if (logs()) {\n <he-svg-icon name=\"help-circle\" />\n }\n </span>\n }\n </div>\n }\n @default {\n <ng-container *ngTemplateOutlet=\"quality\" />\n }\n }\n}\n\n<ng-template #quality>\n <span\n [ngStyle]=\"{\n 'background-color': scoreColor()\n }\"\n class=\"tag-score | tag is-primary has-text-white has-text-weight-semibold\">\n <span>{{ scoreLevel() | uppercase }}</span>\n </span>\n</ng-template>\n\n<ng-template #logsTooltip>\n <div>\n <p class=\"mb-2\">\n Up to {{ scoreMax() }} points are awarded for the following criteria, where \"low\" is 0 to\n {{ scoreMax() - 3 }} points, \"medium\" is {{ scoreMax() - 2 }} to {{ scoreMax() - 1 }} points, and \"high\" is\n {{ scoreMax() }} points:\n </p>\n @if (validScores()) {\n <div class=\"table-container\">\n <table class=\"table is-fullwidth has-text-white has-background-secondary\">\n <thead>\n <tr>\n <th class=\"has-text-white\">Condition</th>\n <th class=\"has-text-white\">Met</th>\n @if (isCycle()) {\n <th class=\"has-text-white\">Details</th>\n }\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>No aggregated emissions included in the system boundary are missing</td>\n <td [class.has-text-success]=\"validScores()[0]\" [class.has-text-danger]=\"!validScores()[0]\">\n {{ validScores()[0] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (missingEmissions()?.length) {\n <span>{{ missingEmissions().length }}</span>\n <span class=\"is-pl-1\">{{ 'emission' | pluralize: missingEmissions().length }} are missing:</span>\n <div class=\"is-mt-2 | missing-emissions\">\n <ul class=\"is-list-style-disc is-pl-4\">\n @for (term of missingEmissions(); track trackByTerm($index, term); let l = $last) {\n <li>\n <he-node-link class=\"is-inline-block is-pl-1\" linkClass=\"is-dark\" [node]=\"term\">\n <span class=\"is-nowrap has-text-ellipsis\">{{ term.name }}</span>\n </he-node-link>\n </li>\n }\n </ul>\n </div>\n } @else if (logs()?.included_emissions) {\n <span>\n All {{ logs().included_emissions }} {{ 'emission' | pluralize: +logs().included_emissions }} for\n this product are included.\n </span>\n }\n </td>\n }\n </tr>\n <tr>\n <td>The aggregation is based on data from over {{ minObservations() }} Cycles</td>\n <td [class.has-text-success]=\"validScores()[1]\" [class.has-text-danger]=\"!validScores()[1]\">\n {{ validScores()[1] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n <span>Based on {{ observations() }} {{ 'Cycle' | pluralize: observations() }}.</span>\n </td>\n }\n </tr>\n @if (isCrop()) {\n <tr>\n <td>\n The difference between yield per hectare here and FAOSTAT is less than \u00B1{{\n logs().yield_delta_min || logs().delta_min | number\n }}%\n </td>\n <td [class.has-text-success]=\"validScores()[2]\" [class.has-text-danger]=\"!validScores()[2]\">\n {{ validScores()[2] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (hasFaostatYield()) {\n <span>FAOSTAT yield =</span>\n <span class=\"is-pl-1\">{{ logs()?.faostat_yield | precision: 3 }}</span>\n <span class=\"is-pl-1\">kg/ha;</span>\n <span class=\"is-pl-1\">Aggregated data yield =</span>\n <span class=\"is-pl-1\">{{ logs()?.product_yield | precision: 3 }}</span>\n <span class=\"is-pl-1\">kg/ha.</span>\n } @else {\n No yield found on FAOSTAT for this product.\n }\n </td>\n }\n </tr>\n }\n <tr>\n <td>\n Data completeness is\n <code class=\"is-p-1\">true</code>\n for all priority areas\n </td>\n <td [class.has-text-success]=\"validScores()[3]\" [class.has-text-danger]=\"!validScores()[3]\">\n {{ validScores()[3] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (missingCompletenessFields()?.length) {\n <span>{{ missingCompletenessFields().length }}</span>\n <span class=\"is-pl-1\">completeness areas missing data:</span>\n @for (field of missingCompletenessFields(); track field; let l = $last) {\n <a class=\"is-inline-block is-pl-1 is-dark\" [href]=\"completenessUrl(field)\" target=\"_blank\">\n {{ field }}\n </a>\n <span>{{ l ? '.' : ',' }}</span>\n }\n }\n </td>\n }\n </tr>\n @if (isCrop() && isGlobal()) {\n <tr>\n <td>Aggregated countries represent at least 75% of {{ countryName() }} production</td>\n <td [class.has-text-success]=\"validScores()[4]\" [class.has-text-danger]=\"!validScores()[4]\">\n {{ validScores()[4] ? 'Yes' : 'No' }}\n </td>\n @if (isCycle()) {\n <td>\n @if (hasProductionQuantity()) {\n <span>{{ countryName() }} production quantity =</span>\n <span class=\"is-pl-1\">{{ regionProductionQuantity() / 1000000 | number: '1.0-0' }}</span>\n <span class=\"is-pl-1\">million tonnes;</span>\n <span class=\"is-pl-1\">Production of countries included in the aggregation =</span>\n <span class=\"is-pl-1\">{{ countriesProductionQuantity() / 1000000 | number: '1.0-0' }}</span>\n <span class=\"is-pl-1\">million tonnes.</span>\n } @else {\n No production quantity found on FAOSTAT for this product.\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n</ng-template>\n", styles: [":host{display:inline-block}::ng-deep .quality-tooltip{min-width:500px}.missing-emissions{max-height:250px;overflow-y:auto}\n"] }]
|
|
12624
12820
|
}], propDecorators: { hidden: [{
|
|
12625
12821
|
type: HostBinding,
|
|
12626
12822
|
args: ['class.is-none']
|