@hestia-earth/ui-components 0.0.4 → 0.0.7

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.
Files changed (91) hide show
  1. package/auth/{public-api.d.ts → index.d.ts} +0 -0
  2. package/bibliographies/{public-api.d.ts → index.d.ts} +0 -0
  3. package/bundles/hestia-earth-ui-components.umd.js +1607 -1673
  4. package/bundles/hestia-earth-ui-components.umd.js.map +1 -1
  5. package/common/blank-node-state/blank-node-state.component.d.ts +1 -1
  6. package/common/blank-node-value-delta/blank-node-value-delta.component.d.ts +2 -2
  7. package/common/common.service.d.ts +1 -2
  8. package/common/delta-utils.d.ts +20 -0
  9. package/common/{public-api.d.ts → index.d.ts} +2 -0
  10. package/common/toast/toast.component.d.ts +2 -2
  11. package/common/toast.service.d.ts +3 -3
  12. package/common/utils.d.ts +8 -26
  13. package/cycles/{public-api.d.ts → index.d.ts} +1 -0
  14. package/engine/aggregation-engine.service.d.ts +13 -25
  15. package/engine/engine.service.d.ts +2 -6
  16. package/engine/index.d.ts +2 -0
  17. package/esm2015/auth/index.js +2 -0
  18. package/esm2015/bibliographies/index.js +3 -0
  19. package/esm2015/common/blank-node-diffs/blank-node-diffs.component.js +3 -4
  20. package/esm2015/common/blank-node-value-delta/blank-node-value-delta.component.js +4 -4
  21. package/esm2015/common/common.service.js +1 -7
  22. package/esm2015/common/delta-utils.js +54 -0
  23. package/esm2015/common/index.js +33 -0
  24. package/esm2015/common/toast/toast.component.js +3 -3
  25. package/esm2015/common/toast.service.js +5 -5
  26. package/esm2015/common/utils.js +20 -84
  27. package/esm2015/cycles/index.js +12 -0
  28. package/esm2015/engine/aggregation-engine.service.js +11 -47
  29. package/esm2015/engine/engine.service.js +9 -15
  30. package/esm2015/engine/index.js +3 -0
  31. package/esm2015/files/index.js +5 -0
  32. package/esm2015/fontawesome/index.js +2 -0
  33. package/esm2015/impact-assessments/impact-assessments-products/impact-assessments-products.component.js +3 -3
  34. package/esm2015/impact-assessments/index.js +7 -0
  35. package/esm2015/mendeley/index.js +2 -0
  36. package/esm2015/mendeley/mendeley.service.js +1 -1
  37. package/esm2015/node/index.js +14 -0
  38. package/esm2015/node/node-csv.service.js +1 -1
  39. package/esm2015/node/node-diffs/node-diffs.component.js +5 -5
  40. package/esm2015/node/node-diffs/node-diffs.model.js +6 -6
  41. package/esm2015/node/node.service.js +26 -16
  42. package/esm2015/public-api.js +16 -16
  43. package/esm2015/schema/index.js +2 -0
  44. package/esm2015/schema/schema.service.js +1 -1
  45. package/esm2015/search/index.js +4 -0
  46. package/esm2015/search/search.service.js +1 -1
  47. package/esm2015/sites/index.js +6 -0
  48. package/esm2015/terms/index.js +2 -0
  49. package/esm2015/users/index.js +2 -0
  50. package/fesm2015/hestia-earth-ui-components.js +827 -919
  51. package/fesm2015/hestia-earth-ui-components.js.map +1 -1
  52. package/files/{public-api.d.ts → index.d.ts} +2 -0
  53. package/fontawesome/{public-api.d.ts → index.d.ts} +0 -0
  54. package/impact-assessments/impact-assessments-products/impact-assessments-products.component.d.ts +2 -2
  55. package/impact-assessments/{public-api.d.ts → index.d.ts} +0 -0
  56. package/mendeley/{public-api.d.ts → index.d.ts} +0 -0
  57. package/mendeley/mendeley.service.d.ts +2 -2
  58. package/node/{public-api.d.ts → index.d.ts} +1 -0
  59. package/node/node-csv.service.d.ts +1 -1
  60. package/node/node-diffs/node-diffs.component.d.ts +3 -3
  61. package/node/node-diffs/node-diffs.model.d.ts +1 -1
  62. package/node/node.service.d.ts +16 -7
  63. package/package.json +1 -1
  64. package/public-api.d.ts +15 -15
  65. package/schema/{public-api.d.ts → index.d.ts} +0 -0
  66. package/schema/schema.service.d.ts +1 -1
  67. package/search/{public-api.d.ts → index.d.ts} +0 -0
  68. package/search/search.service.d.ts +2 -2
  69. package/sites/{public-api.d.ts → index.d.ts} +0 -0
  70. package/terms/{public-api.d.ts → index.d.ts} +0 -0
  71. package/users/{public-api.d.ts → index.d.ts} +0 -0
  72. package/common/blank-node-diffs/blank-node-diffs.service.d.ts +0 -5
  73. package/common/blank-node-value-delta/blank-node-value-delta.service.d.ts +0 -6
  74. package/engine/public-api.d.ts +0 -2
  75. package/esm2015/auth/public-api.js +0 -2
  76. package/esm2015/bibliographies/public-api.js +0 -3
  77. package/esm2015/common/blank-node-diffs/blank-node-diffs.service.js +0 -20
  78. package/esm2015/common/blank-node-value-delta/blank-node-value-delta.service.js +0 -31
  79. package/esm2015/common/public-api.js +0 -31
  80. package/esm2015/cycles/public-api.js +0 -11
  81. package/esm2015/engine/public-api.js +0 -3
  82. package/esm2015/files/public-api.js +0 -3
  83. package/esm2015/fontawesome/public-api.js +0 -2
  84. package/esm2015/impact-assessments/public-api.js +0 -7
  85. package/esm2015/mendeley/public-api.js +0 -2
  86. package/esm2015/node/public-api.js +0 -13
  87. package/esm2015/schema/public-api.js +0 -2
  88. package/esm2015/search/public-api.js +0 -4
  89. package/esm2015/sites/public-api.js +0 -6
  90. package/esm2015/terms/public-api.js +0 -2
  91. package/esm2015/users/public-api.js +0 -2
@@ -14,10 +14,10 @@ import * as i1 from '@fortawesome/angular-fontawesome';
14
14
  import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
15
15
  import { faClone, faCircle, faIdBadge } from '@fortawesome/free-regular-svg-icons';
16
16
  import { faAngleDoubleLeft, faAngleDoubleRight, faAngleDown, faAngleLeft, faAngleRight, faBookOpen, faBuilding, faCalculator, faChartBar, faCheck, faClipboard, faClipboardList, faClone as faClone$1, faComments, faDownload, faDrawPolygon, faEdit, faExclamationTriangle, faExternalLinkAlt, faFilter, faList, faLongArrowAltDown, faLongArrowAltUp, faLongArrowAltLeft, faLongArrowAltRight, faMap, faMapMarked, faMapMarkedAlt, faPlus, faPlusCircle, faSearch, faSeedling, faSpellCheck, faSpinner, faTimes, faUser } from '@fortawesome/free-solid-svg-icons';
17
- import { isEmpty, unique, isUndefined, isNumber, toPrecision, isBoolean, ConvertUnits, converters, convertValue, isEqual, diffInDays } from '@hestia-earth/utils';
18
17
  import { getColor } from 'random-material-color';
19
- import { EmissionMethodTier, NodeType, TermTermType, nestedSearchableKeys, isExpandable, SchemaType, SCHEMA_VERSION, sortKeysByType, CycleFunctionalUnit, SiteSiteType, isTypeValid, isTypeNode, typeToSchemaType } from '@hestia-earth/schema';
18
+ import { EmissionMethodTier, NodeType, isExpandable, TermTermType, nestedSearchableKeys, SchemaType, SCHEMA_VERSION, sortKeysByType, CycleFunctionalUnit, SiteSiteType, isTypeValid, isTypeNode, typeToSchemaType } from '@hestia-earth/schema';
20
19
  import { fileToExt, nodeTypeToParam, DataState, SupportedExtensions } from '@hestia-earth/api';
20
+ import { unique, isEmpty, isUndefined, isNumber, toPrecision, isBoolean, ConvertUnits, converters, convertValue, isEqual, diffInDays } from '@hestia-earth/utils';
21
21
  import { of, ReplaySubject, forkJoin, from, zip } from 'rxjs';
22
22
  import { catchError, map, take, mergeMap, debounceTime, distinctUntilChanged, tap, switchMap, reduce, filter, distinct, toArray, mergeAll, groupBy } from 'rxjs/operators';
23
23
  import { __rest, __awaiter } from 'tslib';
@@ -33,10 +33,10 @@ import { headersFromCsv, toCsv as toCsv$2, toJson } from '@hestia-earth/schema-c
33
33
  import { isCSVIncluded, isDefaultCSVSelected } from '@hestia-earth/json-schema/schema-utils';
34
34
  import { create, formatters } from 'jsondiffpatch';
35
35
  import { Chart } from 'chart.js';
36
- import MarkerClusterer from '@google/markerclustererplus';
37
36
  import * as moment from 'moment';
38
37
  import 'moment/locale/en-gb';
39
38
  import { v4 } from 'uuid';
39
+ import MarkerClusterer from '@google/markerclustererplus';
40
40
  import ChartDataLabels from 'chartjs-plugin-datalabels';
41
41
 
42
42
  class HeAuthService {
@@ -85,9 +85,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
85
85
  }]
86
86
  }], ctorParameters: function () { return [{ type: i1.FaIconLibrary }]; } });
87
87
 
88
- const get$5 = require('lodash.get');
88
+ const get$4 = require('lodash.get');
89
89
  const gitHome = 'https://gitlab.com/hestia-earth';
90
90
  const gitRawBaseUrl = 'https://glcdn.githack.com/hestia-earth';
91
+ const gitBranch = () => ['dev', 'staging'].some(env => baseUrl().includes(env)) ? 'develop' : 'master';
91
92
  const isChrome = () => window.navigator.userAgent.includes('Chrome');
92
93
  const baseUrl = () => window.location.origin.includes('localhost') ?
93
94
  'https://www-dev.hestia.earth' :
@@ -96,18 +97,8 @@ const baseUrl = () => window.location.origin.includes('localhost') ?
96
97
  'https://www.hestia.earth';
97
98
  const isExternal = () => baseUrl() !== window.location.origin;
98
99
  ;
99
- const loadScript = (src) => new Promise((resolve, reject) => {
100
- if (!src || document.body.querySelectorAll('script[src="' + src + '"]').length > 0) {
101
- return resolve({});
102
- }
103
- const script = document.createElement('script');
104
- script.onload = resolve;
105
- script.onerror = reject;
106
- script.src = src;
107
- document.body.appendChild(script);
108
- });
109
100
  const parseErrorStatus = (error) => ((error === null || error === void 0 ? void 0 : error.statusText) || '').toLowerCase().replace(/\s/g, '-');
110
- const parseErrorMessage = (error) => get$5(error, 'error.error', get$5(error, 'error.message', get$5(error, 'error', get$5(error, 'message', error))));
101
+ const parseErrorMessage = (error) => get$4(error, 'error.error', get$4(error, 'error.message', get$4(error, 'error', get$4(error, 'message', error))));
111
102
  const handleAPIError = (error) => {
112
103
  try {
113
104
  error = parseErrorMessage(error);
@@ -122,20 +113,6 @@ const errorText = (error) => {
122
113
  const err = parseErrorMessage(error);
123
114
  return parseErrorStatus(err) || err;
124
115
  };
125
- const toFormData = (data, formData = new FormData()) => {
126
- for (const key in data) {
127
- if (data.hasOwnProperty(key)) {
128
- const value = data[key];
129
- if (typeof value === 'object') {
130
- formData.append(key, JSON.stringify(value));
131
- }
132
- else {
133
- formData.append(key, value);
134
- }
135
- }
136
- }
137
- return formData;
138
- };
139
116
  const filterParams = (obj) => {
140
117
  const res = {};
141
118
  Object.keys(obj).sort().forEach(key => {
@@ -146,13 +123,6 @@ const filterParams = (obj) => {
146
123
  });
147
124
  return res;
148
125
  };
149
- const toSearchParams = (params = {}) => {
150
- const searchParams = new URLSearchParams();
151
- Object.keys(params)
152
- .filter(key => typeof params[key] !== 'undefined')
153
- .forEach(key => searchParams.append(key, `${params[key]}`));
154
- return searchParams;
155
- };
156
126
  const waitFor = (variable, callback) => {
157
127
  if (variable in window) {
158
128
  return callback();
@@ -166,23 +136,6 @@ const scrollToEl = (id, retries = 0) => {
166
136
  setTimeout(() => el ? el.scrollIntoView() : (retries < 10 ? scrollToEl(id, retries + 1) : null), 100);
167
137
  };
168
138
  const scrollTop = () => window.scrollTo(0, 0);
169
- const animateCounter = (total, callback) => {
170
- let animationDuration = 10;
171
- const step = Math.pow(10, `${total}`.length - 1);
172
- const maxSteps = parseInt(`${total / step}`, 10);
173
- let current = 0;
174
- const interval = setInterval(() => {
175
- callback(current);
176
- current += (step / animationDuration);
177
- if (current > maxSteps * step) {
178
- animationDuration = 100;
179
- }
180
- if (current > total) {
181
- callback(total);
182
- clearInterval(interval);
183
- }
184
- }, animationDuration);
185
- };
186
139
  const safeJSONParse = (value, defaultValue) => {
187
140
  try {
188
141
  return typeof value === 'string' ? JSON.parse(value) : value;
@@ -241,7 +194,7 @@ const groupNodesByTerm = (nodes = [], key, originalValues = [], filterMethod) =>
241
194
  group[termId].values[nodeId].nodes.push(blankNode);
242
195
  group[termId].values[nodeId].value = concatBlankNodeValue(group[termId].values[nodeId].value, blankNode.value);
243
196
  grouppedValueKeys.forEach(arrayKey => {
244
- const newValue = get$5(blankNode, arrayKey, []);
197
+ const newValue = get$4(blankNode, arrayKey, []);
245
198
  group[termId].values[nodeId][arrayKey] = [
246
199
  ...(group[termId].values[nodeId][arrayKey] || []),
247
200
  ...(Array.isArray(newValue) ? newValue : [newValue])
@@ -254,7 +207,7 @@ const groupNodesByTerm = (nodes = [], key, originalValues = [], filterMethod) =>
254
207
  Object.keys(group.values).map(nodeId => {
255
208
  const { index } = group.values[nodeId];
256
209
  const termId = group.term['@id'];
257
- const originalValue = get$5(originalValues, `[${index}].${key}`, []).filter((val) => val.term['@id'] === termId);
210
+ const originalValue = get$4(originalValues, `[${index}].${key}`, []).filter((val) => val.term['@id'] === termId);
258
211
  if (originalValue.length > 0) {
259
212
  const value = originalValue.reduce((array, curr) => concatBlankNodeValue(array, curr.value), []);
260
213
  group.originalValues[nodeId] = { value: propertyValue$1(value) };
@@ -263,7 +216,6 @@ const groupNodesByTerm = (nodes = [], key, originalValues = [], filterMethod) =>
263
216
  });
264
217
  return groups;
265
218
  };
266
- const sortByFn = (key, asc) => (a, b) => asc ? get$5(a, key, '').localeCompare(get$5(b, key, '')) : get$5(b, key, '').localeCompare(get$5(a, key, ''));
267
219
  const ellipsis = (text = '', maxlength = 20) => text.length > maxlength ? `${text.substring(0, maxlength)}...` : text;
268
220
  const mapsQuery = 'https://www.google.com/maps/search/?api=1&query=';
269
221
  const mapsUrl = (location) => location ? (location.lat && location.lng ?
@@ -283,8 +235,6 @@ const toDashCase = (value) => value ?
283
235
  // handle years
284
236
  .replace(/([0-9]{4})/g, g => `-${g}`) :
285
237
  null;
286
- const nodeTypeToString = (type) => `${type.charAt(0).toLowerCase()}${type.substring(1)}`;
287
- const parseNodeType = (type) => Object.values(NodeType).find(v => v.toString().toLowerCase() === (type || '').toLowerCase());
288
238
  const nodeDefaultLabel = {
289
239
  [NodeType.ImpactAssessment]: ({ name, country, endDate, product }) => name ? name.replace(`${product === null || product === void 0 ? void 0 : product.name}, `, '') : [
290
240
  product === null || product === void 0 ? void 0 : product.name,
@@ -296,47 +246,8 @@ const nodeDefaultLabel = {
296
246
  const defaultLabel = (node) => node ? (node['@type'] in nodeDefaultLabel ? nodeDefaultLabel[node['@type']](node) : node.name) || node['@id'] || node.id : '';
297
247
  const itemColor = (index) => getColor({ text: `${index}` });
298
248
  const listColor = (_v, index) => itemColor(index);
299
- const minutesBefore = (date, minutes = 1) => {
300
- date.setMinutes(date.getMinutes() - minutes);
301
- return date;
302
- };
303
- const hoursBefore = (date, hours = 1) => {
304
- date.setHours(date.getHours() - hours);
305
- return date;
306
- };
307
- const repeat = (times = 0) => (Array.from(Array(times), Math.random));
308
- var DeltaDisplayType;
309
- (function (DeltaDisplayType) {
310
- DeltaDisplayType["absolute"] = "absolute";
311
- DeltaDisplayType["percent"] = "percent";
312
- })(DeltaDisplayType || (DeltaDisplayType = {}));
313
- const deltaPerType = {
314
- [DeltaDisplayType.absolute]: (value, original) => value - original,
315
- [DeltaDisplayType.percent]: (value, original) => ((value - original) / original) * 100
316
- };
317
- const roundValue = (value) => +`${value}`.substring(0, 10);
318
- const delta = (value, originalValue, displayType = DeltaDisplayType.percent, customDeltaFuncs) => {
319
- const vvalue = roundValue(propertyValue$1(value));
320
- const voriginalValue = roundValue(propertyValue$1(originalValue));
321
- const deltaFuncs = Object.assign(Object.assign({}, deltaPerType), customDeltaFuncs);
322
- const diff = vvalue === voriginalValue ? 0 : deltaFuncs[displayType](vvalue, voriginalValue);
323
- return Number.isFinite(diff) ? (diff === -0 ? 0 : diff) : 0;
324
- };
325
- var DeltaColour;
326
- (function (DeltaColour) {
327
- DeltaColour["Success"] = "success";
328
- DeltaColour["Warning"] = "warning";
329
- DeltaColour["Danger"] = "danger";
330
- })(DeltaColour || (DeltaColour = {}));
331
- const emptyValue = (value) => isEmpty(value) || isNaN(propertyValue$1(value));
332
- const filenameWithoutExt = (filename = '') => {
333
- const file = fileToExt(filename, '');
334
- // remove last .
335
- return file.endsWith('.') ? file.substring(0, file.length - 1) : file;
336
- };
337
-
338
249
  const reduceValues$1 = (values) => values.length ? values.reduce((p, v) => p + propertyValue$1(v.value), 0) : undefined;
339
- const formatValues = (originalValues = [], recalculatedValues = []) => {
250
+ const formatDiffValues = (originalValues = [], recalculatedValues = []) => {
340
251
  const originalValuesFiltered = originalValues.filter((value) => !value.deleted);
341
252
  const recalculatedValuesFiltered = recalculatedValues.filter((value) => !value.deleted);
342
253
  const terms = unique([...originalValuesFiltered, ...recalculatedValuesFiltered].map((v) => v.term['@id'])).sort();
@@ -352,9 +263,39 @@ const formatValues = (originalValues = [], recalculatedValues = []) => {
352
263
  });
353
264
  return values;
354
265
  };
266
+ const emptyValue = (value) => isEmpty(value) || isNaN(propertyValue$1(value));
267
+ const repeat = (times = 0) => (Array.from(Array(times), Math.random));
268
+ const filenameWithoutExt = (filename = '') => {
269
+ const file = fileToExt(filename, '');
270
+ // remove last .
271
+ return file.endsWith('.') ? file.substring(0, file.length - 1) : file;
272
+ };
355
273
 
356
274
  const SUCCESS_CRITERION_MAX_DELTA_PERCENT = 5;
357
275
  const WARNING_CRITERION_MAX_DELTA_PERCENT = 20;
276
+ var DeltaColour;
277
+ (function (DeltaColour) {
278
+ DeltaColour["Success"] = "success";
279
+ DeltaColour["Warning"] = "warning";
280
+ DeltaColour["Danger"] = "danger";
281
+ })(DeltaColour || (DeltaColour = {}));
282
+ var DeltaDisplayType;
283
+ (function (DeltaDisplayType) {
284
+ DeltaDisplayType["absolute"] = "absolute";
285
+ DeltaDisplayType["percent"] = "percent";
286
+ })(DeltaDisplayType || (DeltaDisplayType = {}));
287
+ const deltaPerType = {
288
+ [DeltaDisplayType.absolute]: (value, original) => value - original,
289
+ [DeltaDisplayType.percent]: (value, original) => ((value - original) / original) * 100
290
+ };
291
+ const roundValue = (value) => +`${value}`.substring(0, 10);
292
+ const delta = (value, originalValue, displayType = DeltaDisplayType.percent, mapping) => {
293
+ const vvalue = roundValue(propertyValue$1(value));
294
+ const voriginalValue = roundValue(propertyValue$1(originalValue));
295
+ const deltaFuncs = Object.assign(Object.assign({}, deltaPerType), mapping);
296
+ const diff = vvalue === voriginalValue ? 0 : deltaFuncs[displayType](vvalue, voriginalValue);
297
+ return Number.isFinite(diff) ? (diff === -0 ? 0 : diff) : 0;
298
+ };
358
299
  var PercentDeltaConditions;
359
300
  (function (PercentDeltaConditions) {
360
301
  PercentDeltaConditions["recalculated0"] = "recalculated should be 0";
@@ -377,9 +318,9 @@ const calculatePercentDelta = (recalculated, original) => {
377
318
  const customDeltaFuncs = {
378
319
  [DeltaDisplayType.percent]: calculatePercentDelta
379
320
  };
380
- const evaluateSuccess = (delta) => Math.abs(delta) < SUCCESS_CRITERION_MAX_DELTA_PERCENT
321
+ const evaluateSuccess = (deltaValue) => Math.abs(deltaValue) < SUCCESS_CRITERION_MAX_DELTA_PERCENT
381
322
  ? DeltaColour.Success
382
- : Math.abs(delta) < WARNING_CRITERION_MAX_DELTA_PERCENT
323
+ : Math.abs(deltaValue) < WARNING_CRITERION_MAX_DELTA_PERCENT
383
324
  ? DeltaColour.Warning
384
325
  : DeltaColour.Danger;
385
326
 
@@ -461,7 +402,7 @@ class BlankNodeDiffsComponent {
461
402
  this.values = [];
462
403
  }
463
404
  ngOnInit() {
464
- this.values = formatValues(this.originalValues, this.recalculatedValues);
405
+ this.values = formatDiffValues(this.originalValues, this.recalculatedValues);
465
406
  }
466
407
  }
467
408
  BlankNodeDiffsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: BlankNodeDiffsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -479,210 +420,187 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
479
420
  type: Input
480
421
  }] } });
481
422
 
482
- const searchableTypes = [
483
- NodeType.Cycle,
484
- NodeType.Source
485
- ];
486
- const serializeSearchFilters = (filters) => Object.keys(filters || {})
487
- .filter(key => filters[key].length)
488
- .map(key => `${key}=${filters[key].filter(Boolean).join('|')}`)
489
- .join(';');
490
- const deserializeSearchFilters = (filters) => (filters || '').split(';').reduce((prev, curr) => {
491
- const [key, values] = curr.split('=');
492
- prev[key] = (values || '').split('|').filter(Boolean);
493
- return prev;
494
- }, {});
495
- const matchType = (type) => Object.freeze({
496
- match: { '@type': type }
497
- });
498
- const matchTermType = (termType) => Object.freeze({
499
- match: { termType }
500
- });
501
- const matchRegex = (key, value) => Object.freeze({
502
- regexp: { [key]: { value: value.toString() } }
503
- });
504
- const matchQuery = (key, value, boost) => Object.freeze({
505
- match: {
506
- [key]: Object.assign({ query: value }, (boost ? { boost } : {}))
507
- }
508
- });
509
- const matchExactQuery = (key, value, boost) => matchQuery(`${key}.keyword`, value, boost);
510
- const matchNameNormalized = (query, boost = 20) => Object.freeze({
511
- match: { nameNormalized: { query, boost } }
512
- });
513
- const numberGte = (key, value) => Object.freeze({
514
- range: { [key]: { gte: value } }
423
+ const termProperties = (term) => Object.keys(term).filter(key => !isExpandable(term[key]) && ![
424
+ 'pinned', 'expanded', 'extended', 'selected', 'loading',
425
+ '_score', '@type', '@id', '@context', 'createdAt',
426
+ 'name', 'synonyms', 'termType', 'location', 'properties', 'schemaVersion'
427
+ ].includes(key));
428
+ const findPropertyById = ({ defaultProperties }, key) => (defaultProperties || []).find(({ term }) => term['@id'] === key);
429
+ const termLocationName = ({ gadmFullName }) => ({
430
+ name: gadmFullName || ''
515
431
  });
516
- const multiMatchQuery = (query, fields, type = 'best_fields', boost, analyzer) => Object.freeze({
517
- multi_match: Object.assign(Object.assign({ query,
518
- fields,
519
- type }, (boost ? { boost } : {})), (analyzer ? { analyzer } : {}))
432
+ const termLocation = ({ latitude, longitude }) => ({
433
+ lat: latitude,
434
+ lng: longitude
520
435
  });
521
- const matchPhraseQuery = (query, boost, ...fields) => fields.length ? multiMatchQuery(query, fields, 'phrase', boost) : matchExactQuery('name', query, boost);
522
- const matchPhrasePrefixQuery = (query, boost = 2, ...fields) => fields.length ?
523
- multiMatchQuery(query, fields, 'phrase_prefix', boost) :
524
- Object.freeze({
525
- match_phrase_prefix: {
526
- nameSearchAsYouType: {
527
- query,
528
- boost
529
- }
530
- }
531
- });
532
- const matchBoolPrefixQuery = (query, boost = 1, ...fields) => fields.length ?
533
- multiMatchQuery(query, fields, 'bool_prefix', boost) :
534
- Object.freeze({
535
- match_bool_prefix: {
536
- name: {
537
- query,
538
- boost
539
- }
540
- }
541
- });
542
- const wildcardQuery = (query, boost = 3, ...fields) => Object.freeze({
543
- simple_query_string: {
544
- query: `*${query}*`,
545
- fields,
546
- analyze_wildcard: true,
547
- boost
436
+ const termTypeToLabel = {
437
+ [TermTermType.methodEmissionResourceUse]: 'Method (Emissions)',
438
+ [TermTermType.methodMeasurement]: 'Method (Measurement)',
439
+ [TermTermType.pesticideAI]: 'Pesticide Active Ingredient',
440
+ [TermTermType.standardsLabels]: 'Standards & Labels',
441
+ [TermTermType.usdaSoilType]: 'USDA Soil Type'
442
+ };
443
+ const termTypeLabel = (type = 'N/A') => type in termTypeToLabel ? termTypeToLabel[type] : keyToLabel(type);
444
+ const termTypesToChildren = (termTypes) => termTypes.map(termType => ({ label: termTypeLabel(termType), termType }));
445
+ const groups = Object.freeze({
446
+ [TermTermType.emission]: {
447
+ label: 'Emissions & Resource Use',
448
+ termType: 'Emissions & Resource Use',
449
+ children: termTypesToChildren([
450
+ TermTermType.emission,
451
+ TermTermType.resourceUse,
452
+ TermTermType.model,
453
+ TermTermType.characterisedIndicator,
454
+ TermTermType.endpointIndicator,
455
+ TermTermType.methodEmissionResourceUse
456
+ ])
457
+ },
458
+ [TermTermType.region]: {
459
+ label: termTypeLabel(TermTermType.region),
460
+ termType: TermTermType.region,
461
+ children: []
462
+ },
463
+ infrastructure: {
464
+ label: 'Infrastructure & Equipment',
465
+ termType: 'Infrastructure & Equipment',
466
+ children: termTypesToChildren([
467
+ TermTermType.building,
468
+ TermTermType.cropProtection,
469
+ TermTermType.cropSupport,
470
+ TermTermType.irrigation,
471
+ TermTermType.machinery
472
+ ])
473
+ },
474
+ input: {
475
+ label: 'Inputs',
476
+ termType: 'Inputs',
477
+ children: termTypesToChildren([
478
+ TermTermType.antibiotic,
479
+ TermTermType.electricity,
480
+ TermTermType.fuel,
481
+ TermTermType.material,
482
+ TermTermType.inorganicFertilizer,
483
+ TermTermType.organicFertilizer,
484
+ TermTermType.other,
485
+ TermTermType.pesticideAI,
486
+ TermTermType.pesticideBrandName,
487
+ TermTermType.soilAmendment,
488
+ TermTermType.transport,
489
+ TermTermType.water
490
+ ])
491
+ },
492
+ [TermTermType.measurement]: {
493
+ label: 'Measurements',
494
+ termType: 'Measurements',
495
+ children: termTypesToChildren([
496
+ TermTermType.measurement,
497
+ TermTermType.soilTexture,
498
+ TermTermType.soilType,
499
+ TermTermType.usdaSoilType,
500
+ TermTermType.methodMeasurement
501
+ ])
502
+ },
503
+ practice: {
504
+ label: 'Practices',
505
+ termType: 'Production Practices',
506
+ children: termTypesToChildren([
507
+ TermTermType.animalManagement,
508
+ TermTermType.aquacultureManagement,
509
+ TermTermType.biodiversity,
510
+ TermTermType.cropEstablishment,
511
+ TermTermType.cropResidueManagement,
512
+ TermTermType.excretaManagement,
513
+ TermTermType.landUseManagement,
514
+ TermTermType.operation,
515
+ TermTermType.standardsLabels,
516
+ TermTermType.system,
517
+ TermTermType.tillage,
518
+ TermTermType.waterRegime,
519
+ ])
520
+ },
521
+ product: {
522
+ label: 'Products',
523
+ termType: 'Products',
524
+ children: termTypesToChildren([
525
+ TermTermType.crop,
526
+ TermTermType.cropResidue,
527
+ TermTermType.excreta,
528
+ TermTermType.liveAnimal,
529
+ TermTermType.liveAquaticSpecies,
530
+ TermTermType.animalProduct,
531
+ TermTermType.processedFood
532
+ ])
533
+ },
534
+ [TermTermType.property]: {
535
+ label: termTypeLabel(TermTermType.property),
536
+ termType: TermTermType.property,
537
+ children: []
548
538
  }
549
539
  });
550
- const matchCountryLevel = { match: { gadmLevel: 0 } };
551
- const matchGlobalRegion = { regexp: { '@id': 'region-*' } };
552
- const matchCountry = Object.freeze({
553
- bool: {
554
- should: [
555
- matchCountryLevel,
556
- matchGlobalRegion
557
- ],
558
- minimum_should_match: 1
559
- }
560
- });
561
- const countriesQuery = Object.freeze({
562
- bool: {
563
- must: [
564
- matchType(NodeType.Term),
565
- matchTermType(TermTermType.region),
566
- matchCountryLevel
567
- ]
568
- }
569
- });
570
- const allCountriesQuery = Object.freeze({
571
- bool: Object.assign({ must: [
572
- matchType(NodeType.Term),
573
- matchTermType(TermTermType.region)
574
- ] }, matchCountry.bool)
575
- });
576
- const matchRegion = numberGte('gadmLevel', 1);
577
- const regionsQuery = Object.freeze({
578
- bool: {
579
- must: [
580
- matchType(NodeType.Term),
581
- matchTermType(TermTermType.region),
582
- matchRegion
583
- ]
584
- }
585
- });
586
- const worldRegion = Object.freeze({
587
- '@id': 'region-world',
588
- name: 'World'
589
- });
590
- const cropsQuery = Object.freeze({
591
- bool: {
592
- must: [
593
- matchType(NodeType.Term),
594
- matchTermType(TermTermType.crop)
595
- ]
596
- }
597
- });
598
- const matchAggregatedQuery = matchQuery('aggregated', true);
599
- const isNestedKey = (key) => nestedSearchableKeys.includes(key.split('.')[0]);
600
- const matchNestedKey = (key, query) => isNestedKey(key) ? {
601
- nested: { path: key.split('.')[0], query }
602
- } : query;
603
- /**
604
- * List of fields to return in the search results.
605
- */
606
- const searchResultsFields = Object.freeze({
607
- [NodeType.Cycle]: [
608
- 'description', 'dataDescription', 'endDate',
609
- 'emissionsCount', 'inputsCount', 'productsCount',
610
- 'site.location', 'site.country.name', 'site.region.name'
611
- ],
612
- [NodeType.Source]: ['bibliography.title', 'bibliography.documentDOI'],
613
- [NodeType.ImpactAssessment]: ['emissionsResourceUseCount', 'impactsCount', 'country.name']
614
- });
615
- /**
616
- * List of fields to search in.
617
- */
618
- const searchFields = Object.freeze({
619
- [NodeType.Cycle]: ['name', 'description', 'dataDescription'],
620
- [NodeType.Source]: ['name', 'bibliography.title'],
621
- [NodeType.ImpactAssessment]: ['name', 'product.name', 'country.name']
622
- });
623
- const searchFieldsNested = Object.freeze({
624
- [NodeType.Cycle]: [
625
- 'inputs.term.name',
626
- 'emissions.term.name',
627
- 'practices.term.name'
628
- ],
629
- [NodeType.Source]: [],
630
- [NodeType.ImpactAssessment]: [
631
- 'emissionsResourceUse.term.name',
632
- 'impacts.term.name'
633
- ]
634
- });
635
- /**
636
- * Specific strict queries per type.
637
- */
638
- const searchQueries = {
639
- [NodeType.Cycle]: { must: [], must_not: [matchAggregatedQuery] },
640
- [NodeType.Source]: { must: [], must_not: [matchAggregatedQuery] }
641
- };
642
- const searchFiltersKeys = {
643
- [NodeType.Cycle]: key => key,
644
- [NodeType.Source]: key => key
645
- };
646
- /* eslint-disable complexity */
647
- const searchQuery = (type, query, filters, aggregated) => {
648
- const boolQuery = (Object.assign(Object.assign({ must: [
649
- matchType(type),
650
- aggregated ? matchAggregatedQuery : null
651
- ].filter(Boolean) }, (query ? {
652
- should: [
653
- matchPhraseQuery(query, 100, ...searchFields[type]),
654
- matchPhrasePrefixQuery(query, 20, ...searchFields[type]),
655
- matchBoolPrefixQuery(query, 10, ...searchFields[type]),
656
- ...searchFieldsNested[type].map(field => matchNestedKey(field, matchBoolPrefixQuery(query, 1, field)))
657
- ],
658
- minimum_should_match: 1
659
- } : {})), { must_not: [
660
- aggregated ? null : matchAggregatedQuery
661
- ].filter(Boolean) }));
662
- const keys = Object.keys(filters || {}).filter(key => filters[key].length);
663
- return {
664
- bool: keys.length ? {
665
- must: keys.map(key => {
666
- const filterKey = searchFiltersKeys[type](key);
667
- return {
668
- bool: {
669
- should: filters[key]
670
- .map(value => matchNestedKey(filterKey, matchExactQuery(filterKey, value)))
671
- .map(must => {
672
- const localQuery = JSON.parse(JSON.stringify(boolQuery));
673
- localQuery.must.push(must);
674
- return { bool: localQuery };
675
- }),
676
- minimum_should_match: 1
677
- }
678
- };
679
- })
680
- } : boolQuery
681
- };
540
+ const termTypeGroups = [
541
+ groups.property,
542
+ groups.region,
543
+ groups.input,
544
+ groups.emission,
545
+ groups.product,
546
+ groups.practice,
547
+ groups.measurement,
548
+ groups.infrastructure
549
+ ];
550
+ const termToParent = {
551
+ [TermTermType.property]: 'Properties',
552
+ [TermTermType.region]: 'Geographies'
682
553
  };
683
- /* eslint-enable complexity */
684
-
685
- const primaryProduct = ({ products }) => (products || []).find(({ primary }) => primary);
554
+ const termChildToParent = (termType) => termTypeGroups.find(({ children }) => (children || []).some(child => child.termType === termType)) ||
555
+ { termType: termToParent[termType] };
556
+ const sortOrder = [
557
+ TermTermType.property,
558
+ TermTermType.crop,
559
+ TermTermType.cropResidue,
560
+ TermTermType.animalProduct,
561
+ TermTermType.liveAnimal,
562
+ TermTermType.liveAquaticSpecies,
563
+ TermTermType.processedFood,
564
+ TermTermType.emission,
565
+ TermTermType.resourceUse,
566
+ TermTermType.inorganicFertilizer,
567
+ TermTermType.organicFertilizer,
568
+ TermTermType.soilAmendment,
569
+ TermTermType.soilTexture,
570
+ TermTermType.water,
571
+ TermTermType.material,
572
+ TermTermType.antibiotic,
573
+ TermTermType.electricity,
574
+ TermTermType.fuel,
575
+ TermTermType.aquacultureManagement,
576
+ TermTermType.biodiversity,
577
+ TermTermType.cropResidueManagement,
578
+ TermTermType.animalManagement,
579
+ TermTermType.waterRegime,
580
+ TermTermType.system,
581
+ TermTermType.tillage,
582
+ TermTermType.landUseManagement,
583
+ TermTermType.operation,
584
+ TermTermType.cropEstablishment,
585
+ TermTermType.measurement,
586
+ TermTermType.soilType,
587
+ TermTermType.usdaSoilType,
588
+ TermTermType.methodMeasurement,
589
+ TermTermType.building,
590
+ TermTermType.cropProtection,
591
+ TermTermType.cropSupport,
592
+ TermTermType.irrigation,
593
+ TermTermType.machinery,
594
+ TermTermType.model,
595
+ TermTermType.characterisedIndicator,
596
+ TermTermType.endpointIndicator,
597
+ TermTermType.methodEmissionResourceUse,
598
+ TermTermType.other,
599
+ TermTermType.region,
600
+ TermTermType.pesticideAI,
601
+ TermTermType.pesticideBrandName,
602
+ TermTermType.standardsLabels
603
+ ];
686
604
 
687
605
  const HE_API_BASE_URL = new InjectionToken('HE_API_BASE_URL');
688
606
  class HeCommonService {
@@ -692,11 +610,6 @@ class HeCommonService {
692
610
  get apiBaseUrl() {
693
611
  return this._apiBaseUrl || '/api';
694
612
  }
695
- get gitBranch() {
696
- return ['dev', 'staging'].some((env) => baseUrl().includes(env))
697
- ? 'develop'
698
- : 'master';
699
- }
700
613
  }
701
614
  HeCommonService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeCommonService, deps: [{ token: HE_API_BASE_URL }], target: i0.ɵɵFactoryTarget.Injectable });
702
615
  HeCommonService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeCommonService, providedIn: 'root' });
@@ -710,188 +623,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
710
623
  args: [HE_API_BASE_URL]
711
624
  }] }]; } });
712
625
 
713
- const termProperties = (term) => Object.keys(term).filter(key => !isExpandable(term[key]) && ![
714
- 'pinned', 'expanded', 'extended', 'selected', 'loading',
715
- '_score', '@type', '@id', '@context', 'createdAt',
716
- 'name', 'synonyms', 'termType', 'location', 'properties', 'schemaVersion'
717
- ].includes(key));
718
- const findPropertyById = ({ defaultProperties }, key) => (defaultProperties || []).find(({ term }) => term['@id'] === key);
719
- const termLocationName = ({ gadmFullName }) => ({
720
- name: gadmFullName || ''
721
- });
722
- const termLocation = ({ latitude, longitude }) => ({
723
- lat: latitude,
724
- lng: longitude
725
- });
726
- const termTypeToLabel = {
727
- [TermTermType.methodEmissionResourceUse]: 'Method (Emissions)',
728
- [TermTermType.methodMeasurement]: 'Method (Measurement)',
729
- [TermTermType.pesticideAI]: 'Pesticide Active Ingredient',
730
- [TermTermType.standardsLabels]: 'Standards & Labels',
731
- [TermTermType.usdaSoilType]: 'USDA Soil Type'
626
+ const nodeTypeUrl = (apiBaseUrl, type) => type ? `${apiBaseUrl}/${nodeTypeToParam(type)}` : '';
627
+ /**
628
+ * Get the full url to fetch the node data.
629
+ *
630
+ * @param apiBaseUrl The url of the API hosting the data.
631
+ * @param node The node with a type, id and optionally a dataState
632
+ * @returns The full url.
633
+ */
634
+ const nodeUrl = (apiBaseUrl, _a) => {
635
+ var { dataState } = _a, node = __rest(_a, ["dataState"]);
636
+ return `${nodeTypeUrl(apiBaseUrl, node['@type'] || node.type)}/${node['@id'] || node.id}${dataState ? `?dataState=${dataState}` : ''}`;
732
637
  };
733
- const termTypeLabel = (type = 'N/A') => type in termTypeToLabel ? termTypeToLabel[type] : keyToLabel(type);
734
- const termTypesToChildren = (termTypes) => termTypes.map(termType => ({ label: termTypeLabel(termType), termType }));
735
- const groups = Object.freeze({
736
- [TermTermType.emission]: {
737
- label: 'Emissions & Resource Use',
738
- termType: 'Emissions & Resource Use',
739
- children: termTypesToChildren([
740
- TermTermType.emission,
741
- TermTermType.resourceUse,
742
- TermTermType.model,
743
- TermTermType.characterisedIndicator,
744
- TermTermType.endpointIndicator,
745
- TermTermType.methodEmissionResourceUse
746
- ])
747
- },
748
- [TermTermType.region]: {
749
- label: termTypeLabel(TermTermType.region),
750
- termType: TermTermType.region,
751
- children: []
752
- },
753
- infrastructure: {
754
- label: 'Infrastructure & Equipment',
755
- termType: 'Infrastructure & Equipment',
756
- children: termTypesToChildren([
757
- TermTermType.building,
758
- TermTermType.cropProtection,
759
- TermTermType.cropSupport,
760
- TermTermType.irrigation,
761
- TermTermType.machinery
762
- ])
763
- },
764
- input: {
765
- label: 'Inputs',
766
- termType: 'Inputs',
767
- children: termTypesToChildren([
768
- TermTermType.antibiotic,
769
- TermTermType.electricity,
770
- TermTermType.fuel,
771
- TermTermType.material,
772
- TermTermType.inorganicFertilizer,
773
- TermTermType.organicFertilizer,
774
- TermTermType.other,
775
- TermTermType.pesticideAI,
776
- TermTermType.pesticideBrandName,
777
- TermTermType.soilAmendment,
778
- TermTermType.transport,
779
- TermTermType.water
780
- ])
781
- },
782
- [TermTermType.measurement]: {
783
- label: 'Measurements',
784
- termType: 'Measurements',
785
- children: termTypesToChildren([
786
- TermTermType.measurement,
787
- TermTermType.soilTexture,
788
- TermTermType.soilType,
789
- TermTermType.usdaSoilType,
790
- TermTermType.methodMeasurement
791
- ])
792
- },
793
- practice: {
794
- label: 'Practices',
795
- termType: 'Production Practices',
796
- children: termTypesToChildren([
797
- TermTermType.animalManagement,
798
- TermTermType.aquacultureManagement,
799
- TermTermType.biodiversity,
800
- TermTermType.cropEstablishment,
801
- TermTermType.cropResidueManagement,
802
- TermTermType.excretaManagement,
803
- TermTermType.landUseManagement,
804
- TermTermType.operation,
805
- TermTermType.standardsLabels,
806
- TermTermType.system,
807
- TermTermType.tillage,
808
- TermTermType.waterRegime,
809
- ])
810
- },
811
- product: {
812
- label: 'Products',
813
- termType: 'Products',
814
- children: termTypesToChildren([
815
- TermTermType.crop,
816
- TermTermType.cropResidue,
817
- TermTermType.excreta,
818
- TermTermType.liveAnimal,
819
- TermTermType.liveAquaticSpecies,
820
- TermTermType.animalProduct,
821
- TermTermType.processedFood
822
- ])
823
- },
824
- [TermTermType.property]: {
825
- label: termTypeLabel(TermTermType.property),
826
- termType: TermTermType.property,
827
- children: []
828
- }
829
- });
830
- const termTypeGroups = [
831
- groups.property,
832
- groups.region,
833
- groups.input,
834
- groups.emission,
835
- groups.product,
836
- groups.practice,
837
- groups.measurement,
838
- groups.infrastructure
839
- ];
840
- const termToParent = {
841
- [TermTermType.property]: 'Properties',
842
- [TermTermType.region]: 'Geographies'
638
+ const nodeLogsUrl = (apiBaseUrl, _a) => {
639
+ var { dataState } = _a, node = __rest(_a, ["dataState"]);
640
+ return `${nodeUrl(apiBaseUrl, node)}/log${dataState ? `?dataState=${dataState}` : ''}`;
843
641
  };
844
- const termChildToParent = (termType) => termTypeGroups.find(({ children }) => (children || []).some(child => child.termType === termType)) ||
845
- { termType: termToParent[termType] };
846
- const sortOrder = [
847
- TermTermType.property,
848
- TermTermType.crop,
849
- TermTermType.cropResidue,
850
- TermTermType.animalProduct,
851
- TermTermType.liveAnimal,
852
- TermTermType.liveAquaticSpecies,
853
- TermTermType.processedFood,
854
- TermTermType.emission,
855
- TermTermType.resourceUse,
856
- TermTermType.inorganicFertilizer,
857
- TermTermType.organicFertilizer,
858
- TermTermType.soilAmendment,
859
- TermTermType.soilTexture,
860
- TermTermType.water,
861
- TermTermType.material,
862
- TermTermType.antibiotic,
863
- TermTermType.electricity,
864
- TermTermType.fuel,
865
- TermTermType.aquacultureManagement,
866
- TermTermType.biodiversity,
867
- TermTermType.cropResidueManagement,
868
- TermTermType.animalManagement,
869
- TermTermType.waterRegime,
870
- TermTermType.system,
871
- TermTermType.tillage,
872
- TermTermType.landUseManagement,
873
- TermTermType.operation,
874
- TermTermType.cropEstablishment,
875
- TermTermType.measurement,
876
- TermTermType.soilType,
877
- TermTermType.usdaSoilType,
878
- TermTermType.methodMeasurement,
879
- TermTermType.building,
880
- TermTermType.cropProtection,
881
- TermTermType.cropSupport,
882
- TermTermType.irrigation,
883
- TermTermType.machinery,
884
- TermTermType.model,
885
- TermTermType.characterisedIndicator,
886
- TermTermType.endpointIndicator,
887
- TermTermType.methodEmissionResourceUse,
888
- TermTermType.other,
889
- TermTermType.region,
890
- TermTermType.pesticideAI,
891
- TermTermType.pesticideBrandName,
892
- TermTermType.standardsLabels
893
- ];
894
-
642
+ const lookupUrl = (filename) => filename.startsWith('http') ? filename : `${baseUrl()}/glossary/lookups/${filename}.csv`;
643
+ const termTypeLookupUrl = (termType) => `${baseUrl()}/glossary/${termChildToParent(termType).termType}/${termType}.csv`;
895
644
  class HeNodeService {
896
645
  constructor(http, authService, commonService) {
897
646
  this.http = http;
@@ -944,26 +693,18 @@ class HeNodeService {
944
693
  .catch(() => (responseType === 'json' ? ({}) : ''));
945
694
  }
946
695
  nodeTypeUrl(type) {
947
- return `${this.commonService.apiBaseUrl}/${nodeTypeToParam(type)}`;
696
+ return nodeTypeUrl(this.commonService.apiBaseUrl, type);
948
697
  }
949
- nodeUrl(_a) {
950
- var { dataState } = _a, node = __rest(_a, ["dataState"]);
951
- return `${this.nodeTypeUrl(node['@type'] || node.type)}/${node['@id'] || node.id}${dataState ? `?dataState=${dataState}` : ''}`;
952
- }
953
- nodeLogsUrl(_a) {
954
- var { dataState } = _a, node = __rest(_a, ["dataState"]);
955
- return `${this.nodeUrl(node)}/log${dataState ? `?dataState=${dataState}` : ''}`;
956
- }
957
- lookupUrl(filename) {
958
- return filename.startsWith('http') ? filename : `${baseUrl()}/glossary/lookups/${filename}.csv`;
698
+ nodeUrl(node) {
699
+ return nodeUrl(this.commonService.apiBaseUrl, node);
959
700
  }
960
- termTypeLookupUrl(termType) {
961
- return `${baseUrl()}/glossary/${termChildToParent(termType).termType}/${termType}.csv`;
701
+ nodeLogsUrl(node) {
702
+ return nodeLogsUrl(this.commonService.apiBaseUrl, node);
962
703
  }
963
704
  downloadLookup(filename) {
964
705
  return __awaiter(this, void 0, void 0, function* () {
965
706
  try {
966
- const url = this.lookupUrl(filename);
707
+ const url = lookupUrl(filename);
967
708
  return this.downloadRaw(url, 'text');
968
709
  }
969
710
  catch (_err) {
@@ -975,7 +716,7 @@ class HeNodeService {
975
716
  return __awaiter(this, void 0, void 0, function* () {
976
717
  try {
977
718
  const data = yield this.downloadLookup(termType);
978
- return (data === null || data === void 0 ? void 0 : data.startsWith('term.id')) ? this.lookupUrl(termType) : null;
719
+ return (data === null || data === void 0 ? void 0 : data.startsWith('term.id')) ? lookupUrl(termType) : null;
979
720
  }
980
721
  catch (_err) {
981
722
  return null;
@@ -992,42 +733,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
992
733
  }]
993
734
  }], ctorParameters: function () { return [{ type: i1$1.HttpClient }, { type: HeAuthService }, { type: HeCommonService }]; } });
994
735
 
995
- const get$4 = require('lodash.get');
736
+ const gitUrl$1 = () => `${gitHome}/hestia-aggregation-engine/-/blob/${gitBranch()}`;
737
+ const rawUrl$1 = () => `${gitRawBaseUrl}/hestia-aggregation-engine/-/raw/${gitBranch()}`;
996
738
  const lookups = Object.freeze({
997
739
  cropYield: 'region-crop-cropGroupingFaostatProduction-yield',
998
740
  cropGroupingColumn: 'cropGroupingFaostatProduction'
999
741
  });
1000
- const extractGroupedDataClosesDate = (data, year) => {
1001
- const dataByDate = (data || '').split(';').reduce((prev, curr) => (Object.assign(Object.assign({}, prev), (curr.length > 0 && curr.split(':')[1] !== '-' ? { [curr.split(':')[0]]: curr.split(':')[1] } : {}))), {});
1002
- const years = Object.keys(dataByDate).map(v => +v);
1003
- const closestYear = years.reduce((prev, curr) => Math.abs(curr - year) < Math.abs(prev - year) ? curr : prev, 0);
1004
- return closestYear ? +dataByDate[closestYear] / 10 : null;
1005
- };
1006
- const cropGrouping = (lookup, product) => (lookup.find(({ term }) => term.id === (product === null || product === void 0 ? void 0 : product.term['@id'])) || {})[lookups.cropGroupingColumn];
1007
- const cropYieldCountry = (cropYield, cycle) => cropYield.find(({ term }) => term.id === get$4(cycle, 'site.country.@id', worldRegion['@id'])) || {};
1008
- const extendCycle = ({ crop, cropYield }) => (cycle) => {
1009
- var _a, _b, _c;
1010
- const product = primaryProduct(cycle);
1011
- const grouping = cropGrouping(crop, product);
1012
- const countryData = cropYieldCountry(cropYield, cycle)[grouping];
1013
- const countryValue = extractGroupedDataClosesDate(countryData, +(cycle.endDate || ''));
1014
- const productValue = propertyValue$1(product === null || product === void 0 ? void 0 : product.value);
1015
- return {
1016
- cycle,
1017
- name: (_a = product === null || product === void 0 ? void 0 : product.term) === null || _a === void 0 ? void 0 : _a.name,
1018
- startDate: cycle.startDate,
1019
- endDate: cycle.endDate,
1020
- country: ((_c = (_b = cycle.site) === null || _b === void 0 ? void 0 : _b.country) === null || _c === void 0 ? void 0 : _c.name) || worldRegion.name,
1021
- observations: propertyValue$1(product === null || product === void 0 ? void 0 : product.observations) || 0,
1022
- yield: productValue,
1023
- faoYield: countryValue || undefined,
1024
- diff: delta(productValue, countryValue)
1025
- };
1026
- };
1027
742
  class HeAggregationEngineService {
1028
- constructor(http, commonService, nodeService) {
743
+ constructor(http, nodeService) {
1029
744
  this.http = http;
1030
- this.commonService = commonService;
1031
745
  this.nodeService = nodeService;
1032
746
  this.modelsLoading = false;
1033
747
  this.modelsLoaded = false;
@@ -1042,18 +756,12 @@ class HeAggregationEngineService {
1042
756
  yield this.loadModels().toPromise();
1043
757
  });
1044
758
  }
1045
- get gitUrl() {
1046
- return `${gitHome}/hestia-aggregation-engine/-/blob/${this.commonService.gitBranch}`;
1047
- }
1048
- get rawUrl() {
1049
- return `${gitRawBaseUrl}/hestia-aggregation-engine/-/raw/${this.commonService.gitBranch}`;
1050
- }
1051
759
  loadModels() {
1052
760
  this.modelsLoading = true;
1053
- return this.http.get(`${this.rawUrl}/model-links.json`).pipe(catchError(() => of({ links: [] })), map(({ links }) => {
761
+ return this.http.get(`${rawUrl$1()}/model-links.json`).pipe(catchError(() => of({ links: [] })), map(({ links }) => {
1054
762
  this._models.next(links.map((_a) => {
1055
763
  var { path: modelPath, docPath } = _a, link = __rest(_a, ["path", "docPath"]);
1056
- return (Object.assign(Object.assign({}, link), { path: `${this.gitUrl}/${modelPath}`, docPath: `${this.gitUrl}/${docPath}` }));
764
+ return (Object.assign(Object.assign({}, link), { path: `${gitUrl$1()}/${modelPath}`, docPath: `${gitUrl$1()}/${docPath}` }));
1057
765
  }));
1058
766
  this.modelsLoading = false;
1059
767
  this.modelsLoaded = true;
@@ -1096,16 +804,18 @@ class HeAggregationEngineService {
1096
804
  return this.lookups$.pipe(take(1)).toPromise();
1097
805
  }
1098
806
  }
1099
- HeAggregationEngineService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeAggregationEngineService, deps: [{ token: i1$1.HttpClient }, { token: HeCommonService }, { token: HeNodeService }], target: i0.ɵɵFactoryTarget.Injectable });
807
+ HeAggregationEngineService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeAggregationEngineService, deps: [{ token: i1$1.HttpClient }, { token: HeNodeService }], target: i0.ɵɵFactoryTarget.Injectable });
1100
808
  HeAggregationEngineService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeAggregationEngineService, providedIn: 'root' });
1101
809
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeAggregationEngineService, decorators: [{
1102
810
  type: Injectable,
1103
811
  args: [{
1104
812
  providedIn: 'root'
1105
813
  }]
1106
- }], ctorParameters: function () { return [{ type: i1$1.HttpClient }, { type: HeCommonService }, { type: HeNodeService }]; } });
814
+ }], ctorParameters: function () { return [{ type: i1$1.HttpClient }, { type: HeNodeService }]; } });
1107
815
 
1108
816
  const HE_ORCHESTRATOR_BASE_URL = new InjectionToken('HE_ORCHESTRATOR_BASE_URL');
817
+ const gitUrl = () => `${gitHome}/hestia-engine-models/-/blob/${gitBranch()}`;
818
+ const rawUrl = () => `${gitRawBaseUrl}/hestia-engine-models/-/raw/${gitBranch()}`;
1109
819
  const findConfigModels = ({ models }, modelId, modelKey) => models.flat().filter(({ value, key }) => modelId.startsWith(value) && (!modelKey || key === modelKey));
1110
820
  const pathToApiDocsPath = (model, term) => [
1111
821
  baseUrl(),
@@ -1117,9 +827,8 @@ const pathToApiDocsPath = (model, term) => [
1117
827
  ].filter(Boolean).join('-')
1118
828
  ].join('/');
1119
829
  class HeEngineService {
1120
- constructor(http, commonService) {
830
+ constructor(http) {
1121
831
  this.http = http;
1122
- this.commonService = commonService;
1123
832
  this.modelsLoading = false;
1124
833
  this.modelsLoaded = false;
1125
834
  this._models = new ReplaySubject(1);
@@ -1130,18 +839,12 @@ class HeEngineService {
1130
839
  yield this.loadModels().toPromise();
1131
840
  });
1132
841
  }
1133
- get gitUrl() {
1134
- return `${gitHome}/hestia-engine-models/-/blob/${this.commonService.gitBranch}`;
1135
- }
1136
- get rawUrl() {
1137
- return `${gitRawBaseUrl}/hestia-engine-models/-/raw/${this.commonService.gitBranch}`;
1138
- }
1139
842
  loadModels() {
1140
843
  this.modelsLoading = true;
1141
- return this.http.get(`${this.rawUrl}/model-links.json`).pipe(catchError(() => of({ links: [] })), map(({ links }) => {
844
+ return this.http.get(`${rawUrl()}/model-links.json`).pipe(catchError(() => of({ links: [] })), map(({ links }) => {
1142
845
  this._models.next(links.map((_a) => {
1143
846
  var { path, docPath } = _a, link = __rest(_a, ["path", "docPath"]);
1144
- return (Object.assign(Object.assign({}, link), { path: `${this.gitUrl}/${path}`, docPath: `${this.gitUrl}/${docPath}`, apiDocsPath: pathToApiDocsPath(link.model, link.term || link.modelKey) }));
847
+ return (Object.assign(Object.assign({}, link), { path: `${gitUrl()}/${path}`, docPath: `${gitUrl()}/${docPath}`, apiDocsPath: pathToApiDocsPath(link.model, link.term || link.modelKey) }));
1145
848
  }));
1146
849
  this.modelsLoading = false;
1147
850
  this.modelsLoaded = true;
@@ -1166,14 +869,14 @@ class HeEngineService {
1166
869
  }).toPromise();
1167
870
  }
1168
871
  }
1169
- HeEngineService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeEngineService, deps: [{ token: i1$1.HttpClient }, { token: HeCommonService }], target: i0.ɵɵFactoryTarget.Injectable });
872
+ HeEngineService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeEngineService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
1170
873
  HeEngineService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeEngineService, providedIn: 'root' });
1171
874
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeEngineService, decorators: [{
1172
875
  type: Injectable,
1173
876
  args: [{
1174
877
  providedIn: 'root'
1175
878
  }]
1176
- }], ctorParameters: function () { return [{ type: i1$1.HttpClient }, { type: HeCommonService }]; } });
879
+ }], ctorParameters: function () { return [{ type: i1$1.HttpClient }]; } });
1177
880
 
1178
881
  var NodeKeyState;
1179
882
  (function (NodeKeyState) {
@@ -1749,7 +1452,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
1749
1452
  }] } });
1750
1453
 
1751
1454
  let toastId = 0;
1752
- class ToastService {
1455
+ class HeToastService {
1753
1456
  constructor() {
1754
1457
  this.toasts = new ReplaySubject(1);
1755
1458
  }
@@ -1772,9 +1475,9 @@ class ToastService {
1772
1475
  });
1773
1476
  }
1774
1477
  }
1775
- ToastService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1776
- ToastService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ToastService, providedIn: 'root' });
1777
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ToastService, decorators: [{
1478
+ HeToastService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1479
+ HeToastService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeToastService, providedIn: 'root' });
1480
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeToastService, decorators: [{
1778
1481
  type: Injectable,
1779
1482
  args: [{
1780
1483
  providedIn: 'root'
@@ -1803,7 +1506,7 @@ class ToastComponent {
1803
1506
  this.toasts.splice(this.toasts.findIndex(val => val.id === toast.id), 1);
1804
1507
  }
1805
1508
  }
1806
- ToastComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ToastComponent, deps: [{ token: ToastService }], target: i0.ɵɵFactoryTarget.Component });
1509
+ ToastComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ToastComponent, deps: [{ token: HeToastService }], target: i0.ɵɵFactoryTarget.Component });
1807
1510
  ToastComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: ToastComponent, selector: "he-toast", ngImport: i0, template: "<div class=\"mb-3 columns is-centered is-vcentered\">\n <div class=\"notification is-{{toast.color}}\" role=\"alert\" *ngFor=\"let toast of toasts\">\n <button class=\"delete\" aria-label=\"delete\" (click)=\"dismiss(toast)\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n <strong [ngSwitch]=\"toast.color\">\n <ng-container *ngSwitchCase=\"'danger'\">\n <ng-container [ngSwitch]=\"toast.message\">\n <span *ngSwitchCase=\"'Unauthorized'\">You are not allowed to perform this action.</span>\n <span *ngSwitchCase=\"'form-invalid'\">Please fix all the errors on this page.</span>\n <span *ngSwitchCase=\"'users-email-already-taken'\">Email already taken.</span>\n <span *ngSwitchCase=\"'users-auth-already-taken'\">Account already connected.</span>\n <span *ngSwitchDefault>\n <span *ngIf=\"toast.showRawMessage\">{{toast.message}}</span>\n <span [class.is-hidden]=\"toast.showRawMessage\">An unknown error occurred. Please try again later.</span>\n </span>\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchDefault>\n <span *ngIf=\"toast.showRawMessage\">{{toast.message}}</span>\n <span [class.is-hidden]=\"toast.showRawMessage\">An unknown error occurred. Please try again later.</span>\n </ng-container>\n </strong>\n </div>\n</div>\n", styles: [":host{bottom:0;position:fixed;width:100%}\n"], directives: [{ type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i3.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { type: i3.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { type: i3.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
1808
1511
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ToastComponent, decorators: [{
1809
1512
  type: Component,
@@ -1812,7 +1515,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
1812
1515
  templateUrl: './toast.component.html',
1813
1516
  styleUrls: ['./toast.component.scss']
1814
1517
  }]
1815
- }], ctorParameters: function () { return [{ type: ToastService }]; } });
1518
+ }], ctorParameters: function () { return [{ type: HeToastService }]; } });
1816
1519
 
1817
1520
  class KeysPipe {
1818
1521
  transform(value, sort = false) {
@@ -2228,58 +1931,261 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
2228
1931
  }]
2229
1932
  }], ctorParameters: function () { return [{ type: i1$1.HttpClient }, { type: HeCommonService }]; } });
2230
1933
 
2231
- const emptySearchResult = () => ({
2232
- time: 0,
2233
- count: 0,
2234
- results: []
2235
- });
2236
- const MAX_RESULTS = 10000;
2237
- const suggestMatchQuery = (query) => [
2238
- matchPhraseQuery(query, 10),
2239
- matchQuery('nameNormalized', query, 8),
2240
- matchPhrasePrefixQuery(query, 2)
1934
+ const searchableTypes = [
1935
+ NodeType.Cycle,
1936
+ NodeType.Source
2241
1937
  ];
2242
- const suggestQuery = (query, type, extraQueries = []) => ({
2243
- bool: {
2244
- must: extraQueries,
2245
- should: (type ? [type] : searchableTypes).map(v => ({
2246
- bool: {
2247
- must: [matchType(v)],
2248
- should: suggestMatchQuery(query),
2249
- minimum_should_match: 1
1938
+ const serializeSearchFilters = (filters) => Object.keys(filters || {})
1939
+ .filter(key => filters[key].length)
1940
+ .map(key => `${key}=${filters[key].filter(Boolean).join('|')}`)
1941
+ .join(';');
1942
+ const deserializeSearchFilters = (filters) => (filters || '').split(';').reduce((prev, curr) => {
1943
+ const [key, values] = curr.split('=');
1944
+ prev[key] = (values || '').split('|').filter(Boolean);
1945
+ return prev;
1946
+ }, {});
1947
+ const matchType = (type) => Object.freeze({
1948
+ match: { '@type': type }
1949
+ });
1950
+ const matchTermType = (termType) => Object.freeze({
1951
+ match: { termType }
1952
+ });
1953
+ const matchRegex = (key, value) => Object.freeze({
1954
+ regexp: { [key]: { value: value.toString() } }
1955
+ });
1956
+ const matchQuery = (key, value, boost) => Object.freeze({
1957
+ match: {
1958
+ [key]: Object.assign({ query: value }, (boost ? { boost } : {}))
1959
+ }
1960
+ });
1961
+ const matchExactQuery = (key, value, boost) => matchQuery(`${key}.keyword`, value, boost);
1962
+ const matchNameNormalized = (query, boost = 20) => Object.freeze({
1963
+ match: { nameNormalized: { query, boost } }
1964
+ });
1965
+ const numberGte = (key, value) => Object.freeze({
1966
+ range: { [key]: { gte: value } }
1967
+ });
1968
+ const multiMatchQuery = (query, fields, type = 'best_fields', boost, analyzer) => Object.freeze({
1969
+ multi_match: Object.assign(Object.assign({ query,
1970
+ fields,
1971
+ type }, (boost ? { boost } : {})), (analyzer ? { analyzer } : {}))
1972
+ });
1973
+ const matchPhraseQuery = (query, boost, ...fields) => fields.length ? multiMatchQuery(query, fields, 'phrase', boost) : matchExactQuery('name', query, boost);
1974
+ const matchPhrasePrefixQuery = (query, boost = 2, ...fields) => fields.length ?
1975
+ multiMatchQuery(query, fields, 'phrase_prefix', boost) :
1976
+ Object.freeze({
1977
+ match_phrase_prefix: {
1978
+ nameSearchAsYouType: {
1979
+ query,
1980
+ boost
2250
1981
  }
2251
- })),
2252
- minimum_should_match: 1
1982
+ }
1983
+ });
1984
+ const matchBoolPrefixQuery = (query, boost = 1, ...fields) => fields.length ?
1985
+ multiMatchQuery(query, fields, 'bool_prefix', boost) :
1986
+ Object.freeze({
1987
+ match_bool_prefix: {
1988
+ name: {
1989
+ query,
1990
+ boost
1991
+ }
1992
+ }
1993
+ });
1994
+ const wildcardQuery = (query, boost = 3, ...fields) => Object.freeze({
1995
+ simple_query_string: {
1996
+ query: `*${query}*`,
1997
+ fields,
1998
+ analyze_wildcard: true,
1999
+ boost
2253
2000
  }
2254
2001
  });
2255
- const suggestSourceQuery = (query, fields) => ({
2002
+ const matchCountryLevel = { match: { gadmLevel: 0 } };
2003
+ const matchGlobalRegion = { regexp: { '@id': 'region-*' } };
2004
+ const matchCountry = Object.freeze({
2256
2005
  bool: {
2257
- must: [matchType(NodeType.Source)],
2258
2006
  should: [
2259
- matchPhraseQuery(query, 100, ...fields),
2260
- matchPhrasePrefixQuery(query, 20, ...fields),
2261
- matchBoolPrefixQuery(query, 10, ...fields)
2007
+ matchCountryLevel,
2008
+ matchGlobalRegion
2262
2009
  ],
2263
2010
  minimum_should_match: 1
2264
2011
  }
2265
2012
  });
2266
- class HeSearchService {
2267
- constructor(http, commonService) {
2268
- this.http = http;
2269
- this.commonService = commonService;
2270
- }
2271
- search(params) {
2272
- return this.http.post(`${this.commonService.apiBaseUrl}/search`, params)
2273
- .pipe(catchError(() => of(emptySearchResult())))
2274
- .toPromise();
2275
- }
2276
- count(params = {}) {
2277
- return this.http.post(`${this.commonService.apiBaseUrl}/count`, params).toPromise().catch(() => 0);
2013
+ const countriesQuery = Object.freeze({
2014
+ bool: {
2015
+ must: [
2016
+ matchType(NodeType.Term),
2017
+ matchTermType(TermTermType.region),
2018
+ matchCountryLevel
2019
+ ]
2278
2020
  }
2279
- get$(type, id) {
2280
- return this.http.post(`${this.commonService.apiBaseUrl}/search`, {
2281
- limit: 1,
2282
- query: {
2021
+ });
2022
+ const allCountriesQuery = Object.freeze({
2023
+ bool: Object.assign({ must: [
2024
+ matchType(NodeType.Term),
2025
+ matchTermType(TermTermType.region)
2026
+ ] }, matchCountry.bool)
2027
+ });
2028
+ const matchRegion = numberGte('gadmLevel', 1);
2029
+ const regionsQuery = Object.freeze({
2030
+ bool: {
2031
+ must: [
2032
+ matchType(NodeType.Term),
2033
+ matchTermType(TermTermType.region),
2034
+ matchRegion
2035
+ ]
2036
+ }
2037
+ });
2038
+ const worldRegion = Object.freeze({
2039
+ '@id': 'region-world',
2040
+ name: 'World'
2041
+ });
2042
+ const cropsQuery = Object.freeze({
2043
+ bool: {
2044
+ must: [
2045
+ matchType(NodeType.Term),
2046
+ matchTermType(TermTermType.crop)
2047
+ ]
2048
+ }
2049
+ });
2050
+ const matchAggregatedQuery = matchQuery('aggregated', true);
2051
+ const isNestedKey = (key) => nestedSearchableKeys.includes(key.split('.')[0]);
2052
+ const matchNestedKey = (key, query) => isNestedKey(key) ? {
2053
+ nested: { path: key.split('.')[0], query }
2054
+ } : query;
2055
+ /**
2056
+ * List of fields to return in the search results.
2057
+ */
2058
+ const searchResultsFields = Object.freeze({
2059
+ [NodeType.Cycle]: [
2060
+ 'description', 'dataDescription', 'endDate',
2061
+ 'emissionsCount', 'inputsCount', 'productsCount',
2062
+ 'site.location', 'site.country.name', 'site.region.name'
2063
+ ],
2064
+ [NodeType.Source]: ['bibliography.title', 'bibliography.documentDOI'],
2065
+ [NodeType.ImpactAssessment]: ['emissionsResourceUseCount', 'impactsCount', 'country.name']
2066
+ });
2067
+ /**
2068
+ * List of fields to search in.
2069
+ */
2070
+ const searchFields = Object.freeze({
2071
+ [NodeType.Cycle]: ['name', 'description', 'dataDescription'],
2072
+ [NodeType.Source]: ['name', 'bibliography.title'],
2073
+ [NodeType.ImpactAssessment]: ['name', 'product.name', 'country.name']
2074
+ });
2075
+ const searchFieldsNested = Object.freeze({
2076
+ [NodeType.Cycle]: [
2077
+ 'inputs.term.name',
2078
+ 'emissions.term.name',
2079
+ 'practices.term.name'
2080
+ ],
2081
+ [NodeType.Source]: [],
2082
+ [NodeType.ImpactAssessment]: [
2083
+ 'emissionsResourceUse.term.name',
2084
+ 'impacts.term.name'
2085
+ ]
2086
+ });
2087
+ /**
2088
+ * Specific strict queries per type.
2089
+ */
2090
+ const searchQueries = {
2091
+ [NodeType.Cycle]: { must: [], must_not: [matchAggregatedQuery] },
2092
+ [NodeType.Source]: { must: [], must_not: [matchAggregatedQuery] }
2093
+ };
2094
+ const searchFiltersKeys = {
2095
+ [NodeType.Cycle]: key => key,
2096
+ [NodeType.Source]: key => key
2097
+ };
2098
+ /* eslint-disable complexity */
2099
+ const searchQuery = (type, query, filters, aggregated) => {
2100
+ const boolQuery = (Object.assign(Object.assign({ must: [
2101
+ matchType(type),
2102
+ aggregated ? matchAggregatedQuery : null
2103
+ ].filter(Boolean) }, (query ? {
2104
+ should: [
2105
+ matchPhraseQuery(query, 100, ...searchFields[type]),
2106
+ matchPhrasePrefixQuery(query, 20, ...searchFields[type]),
2107
+ matchBoolPrefixQuery(query, 10, ...searchFields[type]),
2108
+ ...searchFieldsNested[type].map(field => matchNestedKey(field, matchBoolPrefixQuery(query, 1, field)))
2109
+ ],
2110
+ minimum_should_match: 1
2111
+ } : {})), { must_not: [
2112
+ aggregated ? null : matchAggregatedQuery
2113
+ ].filter(Boolean) }));
2114
+ const keys = Object.keys(filters || {}).filter(key => filters[key].length);
2115
+ return {
2116
+ bool: keys.length ? {
2117
+ must: keys.map(key => {
2118
+ const filterKey = searchFiltersKeys[type](key);
2119
+ return {
2120
+ bool: {
2121
+ should: filters[key]
2122
+ .map(value => matchNestedKey(filterKey, matchExactQuery(filterKey, value)))
2123
+ .map(must => {
2124
+ const localQuery = JSON.parse(JSON.stringify(boolQuery));
2125
+ localQuery.must.push(must);
2126
+ return { bool: localQuery };
2127
+ }),
2128
+ minimum_should_match: 1
2129
+ }
2130
+ };
2131
+ })
2132
+ } : boolQuery
2133
+ };
2134
+ };
2135
+ /* eslint-enable complexity */
2136
+
2137
+ const emptySearchResult = () => ({
2138
+ time: 0,
2139
+ count: 0,
2140
+ results: []
2141
+ });
2142
+ const MAX_RESULTS = 10000;
2143
+ const suggestMatchQuery = (query) => [
2144
+ matchPhraseQuery(query, 10),
2145
+ matchQuery('nameNormalized', query, 8),
2146
+ matchPhrasePrefixQuery(query, 2)
2147
+ ];
2148
+ const suggestQuery = (query, type, extraQueries = []) => ({
2149
+ bool: {
2150
+ must: extraQueries,
2151
+ should: (type ? [type] : searchableTypes).map(v => ({
2152
+ bool: {
2153
+ must: [matchType(v)],
2154
+ should: suggestMatchQuery(query),
2155
+ minimum_should_match: 1
2156
+ }
2157
+ })),
2158
+ minimum_should_match: 1
2159
+ }
2160
+ });
2161
+ const suggestSourceQuery = (query, fields) => ({
2162
+ bool: {
2163
+ must: [matchType(NodeType.Source)],
2164
+ should: [
2165
+ matchPhraseQuery(query, 100, ...fields),
2166
+ matchPhrasePrefixQuery(query, 20, ...fields),
2167
+ matchBoolPrefixQuery(query, 10, ...fields)
2168
+ ],
2169
+ minimum_should_match: 1
2170
+ }
2171
+ });
2172
+ class HeSearchService {
2173
+ constructor(http, commonService) {
2174
+ this.http = http;
2175
+ this.commonService = commonService;
2176
+ }
2177
+ search(params) {
2178
+ return this.http.post(`${this.commonService.apiBaseUrl}/search`, params)
2179
+ .pipe(catchError(() => of(emptySearchResult())))
2180
+ .toPromise();
2181
+ }
2182
+ count(params = {}) {
2183
+ return this.http.post(`${this.commonService.apiBaseUrl}/count`, params).toPromise().catch(() => 0);
2184
+ }
2185
+ get$(type, id) {
2186
+ return this.http.post(`${this.commonService.apiBaseUrl}/search`, {
2187
+ limit: 1,
2188
+ query: {
2283
2189
  bool: {
2284
2190
  must: [{ match: { '@type': type } }, { match: { '@id': id } }]
2285
2191
  }
@@ -2479,6 +2385,8 @@ const toCsv$1 = (lines) => json2csvAsync(lines.map(formatLine).filter(data => Ob
2479
2385
  emptyFieldValue: ''
2480
2386
  });
2481
2387
 
2388
+ const primaryProduct = ({ products }) => (products || []).find(({ primary }) => primary);
2389
+
2482
2390
  const linkTypeEnabled = (type) => [
2483
2391
  NodeType.Cycle,
2484
2392
  NodeType.ImpactAssessment,
@@ -2797,11 +2705,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
2797
2705
  type: Output
2798
2706
  }] } });
2799
2707
 
2800
- var DisplayType;
2801
- (function (DisplayType) {
2802
- DisplayType["diffs"] = "diffs";
2803
- DisplayType["sideBySide"] = "side-by-side";
2804
- })(DisplayType || (DisplayType = {}));
2708
+ var DiffsDisplayType;
2709
+ (function (DiffsDisplayType) {
2710
+ DiffsDisplayType["diffs"] = "diffs";
2711
+ DiffsDisplayType["sideBySide"] = "side-by-side";
2712
+ })(DiffsDisplayType || (DiffsDisplayType = {}));
2805
2713
 
2806
2714
  const omit = require('lodash.omit');
2807
2715
  const ignoreProperties = [
@@ -2817,9 +2725,9 @@ const customDiff = create({
2817
2725
  class NodeDiffsComponent {
2818
2726
  constructor(nodeService) {
2819
2727
  this.nodeService = nodeService;
2820
- this.displayType = DisplayType.diffs;
2728
+ this.displayType = DiffsDisplayType.diffs;
2821
2729
  this.diffsLoaded = new EventEmitter();
2822
- this.DisplayType = DisplayType;
2730
+ this.DiffsDisplayType = DiffsDisplayType;
2823
2731
  this.loading = true;
2824
2732
  }
2825
2733
  ngOnInit() {
@@ -2842,7 +2750,7 @@ class NodeDiffsComponent {
2842
2750
  }
2843
2751
  }
2844
2752
  NodeDiffsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: NodeDiffsComponent, deps: [{ token: HeNodeService }], target: i0.ɵɵFactoryTarget.Component });
2845
- NodeDiffsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: NodeDiffsComponent, selector: "he-node-diffs", inputs: { data: "data", id: "id", type: "type", displayType: "displayType" }, outputs: { diffsLoaded: "diffsLoaded" }, ngImport: i0, template: "<div *ngIf=\"loading\" class=\"has-text-center py-3\">\n <fa-icon icon=\"spinner\" [pulse]=\"true\" size=\"lg\"></fa-icon>\n</div>\n\n<ng-container *ngIf=\"left && right\">\n <ng-container [ngSwitch]=\"displayType\">\n <div *ngSwitchCase=\"DisplayType.diffs\" [innerHTML]=\"diffHtml\"></div>\n\n <div *ngSwitchCase=\"DisplayType.sideBySide\" class=\"columns\">\n <div class=\"column is-6\">\n <pre class=\"has-background-black has-text-white\"><code>{{left | json}}</code></pre>\n </div>\n <div class=\"column is-6\">\n <pre class=\"has-background-black has-text-white\"><code>{{right | json}}</code></pre>\n </div>\n </div>\n </ng-container>\n</ng-container>\n", styles: [""], components: [{ type: i1.FaIconComponent, selector: "fa-icon", inputs: ["classes", "icon", "title", "spin", "pulse", "mask", "styles", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { type: i3.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }], pipes: { "json": i3.JsonPipe } });
2753
+ NodeDiffsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: NodeDiffsComponent, selector: "he-node-diffs", inputs: { data: "data", id: "id", type: "type", displayType: "displayType" }, outputs: { diffsLoaded: "diffsLoaded" }, ngImport: i0, template: "<div *ngIf=\"loading\" class=\"has-text-center py-3\">\n <fa-icon icon=\"spinner\" [pulse]=\"true\" size=\"lg\"></fa-icon>\n</div>\n\n<ng-container *ngIf=\"left && right\">\n <ng-container [ngSwitch]=\"displayType\">\n <div *ngSwitchCase=\"DiffsDisplayType.diffs\" [innerHTML]=\"diffHtml\"></div>\n\n <div *ngSwitchCase=\"DiffsDisplayType.sideBySide\" class=\"columns\">\n <div class=\"column is-6\">\n <pre class=\"has-background-black has-text-white\"><code>{{left | json}}</code></pre>\n </div>\n <div class=\"column is-6\">\n <pre class=\"has-background-black has-text-white\"><code>{{right | json}}</code></pre>\n </div>\n </div>\n </ng-container>\n</ng-container>\n", styles: [""], components: [{ type: i1.FaIconComponent, selector: "fa-icon", inputs: ["classes", "icon", "title", "spin", "pulse", "mask", "styles", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { type: i3.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }], pipes: { "json": i3.JsonPipe } });
2846
2754
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: NodeDiffsComponent, decorators: [{
2847
2755
  type: Component,
2848
2756
  args: [{
@@ -3858,315 +3766,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
3858
3766
  }]
3859
3767
  }] });
3860
3768
 
3861
- const isSite = ({ '@type': type, '@id': id }) => type === NodeType.Site && !!id;
3862
- const siteLocation = ({ latitude, longitude }) => latitude && longitude ? ({
3863
- lat: latitude,
3864
- lng: longitude
3865
- }) : undefined;
3866
- const siteDefaultLocation = ({ region, country }) => {
3867
- const markers = [
3868
- region ? createMarker(termLocation(region), termLocationName(region).name, undefined, 20) : undefined,
3869
- country ? createMarker(termLocation(country), termLocationName(country).name, undefined, 40) : undefined
3870
- ].filter(Boolean);
3871
- return (markers.length ? markers[0] : undefined);
3872
- };
3873
- const siteMarker = (site) => createMarker(site ? siteLocation(site) : undefined, site.name);
3874
- const sitePolygon = ({ boundary }) => (boundary ? polygonsFromFeature(boundary) : undefined);
3875
- const regions = (sites) => unique(sites.map(({ country, region }) => region ? region['@id'] : (country ? country['@id'] : null)).filter(Boolean));
3876
- const defaultCenter = { lat: 0, lng: 0 };
3877
- class SitesMapsComponent {
3878
- constructor(nodeService) {
3879
- this.nodeService = nodeService;
3880
- this.loaded = false;
3881
- this.loadPolygons = true;
3882
- this.sites = [];
3883
- this.nodes = [];
3884
- this.center = defaultCenter;
3885
- this.zoom = 2;
3886
- this.showNotice = true;
3887
- this.googleLoaded = false;
3888
- this.showNoLocation = false;
3889
- }
3890
- ngOnInit() {
3891
- waitFor('google', () => {
3892
- this.googleLoaded = true;
3893
- setTimeout(() => !this.loaded && this.loadData());
3894
- });
3895
- }
3896
- ngAfterViewInit() {
3897
- return this.googleLoaded && this.loadData();
3898
- }
3899
- loadData() {
3900
- return __awaiter(this, void 0, void 0, function* () {
3901
- // loaded data as geojson
3902
- this.map.googleMap.data.setStyle(() => (Object.assign(Object.assign({}, strokeStyle), { strokeOpacity: 0.1 })));
3903
- const sites = yield this.getSites();
3904
- const markers = this.addSiteMarkers(sites);
3905
- // add the site.boundary if exist
3906
- const polygons = sites.flatMap(sitePolygon).filter(Boolean);
3907
- polygons.map(polygon => polygon === null || polygon === void 0 ? void 0 : polygon.setMap(this.map.googleMap));
3908
- this.loaded = true;
3909
- // add the default site polygons from GADM region if any
3910
- const termPolygons = (yield Promise.all(regions(sites).map(v => this.addTermsPolygons(v)))).flat();
3911
- this.showNoLocation = markers.length === 0 && polygons.length === 0 && termPolygons.length === 0;
3912
- return markers.length ?
3913
- this.centerMarker(markers[0]) :
3914
- this.centerPolygons(polygons.length ? polygons : termPolygons);
3915
- });
3916
- }
3917
- addMarkers(markers, cluster = false) {
3918
- return cluster ?
3919
- new MarkerClusterer(this.map.googleMap, markers, { imagePath: clustererImage }) :
3920
- markers.map(marker => marker.setMap(this.map.googleMap));
3921
- }
3922
- addTermsPolygons(id) {
3923
- return __awaiter(this, void 0, void 0, function* () {
3924
- return this.loadPolygons ? yield this.termPolygons(id) : [];
3925
- });
3926
- }
3927
- centerMarker(marker) {
3928
- const center = marker.getPosition();
3929
- this.center = center ? { lat: center.lat(), lng: center.lng() } : defaultCenter;
3930
- }
3931
- centerPolygons(polygons) {
3932
- try {
3933
- return polygons.length ? this.map.googleMap.fitBounds(polygonBounds(polygons)) : null;
3934
- }
3935
- catch (err) {
3936
- if (polygons.length) {
3937
- this.map.googleMap.fitBounds(polygonBounds(polygons[0]));
3938
- this.zoom = 3;
3939
- }
3940
- }
3941
- }
3942
- addSiteMarkers(sites) {
3943
- const siteMarkers = sites.map(siteMarker).filter(Boolean);
3944
- const markers = siteMarkers.length ? siteMarkers : sites.map(siteDefaultLocation).filter(Boolean);
3945
- this.addMarkers(markers, !!siteMarkers.length);
3946
- return markers;
3947
- }
3948
- loadSite(node) {
3949
- return __awaiter(this, void 0, void 0, function* () {
3950
- // means the site was already downloaded
3951
- return 'schemaVersion' in node ? node : yield this.nodeService.get(node);
3952
- });
3953
- }
3954
- siteData(node) {
3955
- return __awaiter(this, void 0, void 0, function* () {
3956
- return [
3957
- NodeType.Site,
3958
- NodeType.Organisation
3959
- ].includes(node.type) ? node : (isSite(node) ?
3960
- this.loadSite(node) : ('site' in node && isSite(node.site) ?
3961
- this.loadSite(node.site) :
3962
- this.siteData(yield this.loadSite(node))));
3963
- });
3964
- }
3965
- getSites() {
3966
- const nodes = this.sites && this.sites.length ? this.sites : this.nodes;
3967
- return Promise.all(nodes.map(node => this.siteData(node)));
3968
- }
3969
- termPolygons(id) {
3970
- return __awaiter(this, void 0, void 0, function* () {
3971
- try {
3972
- const data = yield this.nodeService.downloadRaw(`${baseUrl()}/gadm/${id}.geojson`);
3973
- const polygons = polygonsFromFeature(data);
3974
- polygons.map(polygon => polygon.setMap(this.map.googleMap));
3975
- return polygons;
3976
- }
3977
- catch (_err) {
3978
- // ignore error if the file does not exist
3979
- return [];
3980
- }
3981
- });
3982
- }
3983
- }
3984
- SitesMapsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMapsComponent, deps: [{ token: HeNodeService }], target: i0.ɵɵFactoryTarget.Component });
3985
- SitesMapsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: SitesMapsComponent, selector: "he-sites-maps", inputs: { loadPolygons: "loadPolygons", sites: "sites", nodes: "nodes", center: "center", zoom: "zoom", showNotice: "showNotice" }, viewQueries: [{ propertyName: "map", first: true, predicate: GoogleMap, descendants: true }], ngImport: i0, template: "<google-map *ngIf=\"googleLoaded\"\n height=\"100%\"\n width=\"100%\"\n [zoom]=\"zoom\"\n [center]=\"center\"\n></google-map>\n\n<p *ngIf=\"showNotice\" class=\"mt-2 is-italic is-size-7\">The information provided might not be complete</p>\n\n<div class=\"no-location has-text-center has-text-light\" *ngIf=\"showNoLocation\">\n <span>No precise location data</span>\n</div>\n", styles: [":host{display:block;height:100%;position:relative;width:100%}.no-location{background-color:#0000004d;left:0;height:100%;position:absolute;top:0;width:100%;z-index:9}.no-location>span{display:inline-block;margin-top:12%}\n"], components: [{ type: i1$2.GoogleMap, selector: "google-map", inputs: ["height", "width", "center", "zoom", "options", "mapTypeId"], outputs: ["authFailure", "boundsChanged", "centerChanged", "mapClick", "mapDblclick", "mapDrag", "mapDragend", "mapDragstart", "headingChanged", "idle", "maptypeidChanged", "mapMousemove", "mapMouseout", "mapMouseover", "projectionChanged", "mapRightclick", "tilesloaded", "tiltChanged", "zoomChanged"], exportAs: ["googleMap"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
3986
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMapsComponent, decorators: [{
3987
- type: Component,
3988
- args: [{
3989
- selector: 'he-sites-maps',
3990
- templateUrl: './sites-maps.component.html',
3991
- styleUrls: ['./sites-maps.component.scss']
3992
- }]
3993
- }], ctorParameters: function () { return [{ type: HeNodeService }]; }, propDecorators: { map: [{
3994
- type: ViewChild,
3995
- args: [GoogleMap]
3996
- }], loadPolygons: [{
3997
- type: Input
3998
- }], sites: [{
3999
- type: Input
4000
- }], nodes: [{
4001
- type: Input
4002
- }], center: [{
4003
- type: Input
4004
- }], zoom: [{
4005
- type: Input
4006
- }], showNotice: [{
4007
- type: Input
4008
- }] } });
4009
-
4010
- const maxAreaSize = 5000;
4011
- const siteTooBig = ({ area }) => area && area / 100 > maxAreaSize;
4012
- const hasMultipleValues = (values) => (values || []).length > 1;
4013
- const weighedAverage = ({ value, depthLower, depthUpper }) => value.reduce((prev, curr, index) => prev + (curr * (depthLower[index] - depthUpper[index])), 0) /
4014
- value.reduce((prev, _curr, index) => prev + (depthLower[index] - depthUpper[index]), 0);
4015
- const measurementValue = ({ value, depthLower, depthUpper }) => hasMultipleValues(value) && hasMultipleValues(depthLower) && hasMultipleValues(depthUpper) ?
4016
- weighedAverage({ value, depthLower, depthUpper }) :
4017
- propertyValue$1(value);
4018
-
4019
- class SitesMeasurementsLogsComponent {
4020
- constructor(searchService, nodeService) {
4021
- this.searchService = searchService;
4022
- this.nodeService = nodeService;
4023
- this.originalValues = [];
4024
- this.recalculatedValues = [];
4025
- this.loading = true;
4026
- this.NodeType = NodeType;
4027
- this.models = [];
4028
- this.measurements = [];
4029
- }
4030
- ngOnInit() {
4031
- return __awaiter(this, void 0, void 0, function* () {
4032
- this.logsUrl = this.nodeService.nodeLogsUrl(this.node);
4033
- this.logs = yield this.nodeService.getModelsLog(this.node);
4034
- const { results: measurements } = yield this.searchService.search({
4035
- fields: ['@type', '@id', 'name'],
4036
- limit: 1000,
4037
- query: {
4038
- bool: {
4039
- must: [
4040
- matchType(NodeType.Term),
4041
- matchTermType(TermTermType.measurement)
4042
- ]
4043
- }
4044
- }
4045
- });
4046
- this.measurements = measurements;
4047
- this.loading = false;
4048
- });
4049
- }
4050
- get node() {
4051
- return Object.assign(Object.assign({}, this.site), { dataState: DataState.recalculated });
4052
- }
4053
- }
4054
- SitesMeasurementsLogsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMeasurementsLogsComponent, deps: [{ token: HeSearchService }, { token: HeNodeService }], target: i0.ɵɵFactoryTarget.Component });
4055
- SitesMeasurementsLogsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: SitesMeasurementsLogsComponent, selector: "he-sites-measurements-logs", inputs: { site: "site", originalValues: "originalValues", recalculatedValues: "recalculatedValues" }, ngImport: i0, template: "<he-node-logs-models *ngIf=\"!loading; else loader\"\n [logsUrl]=\"logsUrl\"\n [nodeType]=\"NodeType.Site\"\n [originalValues]=\"originalValues\"\n [recalculatedValues]=\"recalculatedValues\"\n [terms]=\"measurements\"\n [logs]=\"logs\"\n filteredType=\"Measurement\"\n></he-node-logs-models>\n\n<ng-template #loader>\n <div class=\"has-text-center py-3\">\n <fa-icon icon=\"spinner\" [pulse]=\"true\" size=\"lg\"></fa-icon>\n </div>\n</ng-template>\n", styles: [""], components: [{ type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["nodeType", "nodeKey", "logsUrl", "originalValues", "recalculatedValues", "terms", "logs", "filteredType"] }, { type: i1.FaIconComponent, selector: "fa-icon", inputs: ["classes", "icon", "title", "spin", "pulse", "mask", "styles", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
4056
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMeasurementsLogsComponent, decorators: [{
4057
- type: Component,
4058
- args: [{
4059
- selector: 'he-sites-measurements-logs',
4060
- templateUrl: './sites-measurements-logs.component.html',
4061
- styleUrls: ['./sites-measurements-logs.component.scss']
4062
- }]
4063
- }], ctorParameters: function () { return [{ type: HeSearchService }, { type: HeNodeService }]; }, propDecorators: { site: [{
4064
- type: Input
4065
- }], originalValues: [{
4066
- type: Input
4067
- }], recalculatedValues: [{
4068
- type: Input
4069
- }] } });
4070
-
4071
- const orderBy$1 = require('lodash.orderby');
4072
- var View$1;
4073
- (function (View) {
4074
- View["table"] = "table";
4075
- View["logs"] = "logs";
4076
- })(View$1 || (View$1 = {}));
4077
- class SitesMeasurementsComponent {
4078
- constructor() {
4079
- this.originalValues = [];
4080
- this.sites = [];
4081
- this.showDownload = false;
4082
- this.View = View$1;
4083
- this.selectedView = View$1.table;
4084
- this.maxAreaSize = maxAreaSize;
4085
- this.siteTooBig = siteTooBig;
4086
- this.defaultLabel = defaultLabel;
4087
- this.measurementValue = measurementValue;
4088
- this.measurements = [];
4089
- }
4090
- ngOnChanges(changes) {
4091
- if ('sites' in changes) {
4092
- return this.update();
4093
- }
4094
- if ('dataState' in changes) {
4095
- this.selectedView = View$1.table;
4096
- }
4097
- }
4098
- trackById(_index, item) {
4099
- return item['@id'];
4100
- }
4101
- get isOriginal() {
4102
- return this.dataState === DataState.original;
4103
- }
4104
- update() {
4105
- const measurementsPerSite = groupNodesByTerm(this.sites, 'measurements', this.originalValues);
4106
- this.measurements = orderBy$1(grouppedKeys(measurementsPerSite), ['key'], ['asc']);
4107
- }
4108
- togglePopover(popover, context) {
4109
- return popover.isOpen() ? popover.close() : popover.open(context);
4110
- }
4111
- get showAreaTooBig() {
4112
- return this.dataState === DataState.recalculated && (this.sites || []).some(siteTooBig);
4113
- }
4114
- }
4115
- SitesMeasurementsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMeasurementsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4116
- SitesMeasurementsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: SitesMeasurementsComponent, selector: "he-sites-measurements", inputs: { originalValues: "originalValues", sites: "sites", dataState: "dataState" }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"measurements.length; else emptyTable\">\n <div class=\"columns is-variable is-2 m-0\">\n <div class=\"column is-hidden-mobile\"></div>\n <div class=\"column is-narrow\">\n <div class=\"field has-addons\">\n <div class=\"control\">\n <button class=\"button is-small\" [class.is-active]=\"selectedView === View.table\" (click)=\"selectedView = View.table\">\n <span class=\"icon is-small\">\n <fa-icon icon=\"list\" aria-hidden=\"true\"></fa-icon>\n </span>\n <span>Table view</span>\n </button>\n </div>\n <div class=\"control\" *ngIf=\"!isOriginal && sites.length === 1\">\n <button class=\"button is-small\" [class.is-active]=\"selectedView === View.logs\" (click)=\"selectedView = View.logs\">\n <span class=\"icon is-small\">\n <fa-icon icon=\"calculator\" aria-hidden=\"true\"></fa-icon>\n </span>\n <span>Recalculations logs</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"px-3 pb-3\" [class.is-hidden]=\"selectedView !== View.table\">\n <div class=\"has-text-right mb-2\">\n <button class=\"button is-dark is-outlined is-small\" (click)=\"showDownload = true\">\n <fa-icon icon=\"download\"></fa-icon>\n <span class=\"pl-2\">Download (CSV)</span>\n </button>\n </div>\n\n <div class=\"table-container data-table-container mb-1\">\n <table class=\"table is-narrow data-table has-children-{{measurements.length}}\">\n <thead>\n <tr>\n <th class=\"width-auto\"></th>\n <th *ngFor=\"let measurement of measurements\"\n [attr.title]=\"measurement.value.term.name\"\n >\n <he-node-link [node]=\"measurement.value.term\">\n <span>{{measurement.value.term.name | ellipsis:30}}</span>\n </he-node-link>\n </th>\n </tr>\n <tr>\n <th class=\"width-auto\"></th>\n <th *ngFor=\"let measurement of measurements\"\n [attr.title]=\"measurement.value.term.units\"\n >{{measurement.value.term.units}}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let site of sites; trackBy: trackById; let i = index\">\n <td class=\"width-auto\" [attr.title]=\"defaultLabel(site)\">\n <he-node-link [node]=\"site\">\n <span class=\"is-nowrap has-text-ellipsis\">{{i + 1}}. {{defaultLabel(site)}}</span>\n </he-node-link>\n </td>\n <td class=\"is-nowrap\" *ngFor=\"let measurement of measurements\">\n <span *ngIf=\"measurement.value.values[site['@id']]\"\n class=\"trigger-popover\"\n [ngbPopover]=\"details\" [autoClose]=\"'outside'\"\n triggers=\"manual\" #p=\"ngbPopover\" placement=\"left\" container=\"body\"\n (click)=\"togglePopover(p, { data: measurement.value.values[site['@id']], site: site, key: 'measurements' })\"\n >\n <span pointer>{{measurementValue(measurement.value.values[site['@id']]) | precision:3 | default:'-'}}</span>\n <he-blank-node-state class=\"ml-1\"\n [node]=\"measurement.value.values[site['@id']].nodes[0]\"\n key=\"value\"\n ></he-blank-node-state>\n </span>\n <span *ngIf=\"!measurement.value.values[site['@id']]\">\n <span>-</span>\n <sup class=\"pl-1\" *ngIf=\"siteTooBig(site)\">(1)</sup>\n </span>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <he-blank-node-state-notice [dataState]=\"dataState\"></he-blank-node-state-notice>\n\n <p class=\"is-size-7 is-italic\" *ngIf=\"showAreaTooBig\">\n (1) This region is >{{maxAreaSize}}km2 and is too large to reliably gap fill Measurements.\n </p>\n </div>\n\n <he-sites-measurements-logs *ngIf=\"selectedView === View.logs && !isOriginal\"\n [site]=\"sites[0]\"\n [originalValues]=\"originalValues[0].measurements\"\n [recalculatedValues]=\"sites[0].measurements\"\n ></he-sites-measurements-logs>\n</ng-container>\n\n<he-node-csv-export-confirm *ngIf=\"showDownload\"\n [nodes]=\"sites\" filename=\"site-measurements.csv\" [isUpload]=\"false\"\n [headerKeys]=\"['site.id', 'site.@id', 'site.measurements.']\"\n (closed)=\"showDownload = false\"\n></he-node-csv-export-confirm>\n\n<ng-template #emptyTable>\n <div class=\"panel-block\">\n <span>No data</span>\n </div>\n</ng-template>\n\n<ng-template #emptyValue>\n <span>-</span>\n</ng-template>\n\n<ng-template #details let-node=\"site\" let-data=\"data\" let-key=\"key\">\n <p><b>{{node.name}}</b></p>\n <he-node-value-details\n [data]=\"data\" [nodeType]=\"node['@type']\" [dataKey]=\"key\"\n ></he-node-value-details>\n</ng-template>\n", styles: ["fa-icon{display:inline-block;width:10px}\n"], components: [{ type: i1.FaIconComponent, selector: "fa-icon", inputs: ["classes", "icon", "title", "spin", "pulse", "mask", "styles", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"] }, { type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "showExternalLink"] }, { type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["nodeType", "dataKey", "key", "node", "state"] }, { type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { type: SitesMeasurementsLogsComponent, selector: "he-sites-measurements-logs", inputs: ["site", "originalValues", "recalculatedValues"] }, { type: NodeCsvExportConfirmComponent, selector: "he-node-csv-export-confirm", inputs: ["nodes", "filename", "headerKeys", "extension", "isUpload"], outputs: ["closed"] }, { type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataKey"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i10.NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "placement", "triggers", "container", "disablePopover", "popoverClass", "openDelay", "closeDelay", "ngbPopover", "popoverTitle"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }], pipes: { "ellipsis": EllipsisPipe, "default": DefaultPipe, "precision": PrecisionPipe } });
4117
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMeasurementsComponent, decorators: [{
4118
- type: Component,
4119
- args: [{
4120
- selector: 'he-sites-measurements',
4121
- templateUrl: './sites-measurements.component.html',
4122
- styleUrls: ['./sites-measurements.component.scss']
4123
- }]
4124
- }], propDecorators: { originalValues: [{
4125
- type: Input
4126
- }], sites: [{
4127
- type: Input
4128
- }], dataState: [{
4129
- type: Input
4130
- }] } });
4131
-
4132
- const components$2 = [
4133
- SitesMapsComponent,
4134
- SitesMeasurementsComponent,
4135
- SitesMeasurementsLogsComponent
4136
- ];
4137
- class HeSitesModule {
4138
- }
4139
- HeSitesModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeSitesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
4140
- HeSitesModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeSitesModule, declarations: [SitesMapsComponent,
4141
- SitesMeasurementsComponent,
4142
- SitesMeasurementsLogsComponent], imports: [CommonModule, ReactiveFormsModule,
4143
- HeCommonModule,
4144
- HeNodeModule], exports: [SitesMapsComponent,
4145
- SitesMeasurementsComponent,
4146
- SitesMeasurementsLogsComponent] });
4147
- HeSitesModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeSitesModule, imports: [[
4148
- CommonModule, ReactiveFormsModule,
4149
- HeCommonModule,
4150
- HeNodeModule
4151
- ]] });
4152
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeSitesModule, decorators: [{
4153
- type: NgModule,
4154
- args: [{
4155
- declarations: components$2,
4156
- exports: components$2,
4157
- imports: [
4158
- CommonModule, ReactiveFormsModule,
4159
- HeCommonModule,
4160
- HeNodeModule
4161
- ]
4162
- }]
4163
- }] });
4164
-
4165
- /* eslint-disable complexity */
4166
- const mapErrorMessage = 'does not contain latitude and longitude';
4167
- const parseDataPath = (dataPath) => {
4168
- const [_, ...paths] = dataPath.split('.');
4169
- return paths.map(path => ({ path, label: keyToLabel(path.replace(/\[\d+\]/g, '')) }));
3769
+ /* eslint-disable complexity */
3770
+ const mapErrorMessage = 'does not contain latitude and longitude';
3771
+ const parseDataPath = (dataPath) => {
3772
+ const [_, ...paths] = dataPath.split('.');
3773
+ return paths.map(path => ({ path, label: keyToLabel(path.replace(/\[\d+\]/g, '')) }));
4170
3774
  };
4171
3775
  const contactUsEmail = 'community@hestia.earth';
4172
3776
  const externalLink = (href, text) => `<a href="${href}" target="_blank">${text}</a>`;
@@ -4874,6 +4478,310 @@ const calculateCycleStartDate = (properties, property) => {
4874
4478
  return moment(endDate.value).locale('en-gb').subtract(cycleDuration.value, 'days').format('YYYY-MM-DD');
4875
4479
  };
4876
4480
 
4481
+ const isSite = ({ '@type': type, '@id': id }) => type === NodeType.Site && !!id;
4482
+ const siteLocation = ({ latitude, longitude }) => latitude && longitude ? ({
4483
+ lat: latitude,
4484
+ lng: longitude
4485
+ }) : undefined;
4486
+ const siteDefaultLocation = ({ region, country }) => {
4487
+ const markers = [
4488
+ region ? createMarker(termLocation(region), termLocationName(region).name, undefined, 20) : undefined,
4489
+ country ? createMarker(termLocation(country), termLocationName(country).name, undefined, 40) : undefined
4490
+ ].filter(Boolean);
4491
+ return (markers.length ? markers[0] : undefined);
4492
+ };
4493
+ const siteMarker = (site) => createMarker(site ? siteLocation(site) : undefined, site.name);
4494
+ const sitePolygon = ({ boundary }) => (boundary ? polygonsFromFeature(boundary) : undefined);
4495
+ const regions = (sites) => unique(sites.map(({ country, region }) => region ? region['@id'] : (country ? country['@id'] : null)).filter(Boolean));
4496
+ const defaultCenter = { lat: 0, lng: 0 };
4497
+ class SitesMapsComponent {
4498
+ constructor(nodeService) {
4499
+ this.nodeService = nodeService;
4500
+ this.loaded = false;
4501
+ this.loadPolygons = true;
4502
+ this.sites = [];
4503
+ this.nodes = [];
4504
+ this.center = defaultCenter;
4505
+ this.zoom = 2;
4506
+ this.showNotice = true;
4507
+ this.googleLoaded = false;
4508
+ this.showNoLocation = false;
4509
+ }
4510
+ ngOnInit() {
4511
+ waitFor('google', () => {
4512
+ this.googleLoaded = true;
4513
+ setTimeout(() => !this.loaded && this.loadData());
4514
+ });
4515
+ }
4516
+ ngAfterViewInit() {
4517
+ return this.googleLoaded && this.loadData();
4518
+ }
4519
+ loadData() {
4520
+ return __awaiter(this, void 0, void 0, function* () {
4521
+ // loaded data as geojson
4522
+ this.map.googleMap.data.setStyle(() => (Object.assign(Object.assign({}, strokeStyle), { strokeOpacity: 0.1 })));
4523
+ const sites = yield this.getSites();
4524
+ const markers = this.addSiteMarkers(sites);
4525
+ // add the site.boundary if exist
4526
+ const polygons = sites.flatMap(sitePolygon).filter(Boolean);
4527
+ polygons.map(polygon => polygon === null || polygon === void 0 ? void 0 : polygon.setMap(this.map.googleMap));
4528
+ this.loaded = true;
4529
+ // add the default site polygons from GADM region if any
4530
+ const termPolygons = (yield Promise.all(regions(sites).map(v => this.addTermsPolygons(v)))).flat();
4531
+ this.showNoLocation = markers.length === 0 && polygons.length === 0 && termPolygons.length === 0;
4532
+ return markers.length ?
4533
+ this.centerMarker(markers[0]) :
4534
+ this.centerPolygons(polygons.length ? polygons : termPolygons);
4535
+ });
4536
+ }
4537
+ addMarkers(markers, cluster = false) {
4538
+ return cluster ?
4539
+ new MarkerClusterer(this.map.googleMap, markers, { imagePath: clustererImage }) :
4540
+ markers.map(marker => marker.setMap(this.map.googleMap));
4541
+ }
4542
+ addTermsPolygons(id) {
4543
+ return __awaiter(this, void 0, void 0, function* () {
4544
+ return this.loadPolygons ? yield this.termPolygons(id) : [];
4545
+ });
4546
+ }
4547
+ centerMarker(marker) {
4548
+ const center = marker.getPosition();
4549
+ this.center = center ? { lat: center.lat(), lng: center.lng() } : defaultCenter;
4550
+ }
4551
+ centerPolygons(polygons) {
4552
+ try {
4553
+ return polygons.length ? this.map.googleMap.fitBounds(polygonBounds(polygons)) : null;
4554
+ }
4555
+ catch (err) {
4556
+ if (polygons.length) {
4557
+ this.map.googleMap.fitBounds(polygonBounds(polygons[0]));
4558
+ this.zoom = 3;
4559
+ }
4560
+ }
4561
+ }
4562
+ addSiteMarkers(sites) {
4563
+ const siteMarkers = sites.map(siteMarker).filter(Boolean);
4564
+ const markers = siteMarkers.length ? siteMarkers : sites.map(siteDefaultLocation).filter(Boolean);
4565
+ this.addMarkers(markers, !!siteMarkers.length);
4566
+ return markers;
4567
+ }
4568
+ loadSite(node) {
4569
+ return __awaiter(this, void 0, void 0, function* () {
4570
+ // means the site was already downloaded
4571
+ return 'schemaVersion' in node ? node : yield this.nodeService.get(node);
4572
+ });
4573
+ }
4574
+ siteData(node) {
4575
+ return __awaiter(this, void 0, void 0, function* () {
4576
+ return [
4577
+ NodeType.Site,
4578
+ NodeType.Organisation
4579
+ ].includes(node.type) ? node : (isSite(node) ?
4580
+ this.loadSite(node) : ('site' in node && isSite(node.site) ?
4581
+ this.loadSite(node.site) :
4582
+ this.siteData(yield this.loadSite(node))));
4583
+ });
4584
+ }
4585
+ getSites() {
4586
+ const nodes = this.sites && this.sites.length ? this.sites : this.nodes;
4587
+ return Promise.all(nodes.map(node => this.siteData(node)));
4588
+ }
4589
+ termPolygons(id) {
4590
+ return __awaiter(this, void 0, void 0, function* () {
4591
+ try {
4592
+ const data = yield this.nodeService.downloadRaw(`${baseUrl()}/gadm/${id}.geojson`);
4593
+ const polygons = polygonsFromFeature(data);
4594
+ polygons.map(polygon => polygon.setMap(this.map.googleMap));
4595
+ return polygons;
4596
+ }
4597
+ catch (_err) {
4598
+ // ignore error if the file does not exist
4599
+ return [];
4600
+ }
4601
+ });
4602
+ }
4603
+ }
4604
+ SitesMapsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMapsComponent, deps: [{ token: HeNodeService }], target: i0.ɵɵFactoryTarget.Component });
4605
+ SitesMapsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: SitesMapsComponent, selector: "he-sites-maps", inputs: { loadPolygons: "loadPolygons", sites: "sites", nodes: "nodes", center: "center", zoom: "zoom", showNotice: "showNotice" }, viewQueries: [{ propertyName: "map", first: true, predicate: GoogleMap, descendants: true }], ngImport: i0, template: "<google-map *ngIf=\"googleLoaded\"\n height=\"100%\"\n width=\"100%\"\n [zoom]=\"zoom\"\n [center]=\"center\"\n></google-map>\n\n<p *ngIf=\"showNotice\" class=\"mt-2 is-italic is-size-7\">The information provided might not be complete</p>\n\n<div class=\"no-location has-text-center has-text-light\" *ngIf=\"showNoLocation\">\n <span>No precise location data</span>\n</div>\n", styles: [":host{display:block;height:100%;position:relative;width:100%}.no-location{background-color:#0000004d;left:0;height:100%;position:absolute;top:0;width:100%;z-index:9}.no-location>span{display:inline-block;margin-top:12%}\n"], components: [{ type: i1$2.GoogleMap, selector: "google-map", inputs: ["height", "width", "center", "zoom", "options", "mapTypeId"], outputs: ["authFailure", "boundsChanged", "centerChanged", "mapClick", "mapDblclick", "mapDrag", "mapDragend", "mapDragstart", "headingChanged", "idle", "maptypeidChanged", "mapMousemove", "mapMouseout", "mapMouseover", "projectionChanged", "mapRightclick", "tilesloaded", "tiltChanged", "zoomChanged"], exportAs: ["googleMap"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
4606
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMapsComponent, decorators: [{
4607
+ type: Component,
4608
+ args: [{
4609
+ selector: 'he-sites-maps',
4610
+ templateUrl: './sites-maps.component.html',
4611
+ styleUrls: ['./sites-maps.component.scss']
4612
+ }]
4613
+ }], ctorParameters: function () { return [{ type: HeNodeService }]; }, propDecorators: { map: [{
4614
+ type: ViewChild,
4615
+ args: [GoogleMap]
4616
+ }], loadPolygons: [{
4617
+ type: Input
4618
+ }], sites: [{
4619
+ type: Input
4620
+ }], nodes: [{
4621
+ type: Input
4622
+ }], center: [{
4623
+ type: Input
4624
+ }], zoom: [{
4625
+ type: Input
4626
+ }], showNotice: [{
4627
+ type: Input
4628
+ }] } });
4629
+
4630
+ const maxAreaSize = 5000;
4631
+ const siteTooBig = ({ area }) => area && area / 100 > maxAreaSize;
4632
+ const hasMultipleValues = (values) => (values || []).length > 1;
4633
+ const weighedAverage = ({ value, depthLower, depthUpper }) => value.reduce((prev, curr, index) => prev + (curr * (depthLower[index] - depthUpper[index])), 0) /
4634
+ value.reduce((prev, _curr, index) => prev + (depthLower[index] - depthUpper[index]), 0);
4635
+ const measurementValue = ({ value, depthLower, depthUpper }) => hasMultipleValues(value) && hasMultipleValues(depthLower) && hasMultipleValues(depthUpper) ?
4636
+ weighedAverage({ value, depthLower, depthUpper }) :
4637
+ propertyValue$1(value);
4638
+
4639
+ class SitesMeasurementsLogsComponent {
4640
+ constructor(searchService, nodeService) {
4641
+ this.searchService = searchService;
4642
+ this.nodeService = nodeService;
4643
+ this.originalValues = [];
4644
+ this.recalculatedValues = [];
4645
+ this.loading = true;
4646
+ this.NodeType = NodeType;
4647
+ this.models = [];
4648
+ this.measurements = [];
4649
+ }
4650
+ ngOnInit() {
4651
+ return __awaiter(this, void 0, void 0, function* () {
4652
+ this.logsUrl = this.nodeService.nodeLogsUrl(this.node);
4653
+ this.logs = yield this.nodeService.getModelsLog(this.node);
4654
+ const { results: measurements } = yield this.searchService.search({
4655
+ fields: ['@type', '@id', 'name'],
4656
+ limit: 1000,
4657
+ query: {
4658
+ bool: {
4659
+ must: [
4660
+ matchType(NodeType.Term),
4661
+ matchTermType(TermTermType.measurement)
4662
+ ]
4663
+ }
4664
+ }
4665
+ });
4666
+ this.measurements = measurements;
4667
+ this.loading = false;
4668
+ });
4669
+ }
4670
+ get node() {
4671
+ return Object.assign(Object.assign({}, this.site), { dataState: DataState.recalculated });
4672
+ }
4673
+ }
4674
+ SitesMeasurementsLogsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMeasurementsLogsComponent, deps: [{ token: HeSearchService }, { token: HeNodeService }], target: i0.ɵɵFactoryTarget.Component });
4675
+ SitesMeasurementsLogsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: SitesMeasurementsLogsComponent, selector: "he-sites-measurements-logs", inputs: { site: "site", originalValues: "originalValues", recalculatedValues: "recalculatedValues" }, ngImport: i0, template: "<he-node-logs-models *ngIf=\"!loading; else loader\"\n [logsUrl]=\"logsUrl\"\n [nodeType]=\"NodeType.Site\"\n [originalValues]=\"originalValues\"\n [recalculatedValues]=\"recalculatedValues\"\n [terms]=\"measurements\"\n [logs]=\"logs\"\n filteredType=\"Measurement\"\n></he-node-logs-models>\n\n<ng-template #loader>\n <div class=\"has-text-center py-3\">\n <fa-icon icon=\"spinner\" [pulse]=\"true\" size=\"lg\"></fa-icon>\n </div>\n</ng-template>\n", styles: [""], components: [{ type: NodeLogsModelsComponent, selector: "he-node-logs-models", inputs: ["nodeType", "nodeKey", "logsUrl", "originalValues", "recalculatedValues", "terms", "logs", "filteredType"] }, { type: i1.FaIconComponent, selector: "fa-icon", inputs: ["classes", "icon", "title", "spin", "pulse", "mask", "styles", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
4676
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMeasurementsLogsComponent, decorators: [{
4677
+ type: Component,
4678
+ args: [{
4679
+ selector: 'he-sites-measurements-logs',
4680
+ templateUrl: './sites-measurements-logs.component.html',
4681
+ styleUrls: ['./sites-measurements-logs.component.scss']
4682
+ }]
4683
+ }], ctorParameters: function () { return [{ type: HeSearchService }, { type: HeNodeService }]; }, propDecorators: { site: [{
4684
+ type: Input
4685
+ }], originalValues: [{
4686
+ type: Input
4687
+ }], recalculatedValues: [{
4688
+ type: Input
4689
+ }] } });
4690
+
4691
+ const orderBy$1 = require('lodash.orderby');
4692
+ var View$1;
4693
+ (function (View) {
4694
+ View["table"] = "table";
4695
+ View["logs"] = "logs";
4696
+ })(View$1 || (View$1 = {}));
4697
+ class SitesMeasurementsComponent {
4698
+ constructor() {
4699
+ this.originalValues = [];
4700
+ this.sites = [];
4701
+ this.showDownload = false;
4702
+ this.View = View$1;
4703
+ this.selectedView = View$1.table;
4704
+ this.maxAreaSize = maxAreaSize;
4705
+ this.siteTooBig = siteTooBig;
4706
+ this.defaultLabel = defaultLabel;
4707
+ this.measurementValue = measurementValue;
4708
+ this.measurements = [];
4709
+ }
4710
+ ngOnChanges(changes) {
4711
+ if ('sites' in changes) {
4712
+ return this.update();
4713
+ }
4714
+ if ('dataState' in changes) {
4715
+ this.selectedView = View$1.table;
4716
+ }
4717
+ }
4718
+ trackById(_index, item) {
4719
+ return item['@id'];
4720
+ }
4721
+ get isOriginal() {
4722
+ return this.dataState === DataState.original;
4723
+ }
4724
+ update() {
4725
+ const measurementsPerSite = groupNodesByTerm(this.sites, 'measurements', this.originalValues);
4726
+ this.measurements = orderBy$1(grouppedKeys(measurementsPerSite), ['key'], ['asc']);
4727
+ }
4728
+ togglePopover(popover, context) {
4729
+ return popover.isOpen() ? popover.close() : popover.open(context);
4730
+ }
4731
+ get showAreaTooBig() {
4732
+ return this.dataState === DataState.recalculated && (this.sites || []).some(siteTooBig);
4733
+ }
4734
+ }
4735
+ SitesMeasurementsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMeasurementsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4736
+ SitesMeasurementsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: SitesMeasurementsComponent, selector: "he-sites-measurements", inputs: { originalValues: "originalValues", sites: "sites", dataState: "dataState" }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"measurements.length; else emptyTable\">\n <div class=\"columns is-variable is-2 m-0\">\n <div class=\"column is-hidden-mobile\"></div>\n <div class=\"column is-narrow\">\n <div class=\"field has-addons\">\n <div class=\"control\">\n <button class=\"button is-small\" [class.is-active]=\"selectedView === View.table\" (click)=\"selectedView = View.table\">\n <span class=\"icon is-small\">\n <fa-icon icon=\"list\" aria-hidden=\"true\"></fa-icon>\n </span>\n <span>Table view</span>\n </button>\n </div>\n <div class=\"control\" *ngIf=\"!isOriginal && sites.length === 1\">\n <button class=\"button is-small\" [class.is-active]=\"selectedView === View.logs\" (click)=\"selectedView = View.logs\">\n <span class=\"icon is-small\">\n <fa-icon icon=\"calculator\" aria-hidden=\"true\"></fa-icon>\n </span>\n <span>Recalculations logs</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"px-3 pb-3\" [class.is-hidden]=\"selectedView !== View.table\">\n <div class=\"has-text-right mb-2\">\n <button class=\"button is-dark is-outlined is-small\" (click)=\"showDownload = true\">\n <fa-icon icon=\"download\"></fa-icon>\n <span class=\"pl-2\">Download (CSV)</span>\n </button>\n </div>\n\n <div class=\"table-container data-table-container mb-1\">\n <table class=\"table is-narrow data-table has-children-{{measurements.length}}\">\n <thead>\n <tr>\n <th class=\"width-auto\"></th>\n <th *ngFor=\"let measurement of measurements\"\n [attr.title]=\"measurement.value.term.name\"\n >\n <he-node-link [node]=\"measurement.value.term\">\n <span>{{measurement.value.term.name | ellipsis:30}}</span>\n </he-node-link>\n </th>\n </tr>\n <tr>\n <th class=\"width-auto\"></th>\n <th *ngFor=\"let measurement of measurements\"\n [attr.title]=\"measurement.value.term.units\"\n >{{measurement.value.term.units}}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let site of sites; trackBy: trackById; let i = index\">\n <td class=\"width-auto\" [attr.title]=\"defaultLabel(site)\">\n <he-node-link [node]=\"site\">\n <span class=\"is-nowrap has-text-ellipsis\">{{i + 1}}. {{defaultLabel(site)}}</span>\n </he-node-link>\n </td>\n <td class=\"is-nowrap\" *ngFor=\"let measurement of measurements\">\n <span *ngIf=\"measurement.value.values[site['@id']]\"\n class=\"trigger-popover\"\n [ngbPopover]=\"details\" [autoClose]=\"'outside'\"\n triggers=\"manual\" #p=\"ngbPopover\" placement=\"left\" container=\"body\"\n (click)=\"togglePopover(p, { data: measurement.value.values[site['@id']], site: site, key: 'measurements' })\"\n >\n <span pointer>{{measurementValue(measurement.value.values[site['@id']]) | precision:3 | default:'-'}}</span>\n <he-blank-node-state class=\"ml-1\"\n [node]=\"measurement.value.values[site['@id']].nodes[0]\"\n key=\"value\"\n ></he-blank-node-state>\n </span>\n <span *ngIf=\"!measurement.value.values[site['@id']]\">\n <span>-</span>\n <sup class=\"pl-1\" *ngIf=\"siteTooBig(site)\">(1)</sup>\n </span>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <he-blank-node-state-notice [dataState]=\"dataState\"></he-blank-node-state-notice>\n\n <p class=\"is-size-7 is-italic\" *ngIf=\"showAreaTooBig\">\n (1) This region is >{{maxAreaSize}}km2 and is too large to reliably gap fill Measurements.\n </p>\n </div>\n\n <he-sites-measurements-logs *ngIf=\"selectedView === View.logs && !isOriginal\"\n [site]=\"sites[0]\"\n [originalValues]=\"originalValues[0].measurements\"\n [recalculatedValues]=\"sites[0].measurements\"\n ></he-sites-measurements-logs>\n</ng-container>\n\n<he-node-csv-export-confirm *ngIf=\"showDownload\"\n [nodes]=\"sites\" filename=\"site-measurements.csv\" [isUpload]=\"false\"\n [headerKeys]=\"['site.id', 'site.@id', 'site.measurements.']\"\n (closed)=\"showDownload = false\"\n></he-node-csv-export-confirm>\n\n<ng-template #emptyTable>\n <div class=\"panel-block\">\n <span>No data</span>\n </div>\n</ng-template>\n\n<ng-template #emptyValue>\n <span>-</span>\n</ng-template>\n\n<ng-template #details let-node=\"site\" let-data=\"data\" let-key=\"key\">\n <p><b>{{node.name}}</b></p>\n <he-node-value-details\n [data]=\"data\" [nodeType]=\"node['@type']\" [dataKey]=\"key\"\n ></he-node-value-details>\n</ng-template>\n", styles: ["fa-icon{display:inline-block;width:10px}\n"], components: [{ type: i1.FaIconComponent, selector: "fa-icon", inputs: ["classes", "icon", "title", "spin", "pulse", "mask", "styles", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"] }, { type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "showExternalLink"] }, { type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["nodeType", "dataKey", "key", "node", "state"] }, { type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { type: SitesMeasurementsLogsComponent, selector: "he-sites-measurements-logs", inputs: ["site", "originalValues", "recalculatedValues"] }, { type: NodeCsvExportConfirmComponent, selector: "he-node-csv-export-confirm", inputs: ["nodes", "filename", "headerKeys", "extension", "isUpload"], outputs: ["closed"] }, { type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataKey"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i10.NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "placement", "triggers", "container", "disablePopover", "popoverClass", "openDelay", "closeDelay", "ngbPopover", "popoverTitle"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }], pipes: { "ellipsis": EllipsisPipe, "default": DefaultPipe, "precision": PrecisionPipe } });
4737
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SitesMeasurementsComponent, decorators: [{
4738
+ type: Component,
4739
+ args: [{
4740
+ selector: 'he-sites-measurements',
4741
+ templateUrl: './sites-measurements.component.html',
4742
+ styleUrls: ['./sites-measurements.component.scss']
4743
+ }]
4744
+ }], propDecorators: { originalValues: [{
4745
+ type: Input
4746
+ }], sites: [{
4747
+ type: Input
4748
+ }], dataState: [{
4749
+ type: Input
4750
+ }] } });
4751
+
4752
+ const components$2 = [
4753
+ SitesMapsComponent,
4754
+ SitesMeasurementsComponent,
4755
+ SitesMeasurementsLogsComponent
4756
+ ];
4757
+ class HeSitesModule {
4758
+ }
4759
+ HeSitesModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeSitesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
4760
+ HeSitesModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeSitesModule, declarations: [SitesMapsComponent,
4761
+ SitesMeasurementsComponent,
4762
+ SitesMeasurementsLogsComponent], imports: [CommonModule, ReactiveFormsModule,
4763
+ HeCommonModule,
4764
+ HeNodeModule], exports: [SitesMapsComponent,
4765
+ SitesMeasurementsComponent,
4766
+ SitesMeasurementsLogsComponent] });
4767
+ HeSitesModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeSitesModule, imports: [[
4768
+ CommonModule, ReactiveFormsModule,
4769
+ HeCommonModule,
4770
+ HeNodeModule
4771
+ ]] });
4772
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: HeSitesModule, decorators: [{
4773
+ type: NgModule,
4774
+ args: [{
4775
+ declarations: components$2,
4776
+ exports: components$2,
4777
+ imports: [
4778
+ CommonModule, ReactiveFormsModule,
4779
+ HeCommonModule,
4780
+ HeNodeModule
4781
+ ]
4782
+ }]
4783
+ }] });
4784
+
4877
4785
  const user = {};
4878
4786
  class HeUsersService {
4879
4787
  get loggedInUser() {
@@ -5863,7 +5771,7 @@ class ImpactAssessmentsProductsComponent {
5863
5771
  });
5864
5772
  }
5865
5773
  }
5866
- ImpactAssessmentsProductsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ImpactAssessmentsProductsComponent, deps: [{ token: i1$4.FormBuilder }, { token: HeNodeService }, { token: HeSearchService }, { token: ToastService }], target: i0.ɵɵFactoryTarget.Component });
5774
+ ImpactAssessmentsProductsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ImpactAssessmentsProductsComponent, deps: [{ token: i1$4.FormBuilder }, { token: HeNodeService }, { token: HeSearchService }, { token: HeToastService }], target: i0.ɵɵFactoryTarget.Component });
5867
5775
  ImpactAssessmentsProductsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: ImpactAssessmentsProductsComponent, selector: "he-impact-assessments-products", inputs: { cycles: "cycles", impactAssessments: "impactAssessments", key: "key", dataState: "dataState", enableCompare: "enableCompare", filterTermTypes: "filterTermTypes", enableFilterMethodModel: "enableFilterMethodModel" }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"indicators.length; else emptyTable\">\n <div class=\"columns is-variable is-2 m-0\">\n <div class=\"column is-hidden-mobile\"></div>\n <div class=\"column is-narrow\">\n <div class=\"field has-addons\">\n <div class=\"control\">\n <button class=\"button is-small\" [class.is-active]=\"selectedView === View.table\" (click)=\"selectedView = View.table\">\n <span class=\"icon is-small\">\n <fa-icon icon=\"list\" aria-hidden=\"true\"></fa-icon>\n </span>\n <span>Table view</span>\n </button>\n </div>\n <div class=\"control\" *ngIf=\"impactAssessments.length > 1\">\n <button class=\"button is-small\" [class.is-active]=\"selectedView === View.chart\" (click)=\"selectedView = View.chart\">\n <span class=\"icon is-small\">\n <fa-icon icon=\"chart-bar\" aria-hidden=\"true\"></fa-icon>\n </span>\n <span>Chart view</span>\n </button>\n </div>\n <div class=\"control\" *ngIf=\"enableBreakdown\">\n <button class=\"button is-small\" [class.is-active]=\"selectedView === View.breakdown\" (click)=\"selectedView = View.breakdown\">\n <span class=\"icon is-small\">\n <fa-icon icon=\"chart-bar\" aria-hidden=\"true\"></fa-icon>\n </span>\n <span>Breakdown view</span>\n </button>\n </div>\n <div class=\"control\" *ngIf=\"!isOriginal && impactAssessments.length === 1\">\n <button class=\"button is-small\" [class.is-active]=\"selectedView === View.logs\" (click)=\"showRecalculationLogs()\">\n <span class=\"icon is-small\">\n <fa-icon icon=\"calculator\" aria-hidden=\"true\"></fa-icon>\n </span>\n <span>Recalculations logs</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"px-3 pb-3\" [class.is-hidden]=\"selectedView !== View.table\">\n <div class=\"has-text-right mb-2\">\n <button class=\"button is-dark is-outlined is-small\" (click)=\"showDownload = true\">\n <fa-icon icon=\"download\"></fa-icon>\n <span class=\"pl-2\">Download (CSV)</span>\n </button>\n </div>\n\n <div class=\"table-container data-table-container mb-1\" *bindOnce=\"indicators\">\n <table class=\"table is-narrow data-table has-children-{{indicators.length + 1}}\">\n <thead>\n <tr>\n <th class=\"width-auto\">\n <div class=\"select is-small\" *ngIf=\"enableFilterMethodModel\">\n <select name=\"selectedMethodModel\"\n (change)=\"updateImpacts()\" [(ngModel)]=\"selectedMethodModel\"\n >\n <option [ngValue]=\"undefined\">Filter Model</option>\n <option *ngFor=\"let term of methodModels\" [ngValue]=\"term\">{{term.name}}</option>\n </select>\n </div>\n </th>\n <th></th>\n <th *ngFor=\"let indicator of indicators\"\n [attr.title]=\"indicator.value.term.name\"\n >\n <he-node-link [node]=\"indicator.value.term\">\n <span>{{indicator.value.term.name | ellipsis:30}}</span>\n </he-node-link>\n </th>\n </tr>\n <tr>\n <th class=\"width-auto\">\n <a [href]=\"baseUrl + '/schema/ImpactAssessment#functionalUnit'\" target=\"_blank\">Functional unit:</a>\n <span class=\"pl-1\">1 kg</span>\n </th>\n <th>Product</th>\n <th *ngFor=\"let indicator of indicators\"\n [attr.title]=\"indicator.value.term.units\"\n >{{indicator.value.term.units}}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let impactAssessment of impactAssessments; trackBy: trackById; let i = index\">\n <td class=\"width-auto\" [attr.title]=\"impactName(impactAssessment)\" [style.border-left-color]=\"itemColor(i)\">\n <label *ngIf=\"enableCompare\" class=\"is-inline-block checkbox\">\n <input type=\"checkbox\" class=\"selector\"\n (change)=\"toggleImpact(impactAssessment)\"\n [checked]=\"isSelected(impactAssessment)\"\n >\n </label>\n <he-node-link class=\"is-inline-block\" [node]=\"impactAssessment\">\n <span class=\"is-nowrap has-text-ellipsis\">{{i + 1}}. {{impactName(impactAssessment)}}</span>\n </he-node-link>\n </td>\n <td [attr.title]=\"impactAssessment.product?.name\">\n <he-node-link *ngIf=\"impactAssessment.product\" [node]=\"impactAssessment.product\">\n <span>{{impactAssessment.product.name | ellipsis:30}}</span>\n </he-node-link>\n </td>\n <td class=\"is-nowrap\" *ngFor=\"let indicator of indicators\">\n <span *ngIf=\"indicator.value.values[impactAssessment['@id']]; else emptyValue\"\n class=\"trigger-popover\"\n [ngbPopover]=\"details\" [autoClose]=\"'outside'\"\n triggers=\"manual\" #p=\"ngbPopover\" placement=\"left\" container=\"body\"\n (click)=\"togglePopover(p, { data: indicator.value.values[impactAssessment['@id']], impactAssessment: impactAssessment, key: key })\"\n >\n <span pointer>{{propertyValue(indicator.value.values[impactAssessment['@id']].value, key === 'impacts') | precision:3 | default:'-'}}</span>\n <he-blank-node-state class=\"ml-1\"\n [node]=\"indicator.value.values[impactAssessment['@id']].nodes[0]\"\n key=\"value\"\n [state]=\"impactAssessment.aggregated ? 'aggregated' : null\"\n ></he-blank-node-state>\n </span>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <he-blank-node-state-notice [dataState]=\"dataState\"></he-blank-node-state-notice>\n\n <form *ngIf=\"enableCompare\" class=\"mt-2\" [formGroup]=\"form\" (submit)=\"form.valid && addImpact()\">\n <div class=\"field has-addons\">\n <div class=\"control\">\n <span class=\"button is-small is-static\">Compare with Aggregated Impact Assessment</span>\n </div>\n <div class=\"control is-expanded\" [class.has-icons-right]=\"suggesting || loading\">\n <input class=\"input is-small\"\n placeholder=\"Search by name or id\"\n formControlName=\"search\" name=\"search\"\n\n [ngbTypeahead]=\"suggestImpactAssessment\"\n [inputFormatter]=\"formatter\"\n [resultTemplate]=\"suggestion\"\n [focusFirst]=\"true\"\n >\n <span class=\"icon is-small is-right has-text-grey-dark\" [class.is-hidden]=\"!(suggesting || loading)\">\n <fa-icon icon=\"spinner\" [pulse]=\"true\" size=\"sm\"></fa-icon>\n </span>\n </div>\n <div class=\"control\">\n <button class=\"button is-small\" type=\"submit\" [disabled]=\"suggesting || loading\">\n <fa-icon icon=\"plus\"></fa-icon>\n </button>\n </div>\n </div>\n </form>\n </div>\n\n <he-impact-assessments-indicator-breakdown-chart *ngIf=\"selectedView === View.breakdown\"\n [impactAssessment]=\"impactAssessments[0]\"\n [indicators]=\"impactAssessments[0][key]\"\n ></he-impact-assessments-indicator-breakdown-chart>\n\n <he-impact-assessments-indicators-chart *ngIf=\"impactAssessments.length > 1\" [class.is-hidden]=\"selectedView !== View.chart\"\n [key]=\"key\"\n [impactAssessments]=\"selectedImpactAssessments\"\n [filterTermTypes]=\"filterTermTypes\"\n ></he-impact-assessments-indicators-chart>\n\n <he-impact-assessments-products-logs *ngIf=\"selectedView === View.logs && !isOriginal\"\n [key]=\"key\"\n [impactAssessment]=\"impactAssessments[0]\"\n [filterTermTypes]=\"filterTermTypes\"\n [originalValues]=\"originalValues[0][key]\"\n [recalculatedValues]=\"impactAssessments[0][key]\"\n ></he-impact-assessments-products-logs>\n</ng-container>\n\n<he-node-csv-export-confirm *ngIf=\"showDownload\"\n [nodes]=\"impactAssessments\" [filename]=\"'impact-' + key + '.csv'\" [isUpload]=\"false\"\n [headerKeys]=\"['impactAssessment.id', 'impactAssessment.@id', 'impactAssessment.' + key + '.']\"\n (closed)=\"showDownload = false\"\n></he-node-csv-export-confirm>\n\n<ng-template #emptyTable>\n <div class=\"panel-block\">\n <span>No data</span>\n </div>\n</ng-template>\n\n<ng-template #emptyValue>\n <span>-</span>\n</ng-template>\n\n<ng-template #details let-node=\"impactAssessment\" let-data=\"data\" let-key=\"key\">\n <p *bindOnce=\"node\">\n <b>\n <span *ngIf=\"data.cycle\">{{cycleLabel(node.cycle)}}</span>\n <span *ngIf=\"!data.cycle\">{{data.name}}</span>\n </b>\n </p>\n <he-node-value-details\n [data]=\"data\" [nodeType]=\"node['@type']\" [dataKey]=\"key\"\n ></he-node-value-details>\n</ng-template>\n\n<ng-template #suggestion let-impact=\"result\" let-t=\"term\">\n <div class=\"is-block\">\n <ngb-highlight [result]=\"impact.name || impact.cycle.name\" [term]=\"t\"></ngb-highlight>\n </div>\n <div class=\"columns is-flex\">\n <div class=\"column\" *ngIf=\"impact.country\">\n <span class=\"pr-1 has-text-underline\">Country:</span>\n <span class=\"is-inline-flex\"><ngb-highlight [result]=\"impact.country.name\" [term]=\"t\"></ngb-highlight></span>\n </div>\n <div class=\"column\" *ngIf=\"impact.product\">\n <span class=\"pr-1 has-text-underline\">Product:</span>\n <span class=\"is-inline-flex\"><ngb-highlight [result]=\"impact.product.name\" [term]=\"t\"></ngb-highlight></span>\n </div>\n <div class=\"column\" *ngIf=\"impact.endDate\">\n <span class=\"pr-1 has-text-underline\">Date:</span>\n <span class=\"is-inline-flex\"><ngb-highlight [result]=\"impact.endDate\" [term]=\"t\"></ngb-highlight></span>\n </div>\n </div>\n</ng-template>\n", styles: ["label.checkbox{width:20px}td he-node-link{width:160px}table.data-table td:first-child{border-left-width:8px}\n"], components: [{ type: i1.FaIconComponent, selector: "fa-icon", inputs: ["classes", "icon", "title", "spin", "pulse", "mask", "styles", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"] }, { type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "showExternalLink"] }, { type: BlankNodeStateComponent, selector: "he-blank-node-state", inputs: ["nodeType", "dataKey", "key", "node", "state"] }, { type: BlankNodeStateNoticeComponent, selector: "he-blank-node-state-notice", inputs: ["dataState", "showDeleted"] }, { type: ImpactAssessmentsIndicatorBreakdownChartComponent, selector: "he-impact-assessments-indicator-breakdown-chart", inputs: ["impactAssessment", "indicators"] }, { type: ImpactAssessmentsIndicatorsChartComponent, selector: "he-impact-assessments-indicators-chart", inputs: ["impactAssessments", "key", "filterTermTypes"] }, { type: ImpactAssessmentsProductsLogsComponent, selector: "he-impact-assessments-products-logs", inputs: ["impactAssessment", "key", "filterTermTypes", "originalValues", "recalculatedValues"] }, { type: NodeCsvExportConfirmComponent, selector: "he-node-csv-export-confirm", inputs: ["nodes", "filename", "headerKeys", "extension", "isUpload"], outputs: ["closed"] }, { type: NodeValueDetailsComponent, selector: "he-node-value-details", inputs: ["data", "nodeType", "dataKey"] }, { type: i10.NgbHighlight, selector: "ngb-highlight", inputs: ["highlightClass", "result", "term"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: BindOnceDirective, selector: "[bindOnce]", inputs: ["bindOnce"] }, { type: i1$4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { type: i1$4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i10.NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "placement", "triggers", "container", "disablePopover", "popoverClass", "openDelay", "closeDelay", "ngbPopover", "popoverTitle"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { type: i1$4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i1$4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i10.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "placement", "container", "editable", "focusFirst", "showHint", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { type: i1$4.FormControlName, selector: "[formControlName]", inputs: ["disabled", "formControlName", "ngModel"], outputs: ["ngModelChange"] }], pipes: { "ellipsis": EllipsisPipe, "default": DefaultPipe, "precision": PrecisionPipe } });
5868
5776
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: ImpactAssessmentsProductsComponent, decorators: [{
5869
5777
  type: Component,
@@ -5872,7 +5780,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
5872
5780
  templateUrl: './impact-assessments-products.component.html',
5873
5781
  styleUrls: ['./impact-assessments-products.component.scss']
5874
5782
  }]
5875
- }], ctorParameters: function () { return [{ type: i1$4.FormBuilder }, { type: HeNodeService }, { type: HeSearchService }, { type: ToastService }]; }, propDecorators: { cycles: [{
5783
+ }], ctorParameters: function () { return [{ type: i1$4.FormBuilder }, { type: HeNodeService }, { type: HeSearchService }, { type: HeToastService }]; }, propDecorators: { cycles: [{
5876
5784
  type: Input
5877
5785
  }], impactAssessments: [{
5878
5786
  type: Input
@@ -5957,5 +5865,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
5957
5865
  * Generated bundle index. Do not edit.
5958
5866
  */
5959
5867
 
5960
- export { BibliographiesSearchConfirmComponent, BindOnceDirective, BlankNodeDiffsComponent, BlankNodeStateComponent, BlankNodeStateNoticeComponent, BlankNodeValueDeltaComponent, ClickOutsideDirective, ClipboardComponent, CyclesActivityComponent, CyclesActivityLogsComponent, CyclesCompletenessComponent, CyclesEmissionsChartComponent, CyclesEmissionsComponent, CyclesEmissionsLogsComponent, CyclesFunctionalUnitMeasureComponent, CyclesResultComponent, CyclesSuggestFormComponent, DefaultPipe, DeltaColour, DeltaDisplayType, EllipsisPipe, FilesFormComponent, GetPipe, HE_API_BASE_URL, HE_ORCHESTRATOR_BASE_URL, HeAggregationEngineService, HeAuthService, HeBibliographiesModule, HeCommonModule, HeCommonService, HeCyclesModule, HeEngineService, HeFilesModule, HeFontawesomeModule, HeImpactAssessmentsModule, HeMendeleyService, HeNodeCsvService, HeNodeModule, HeNodeService, HeSchemaService, HeSearchModule, HeSearchService, HeSitesModule, HeUsersService, ImpactAssessmentsIndicatorBreakdownChartComponent, ImpactAssessmentsIndicatorsChartComponent, ImpactAssessmentsProductsComponent, ImpactAssessmentsProductsLogsComponent, KeyToLabelPipe, KeysPipe, Level, LinkKeyValueComponent, MAX_RESULTS, MapsDrawingConfirmComponent, MendeleySearchResult, NodeCsvExportConfirmComponent, NodeCsvSelectHeadersComponent, NodeDiffsComponent, NodeIconComponent, NodeLinkComponent, NodeLogsFileComponent, NodeLogsModelsComponent, NodeMissingLookupFactorsComponent, NodeValueDetailsComponent, PluralizePipe, PopoverComponent, PopoverConfirmComponent, PrecisionPipe, SchemaVersionLinkComponent, SitesMapsComponent, SitesMeasurementsComponent, SitesMeasurementsLogsComponent, SkeletonTextComponent, SocialTagsComponent, TagsInputDirective, TimesPipe, ToastComponent, ToastService, UnitConverterComponent, allCountriesQuery, animateCounter, arrayValue, availableProperties, baseUrl, bottom, countriesQuery, cropsQuery, dataValue, defaultLabel, definitionToSchemaType, delta, deserializeSearchFilters, ellipsis, emptyValue, errorText, filenameWithoutExt, filterParams, findPropertyById, gitHome, gitRawBaseUrl, groupNodesByTerm, grouppedKeys, grouppedValueKeys, handleAPIError, hoursBefore, isChrome, isExternal, isSchemaIri, isScrolledBelow, itemColor, keyToLabel, levels, linkTypeEnabled, listColor, loadScript, mapsUrl, matchAggregatedQuery, matchBoolPrefixQuery, matchCountry, matchExactQuery, matchGlobalRegion, matchNameNormalized, matchNestedKey, matchPhrasePrefixQuery, matchPhraseQuery, matchQuery, matchRegex, matchRegion, matchTermType, matchType, maxAreaSize, measurementValue, minutesBefore, multiMatchQuery, nestingEnabled, nestingTypeEnabled, nodeTypeToString, numberGte, parseData, parseLines, parseMessage, parseNodeType, propertyValue$1 as propertyValue, refToSchemaType, regionsQuery, repeat, roundValue, safeJSONParse, safeJSONStringify, schemaRequiredProperties, schemaTypeToDefaultValue, scrollToEl, scrollTop, searchQuery, searchResultsFields, searchableTypes, serializeSearchFilters, siteTooBig, sortByFn, sortOrder, suggestMatchQuery, suggestQuery, termChildToParent, termLocation, termLocationName, termProperties, termTypeGroups, termTypeLabel, toCsv$1 as toCsv, toDashCase, toFormData, toSearchParams, valueTypeToDefault, waitFor, wildcardQuery, worldRegion };
5868
+ export { ARRAY_DELIMITER, BibliographiesSearchConfirmComponent, BindOnceDirective, BlankNodeDiffsComponent, BlankNodeStateComponent, BlankNodeStateNoticeComponent, BlankNodeValueDeltaComponent, ClickOutsideDirective, ClipboardComponent, CyclesActivityComponent, CyclesActivityLogsComponent, CyclesCompletenessComponent, CyclesEmissionsChartComponent, CyclesEmissionsComponent, CyclesEmissionsLogsComponent, CyclesFunctionalUnitMeasureComponent, CyclesResultComponent, CyclesSuggestFormComponent, DefaultPipe, DeltaColour, DeltaDisplayType, DiffsDisplayType, EllipsisPipe, FilesFormComponent, GetPipe, HE_API_BASE_URL, HE_ORCHESTRATOR_BASE_URL, HeAggregationEngineService, HeAuthService, HeBibliographiesModule, HeCommonModule, HeCommonService, HeCyclesModule, HeEngineService, HeFilesModule, HeFontawesomeModule, HeImpactAssessmentsModule, HeMendeleyService, HeNodeCsvService, HeNodeModule, HeNodeService, HeSchemaService, HeSearchModule, HeSearchService, HeSitesModule, HeToastService, HeUsersService, ImpactAssessmentsIndicatorBreakdownChartComponent, ImpactAssessmentsIndicatorsChartComponent, ImpactAssessmentsProductsComponent, ImpactAssessmentsProductsLogsComponent, KeyToLabelPipe, KeysPipe, Level, LinkKeyValueComponent, MAX_RESULTS, MapsDrawingConfirmComponent, MendeleySearchResult, NodeCsvExportConfirmComponent, NodeCsvSelectHeadersComponent, NodeDiffsComponent, NodeIconComponent, NodeLinkComponent, NodeLogsFileComponent, NodeLogsModelsComponent, NodeMissingLookupFactorsComponent, NodeValueDetailsComponent, PluralizePipe, PopoverComponent, PopoverConfirmComponent, PrecisionPipe, SchemaVersionLinkComponent, SitesMapsComponent, SitesMeasurementsComponent, SitesMeasurementsLogsComponent, SkeletonTextComponent, SocialTagsComponent, TagsInputDirective, TimesPipe, ToastComponent, UnitConverterComponent, addPolygonToFeature, allCountriesQuery, arrayValue, availableProperties, baseUrl, bottom, calculateCycleDuration, calculateCycleDurationEnabled, calculateCycleStartDate, calculateCycleStartDateEnabled, calculatePercentDelta, clustererImage, code, coordinatesToPoint, countriesQuery, createMarker, cropsQuery, customDeltaFuncs, dataPathToKey, dataValue, defaultFeature, defaultLabel, defaultSuggestionType, definitionToSchemaType, delta, deserializeSearchFilters, ellipsis, emptyValue, errorHasError, errorHasWarning, errorText, evaluateSuccess, filenameWithoutExt, fillColor, fillStyle, filterError, filterParams, findConfigModels, findProperty, findPropertyById, formatCustomErrorMessage, formatDiffValues, formatError, formatLinkNodesSuggestions, formatPropertyError, formatSuggestion, gitBranch, gitHome, gitRawBaseUrl, groupChanged, groupNodesByTerm, grouppedKeys, grouppedValueKeys, handleAPIError, hasError, hasWarning, isAddPropertyEnabled, isChrome, isExternal, isMissingOneOfError, isMissingPropertyError, isSchemaIri, isScrolledBelow, itemColor, keyToDataPath, keyToLabel, levels, linkTypeEnabled, listColor, locationQuery, lookupUrl, lookups, mapsUrl, markerIcon, markerPie, matchAggregatedQuery, matchBoolPrefixQuery, matchCountry, matchExactQuery, matchGlobalRegion, matchNameNormalized, matchNestedKey, matchPhrasePrefixQuery, matchPhraseQuery, matchQuery, matchRegex, matchRegion, matchTermType, matchType, maxAreaSize, measurementValue, missingNodeErrorMessage, missingNodeErrors, multiMatchQuery, nestedProperty, nestingEnabled, nestingTypeEnabled, nodeAvailableProperties, nodeLink, nodeLogsUrl, nodeUrl, numberGte, parentKey, parentProperty, parseData, parseDataPath, parseLines, parseMessage, parseNewValue, pathToApiDocsPath, pointToCoordinates, polygonBounds, polygonToCoordinates, polygonsFromFeature, primaryProduct, propertyError, propertyId, propertyValue$1 as propertyValue, recursiveProperties, refToSchemaType, refreshPropertyKeys, regionsQuery, repeat, roundValue, safeJSONParse, safeJSONStringify, schemaRequiredProperties, schemaTypeToDefaultValue, scrollToEl, scrollTop, searchQuery, searchResultsFields, searchableTypes, serializeSearchFilters, siblingProperty, singleProperty, siteTooBig, sortOrder, sortProperties, strokeColor, strokeStyle, suggestMatchQuery, suggestQuery, termChildToParent, termLocation, termLocationName, termProperties, termTypeGroups, termTypeLabel, termTypeLookupUrl, toCsv$1 as toCsv, toDashCase, typeToNewProperty, updateProperties, valueTypeToDefault, waitFor, wildcardQuery, worldRegion };
5961
5869
  //# sourceMappingURL=hestia-earth-ui-components.js.map