"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getChoroplethAnomaliesLayer = exports.AnomaliesMap = void 0; var _react = _interopRequireWildcard(require("react")); var _i18nReact = require("@kbn/i18n-react"); var _i18n = require("@kbn/i18n"); var _eui = require("@elastic/eui"); var _common = require("@kbn/maps-plugin/common"); var _mlIsDefined = require("@kbn/ml-is-defined"); var _kibana = require("../contexts/kibana"); var _ml_embedded_map = require("../components/ml_embedded_map"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ const MAX_ENTITY_VALUES = 3; function getAnomalyRows(anomalies, jobId) { const anomalyRows = {}; for (let i = 0; i < anomalies.length; i++) { const anomaly = anomalies[i]; const location = anomaly.entityValue; if (anomaly.jobId !== jobId) continue; if (anomalyRows[location] === undefined) { // add it to the object and set count to 1 anomalyRows[location] = { count: 1, entityValue: location, max_severity: Math.floor(anomaly.severity) }; } else { anomalyRows[location].count += 1; if (anomaly.severity > anomalyRows[location].max_severity) { anomalyRows[location].max_severity = Math.floor(anomaly.severity); } } } return Object.values(anomalyRows); } const getChoroplethAnomaliesLayer = (anomalies, { layerId, field, jobId }) => { return { id: (0, _eui.htmlIdGenerator)()(), label: _i18n.i18n.translate('xpack.ml.explorer.anomaliesMap.anomaliesCount', { defaultMessage: 'Anomalies count: {jobId}', values: { jobId } }), joins: [{ // Left join is the id from the type of field (e.g. world_countries) leftField: field, right: { id: 'anomaly_count', type: _common.SOURCE_TYPES.TABLE_SOURCE, __rows: getAnomalyRows(anomalies, jobId), __columns: [{ name: 'entityValue', type: 'string' }, { name: 'count', type: 'number' }, { name: 'max_severity', type: 'number' }], // Right join/term is the field in the doc you’re trying to join it to (foreign key - e.g. US) term: 'entityValue' } }], sourceDescriptor: { type: 'EMS_FILE', id: layerId }, style: { type: 'VECTOR', // @ts-ignore missing style properties. Remove once 'VectorLayerDescriptor' type is updated properties: { icon: { type: _common.STYLE_TYPE.STATIC, options: { value: 'marker' } }, fillColor: { type: _common.STYLE_TYPE.DYNAMIC, options: { color: 'Blue to Red', colorCategory: 'palette_0', fieldMetaOptions: { isEnabled: true, sigma: 3 }, type: _common.COLOR_MAP_TYPE.ORDINAL, field: { name: 'count', origin: _common.FIELD_ORIGIN.JOIN }, useCustomColorRamp: false } }, lineColor: { type: _common.STYLE_TYPE.DYNAMIC, options: { fieldMetaOptions: { isEnabled: true } } }, lineWidth: { type: _common.STYLE_TYPE.STATIC, options: { size: 1 } } }, isTimeAware: true }, visible: false, type: _common.LAYER_TYPE.GEOJSON_VECTOR }; }; exports.getChoroplethAnomaliesLayer = getChoroplethAnomaliesLayer; const AnomaliesMap = ({ anomalies, jobIds }) => { const [EMSSuggestions, setEMSSuggestions] = (0, _react.useState)(); const { services: { maps: mapsPlugin } } = (0, _kibana.useMlKibana)(); const getEMSTermSuggestions = (0, _react.useCallback)(async () => { if (!mapsPlugin) return; const suggestions = await Promise.all(jobIds.map(async jobId => { const entityValues = new Set(); let entityName; for (let i = 0; i < anomalies.length; i++) { if (jobId === anomalies[i].jobId && anomalies[i].entityValue !== '' && anomalies[i].entityValue !== undefined && anomalies[i].entityName !== '' && anomalies[i].entityName !== undefined) { entityValues.add(anomalies[i].entityValue); if (!entityName) { entityName = anomalies[i].entityName; } } if ( // convert to set so it's unique values entityValues.size === MAX_ENTITY_VALUES) break; } const suggestion = await mapsPlugin.suggestEMSTermJoinConfig({ sampleValues: Array.from(entityValues), sampleValuesColumnName: entityName || '' }); if (suggestion) { return { jobId, ...suggestion }; } return suggestion; })); setEMSSuggestions(suggestions.filter(_mlIsDefined.isDefined)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [...jobIds]); (0, _react.useEffect)(function getInitialEMSTermSuggestions() { if (anomalies && anomalies.length > 0) { getEMSTermSuggestions(); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [...jobIds]); const layerList = (0, _react.useMemo)(() => { if (!(EMSSuggestions !== null && EMSSuggestions !== void 0 && EMSSuggestions.length)) return []; return EMSSuggestions.map(suggestion => { return getChoroplethAnomaliesLayer(anomalies, suggestion); }, []); }, [EMSSuggestions, anomalies]); const layersWithAnomalies = layerList.filter(layer => { var _layer$joins$, _layer$joins$$right; // @ts-ignore _rows does not exist - can remove when VectorLayerDescriptor is updated const rows = Array.isArray(layer.joins) ? (_layer$joins$ = layer.joins[0]) === null || _layer$joins$ === void 0 ? void 0 : (_layer$joins$$right = _layer$joins$.right) === null || _layer$joins$$right === void 0 ? void 0 : _layer$joins$$right.__rows : []; return rows.length; }); // set the layer with anomalies to visible if (layersWithAnomalies.length > 0) { layersWithAnomalies[0].visible = true; } if ((EMSSuggestions === null || EMSSuggestions === void 0 ? void 0 : EMSSuggestions.length) === 0 || layersWithAnomalies.length === 0) { return null; } return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_eui.EuiPanel, { "data-test-subj": "mlAnomaliesMapContainer", hasShadow: false, hasBorder: true }, /*#__PURE__*/_react.default.createElement(_eui.EuiAccordion, { id: "mlAnomalyExplorerAnomaliesMapAccordionId", initialIsOpen: true, buttonContent: /*#__PURE__*/_react.default.createElement(_eui.EuiTitle, { size: 'xs' }, /*#__PURE__*/_react.default.createElement("h2", null, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.mapTitle", defaultMessage: "Anomaly count by location {infoTooltip}", values: { infoTooltip: /*#__PURE__*/_react.default.createElement(_eui.EuiIconTip, { content: "Map colors indicate the number of anomalies in each area.", position: "top", type: "iInCircle" }) } }))) }, /*#__PURE__*/_react.default.createElement("div", { "data-test-subj": "mlAnomalyExplorerAnomaliesMap", style: { width: '100%', height: 300 } }, /*#__PURE__*/_react.default.createElement(_ml_embedded_map.MlEmbeddedMapComponent, { layerList: layerList })))), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "m" })); }; exports.AnomaliesMap = AnomaliesMap;