diff --git a/x-pack/plugins/ml/common/constants/file_datavisualizer.js b/x-pack/plugins/ml/common/constants/file_datavisualizer.js new file mode 100644 index 000000000000..262e2a1a337a --- /dev/null +++ b/x-pack/plugins/ml/common/constants/file_datavisualizer.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +export const MAX_BYTES = 104857600; diff --git a/x-pack/plugins/ml/common/constants/license.js b/x-pack/plugins/ml/common/constants/license.js new file mode 100644 index 000000000000..9f4b06bbbc95 --- /dev/null +++ b/x-pack/plugins/ml/common/constants/license.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +export const LICENSE_TYPE = { + BASIC: 0, + FULL: 1, +}; diff --git a/x-pack/plugins/ml/index.js b/x-pack/plugins/ml/index.js index cca3e97bdcd8..4a434ec515ca 100644 --- a/x-pack/plugins/ml/index.js +++ b/x-pack/plugins/ml/index.js @@ -24,6 +24,7 @@ import { filtersRoutes } from './server/routes/filters'; import { resultsServiceRoutes } from './server/routes/results_service'; import { jobServiceRoutes } from './server/routes/job_service'; import { jobAuditMessagesRoutes } from './server/routes/job_audit_messages'; +import { fileDataVisualizerRoutes } from './server/routes/file_data_visualizer'; export const ml = (kibana) => { return new kibana.Plugin({ @@ -40,8 +41,9 @@ export const ml = (kibana) => { euiIconType: 'machineLearningApp', main: 'plugins/ml/app', }, + styleSheetPaths: `${__dirname}/public/index.scss`, hacks: ['plugins/ml/hacks/toggle_app_link_in_nav'], - home: ['plugins/ml/register_feature'] + home: ['plugins/ml/register_feature'], }, @@ -73,7 +75,7 @@ export const ml = (kibana) => { const config = server.config(); return { kbnIndex: config.get('kibana.index'), - esServerUrl: config.get('elasticsearch.url') + esServerUrl: config.get('elasticsearch.url'), }; }); @@ -91,6 +93,7 @@ export const ml = (kibana) => { resultsServiceRoutes(server, commonRouteConfig); jobServiceRoutes(server, commonRouteConfig); jobAuditMessagesRoutes(server, commonRouteConfig); + fileDataVisualizerRoutes(server, commonRouteConfig); } }); diff --git a/x-pack/plugins/ml/public/app.js b/x-pack/plugins/ml/public/app.js index ad76c56cfdf1..1af1b28286b9 100644 --- a/x-pack/plugins/ml/public/app.js +++ b/x-pack/plugins/ml/public/app.js @@ -32,6 +32,7 @@ import 'plugins/ml/components/confirm_modal'; import 'plugins/ml/components/nav_menu'; import 'plugins/ml/components/loading_indicator'; import 'plugins/ml/settings'; +import 'plugins/ml/file_datavisualizer'; import uiRoutes from 'ui/routes'; diff --git a/x-pack/plugins/ml/public/components/nav_menu/_index.scss b/x-pack/plugins/ml/public/components/nav_menu/_index.scss new file mode 100644 index 000000000000..a5981e1022e1 --- /dev/null +++ b/x-pack/plugins/ml/public/components/nav_menu/_index.scss @@ -0,0 +1 @@ +@import 'nav_menu' diff --git a/x-pack/plugins/ml/public/components/nav_menu/_nav_menu.scss b/x-pack/plugins/ml/public/components/nav_menu/_nav_menu.scss new file mode 100644 index 000000000000..6b1be3c23839 --- /dev/null +++ b/x-pack/plugins/ml/public/components/nav_menu/_nav_menu.scss @@ -0,0 +1,4 @@ +.disabled-nav-link { + color: $euiColorMediumShade; + pointer-events: none; +} diff --git a/x-pack/plugins/ml/public/components/nav_menu/nav_menu.html b/x-pack/plugins/ml/public/components/nav_menu/nav_menu.html index 837bd41b511a..345ed3bddb97 100644 --- a/x-pack/plugins/ml/public/components/nav_menu/nav_menu.html +++ b/x-pack/plugins/ml/public/components/nav_menu/nav_menu.html @@ -15,17 +15,25 @@
- Job Management + ng-class="{'kuiLocalTab-isSelected': isActiveTab('jobs'), 'disabled-nav-link': disableLinks}"> + Job Management + - Anomaly Explorer + ng-class="{'kuiLocalTab-isSelected': isActiveTab('explorer'), 'disabled-nav-link': disableLinks}"> + Anomaly Explorer + - Single Metric Viewer + ng-class="{'kuiLocalTab-isSelected': isActiveTab('timeseriesexplorer'), 'disabled-nav-link': disableLinks}"> + Single Metric Viewer + + + Data Visualizer + - Settings + ng-class="{'kuiLocalTab-isSelected': isActiveTab('settings'), 'disabled-nav-link': disableLinks}"> + Settings +
diff --git a/x-pack/plugins/ml/public/components/nav_menu/nav_menu.js b/x-pack/plugins/ml/public/components/nav_menu/nav_menu.js index 117a0fdc03b7..c9024fb41c3d 100644 --- a/x-pack/plugins/ml/public/components/nav_menu/nav_menu.js +++ b/x-pack/plugins/ml/public/components/nav_menu/nav_menu.js @@ -10,6 +10,7 @@ import _ from 'lodash'; import $ from 'jquery'; import template from './nav_menu.html'; import uiRouter from 'ui/routes'; +import { isFullLicense } from '../../license/check_license'; import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/ml'); @@ -21,12 +22,16 @@ module.directive('mlNavMenu', function (breadcrumbState, config) { template, link: function (scope, el, attrs) { + + // Tabs scope.name = attrs.name; scope.showTabs = false; if (scope.name === 'jobs' || scope.name === 'settings' || + scope.name === 'datavisualizer' || + scope.name === 'filedatavisualizer' || scope.name === 'timeseriesexplorer' || scope.name === 'explorer') { scope.showTabs = true; @@ -35,6 +40,8 @@ module.directive('mlNavMenu', function (breadcrumbState, config) { return scope.name === path; }; + scope.disableLinks = (isFullLicense() === false); + // Breadcrumbs const crumbNames = { jobs: { label: 'Job Management', url: '#/jobs' }, @@ -44,6 +51,7 @@ module.directive('mlNavMenu', function (breadcrumbState, config) { population: { label: 'Population job', url: '' }, advanced: { label: 'Advanced Job Configuration', url: '' }, datavisualizer: { label: 'Data Visualizer', url: '' }, + filedatavisualizer: { label: 'File Data Visualizer', url: '' }, explorer: { label: 'Anomaly Explorer', url: '#/explorer' }, timeseriesexplorer: { label: 'Single Metric Viewer', url: '#/timeseriesexplorer' }, settings: { label: 'Settings', url: '#/settings' }, diff --git a/x-pack/plugins/ml/public/datavisualizer/_index.scss b/x-pack/plugins/ml/public/datavisualizer/_index.scss new file mode 100644 index 000000000000..af259ce363a5 --- /dev/null +++ b/x-pack/plugins/ml/public/datavisualizer/_index.scss @@ -0,0 +1 @@ +@import './selector/index'; diff --git a/x-pack/plugins/ml/public/datavisualizer/datavisualizer.html b/x-pack/plugins/ml/public/datavisualizer/datavisualizer.html index 3719bafece2f..4a4b782602ad 100644 --- a/x-pack/plugins/ml/public/datavisualizer/datavisualizer.html +++ b/x-pack/plugins/ml/public/datavisualizer/datavisualizer.html @@ -11,7 +11,10 @@ -
+
@@ -170,7 +173,7 @@
-
+
diff --git a/x-pack/plugins/ml/public/datavisualizer/datavisualizer_controller.js b/x-pack/plugins/ml/public/datavisualizer/datavisualizer_controller.js index d5ca427c0661..28af872abfc1 100644 --- a/x-pack/plugins/ml/public/datavisualizer/datavisualizer_controller.js +++ b/x-pack/plugins/ml/public/datavisualizer/datavisualizer_controller.js @@ -24,7 +24,7 @@ import { decorateQuery, luceneStringToDsl } from 'ui/courier'; import { ML_JOB_FIELD_TYPES, KBN_FIELD_TYPES } from 'plugins/ml/../common/constants/field_types'; import { kbnTypeToMLJobType } from 'plugins/ml/util/field_types_utils'; import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets'; -import { checkLicenseExpired } from 'plugins/ml/license/check_license'; +import { checkBasicLicense, isFullLicense } from 'plugins/ml/license/check_license'; import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege'; import { createSearchItems } from 'plugins/ml/jobs/new_job/utils/new_job_utils'; import { loadCurrentIndexPattern, loadCurrentSavedSearch, timeBasedIndexCheck } from 'plugins/ml/util/index_utils'; @@ -37,7 +37,7 @@ uiRoutes .when('/jobs/new_job/datavisualizer', { template, resolve: { - CheckLicense: checkLicenseExpired, + CheckLicense: checkBasicLicense, privileges: checkGetJobsPrivilege, indexPattern: loadCurrentIndexPattern, savedSearch: loadCurrentSavedSearch, @@ -93,6 +93,8 @@ module $scope.fieldFilter = ''; $scope.recognizerResults = { count: 0 }; + $scope.showSidebar = isFullLicense(); + // Check for a saved query in the AppState or via a savedSearchId in the URL. $scope.searchQueryText = ''; if (_.has($scope.appState, 'query')) { diff --git a/x-pack/plugins/ml/public/datavisualizer/index.js b/x-pack/plugins/ml/public/datavisualizer/index.js index e94162faefcc..6d5d23498998 100644 --- a/x-pack/plugins/ml/public/datavisualizer/index.js +++ b/x-pack/plugins/ml/public/datavisualizer/index.js @@ -7,6 +7,7 @@ import './styles/main.less'; +import './selector'; import './datavisualizer_controller'; import 'plugins/ml/components/data_recognizer'; import 'plugins/ml/components/field_data_card'; diff --git a/x-pack/plugins/ml/public/datavisualizer/selector/_index.scss b/x-pack/plugins/ml/public/datavisualizer/selector/_index.scss new file mode 100644 index 000000000000..75f8d1c01439 --- /dev/null +++ b/x-pack/plugins/ml/public/datavisualizer/selector/_index.scss @@ -0,0 +1 @@ +@import 'selector'; diff --git a/x-pack/plugins/ml/public/datavisualizer/selector/_selector.scss b/x-pack/plugins/ml/public/datavisualizer/selector/_selector.scss new file mode 100644 index 000000000000..a6007a63620e --- /dev/null +++ b/x-pack/plugins/ml/public/datavisualizer/selector/_selector.scss @@ -0,0 +1,5 @@ +.ml-datavisualizer-selector { + flex-grow: 1; + background-color: $euiColorLightestShade; + min-height: 100vh; +} diff --git a/x-pack/plugins/ml/public/datavisualizer/selector/datavisualizer_selector.js b/x-pack/plugins/ml/public/datavisualizer/selector/datavisualizer_selector.js new file mode 100644 index 000000000000..c9fee6ea9cd8 --- /dev/null +++ b/x-pack/plugins/ml/public/datavisualizer/selector/datavisualizer_selector.js @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { + EuiButton, + EuiCard, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiPage, + EuiPageBody, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +import { isFullLicense } from '../../license/check_license'; + +function startTrialDescription() { + return ( + + To experience what the full Machine Learning features of a {' '} + + Platinum subscription + {' '} + have to offer, start a 30-day trial from the license management page. + + ); +} + + +export function DatavisualizerSelector() { + + const startTrialVisible = (isFullLicense() === false); + + return ( + + + + + +

Data Visualizer

+
+
+
+ + + + + The Machine Learning Data Visualizer tool helps you understand your data, by analyzing the metrics and fields in + a log file or an existing Elasticsearch index. + + + + + + + } + title="Import data" + description="Visualize data from a log file. Supported for files up to 100MB in size." + betaBadgeLabel="Experimental" + betaBadgeTooltipContent="Experimental feature. We'd love to hear your feedback." + footer={ + + Select file + + } + /> + + + } + title="Pick index pattern" + description="Visualize data in an existing Elasticsearch index." + footer={ + + Select index + + } + /> + + + {startTrialVisible === true && + + + + + + + Start trial + + } + /> + + + + } +
+
+ ); +} diff --git a/x-pack/plugins/ml/public/datavisualizer/selector/directive.js b/x-pack/plugins/ml/public/datavisualizer/selector/directive.js new file mode 100644 index 000000000000..206ebcd96ed8 --- /dev/null +++ b/x-pack/plugins/ml/public/datavisualizer/selector/directive.js @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import 'ngreact'; + +import { uiModules } from 'ui/modules'; +const module = uiModules.get('apps/ml', ['react']); + +import { checkBasicLicense } from 'plugins/ml/license/check_license'; +import { checkFindFileStructurePrivilege } from 'plugins/ml/privilege/check_privilege'; +import { initPromise } from 'plugins/ml/util/promise'; + +import uiRoutes from 'ui/routes'; + +const template = ``; + +uiRoutes + .when('/datavisualizer', { + template, + resolve: { + CheckLicense: checkBasicLicense, + privileges: checkFindFileStructurePrivilege, + initPromise: initPromise(false) + } + }); + + +import { DatavisualizerSelector } from './datavisualizer_selector'; + +module.directive('datavisualizerSelector', function ($injector) { + const reactDirective = $injector.get('reactDirective'); + + return reactDirective(DatavisualizerSelector, undefined, { restrict: 'E' }, { }); +}); diff --git a/x-pack/plugins/ml/public/datavisualizer/selector/index.js b/x-pack/plugins/ml/public/datavisualizer/selector/index.js new file mode 100644 index 000000000000..383901729132 --- /dev/null +++ b/x-pack/plugins/ml/public/datavisualizer/selector/index.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import './directive'; diff --git a/x-pack/plugins/ml/public/datavisualizer/styles/main.less b/x-pack/plugins/ml/public/datavisualizer/styles/main.less index 64595835e608..5ce72afc8aa4 100644 --- a/x-pack/plugins/ml/public/datavisualizer/styles/main.less +++ b/x-pack/plugins/ml/public/datavisualizer/styles/main.less @@ -25,6 +25,9 @@ display: inline-block; padding-right: 10px; } + .no-sidebar { + width: 100%; + } .datavisualizer-sidebar { width: 290px; diff --git a/x-pack/plugins/ml/public/explorer/explorer_controller.js b/x-pack/plugins/ml/public/explorer/explorer_controller.js index e142ce0f8e3a..bbecb26e5b2f 100644 --- a/x-pack/plugins/ml/public/explorer/explorer_controller.js +++ b/x-pack/plugins/ml/public/explorer/explorer_controller.js @@ -28,7 +28,7 @@ import { initPromise } from 'plugins/ml/util/promise'; import template from './explorer.html'; import uiRoutes from 'ui/routes'; -import { checkLicense } from 'plugins/ml/license/check_license'; +import { checkFullLicense } from 'plugins/ml/license/check_license'; import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege'; import { loadIndexPatterns, getIndexPatterns } from 'plugins/ml/util/index_utils'; import { refreshIntervalWatcher } from 'plugins/ml/util/refresh_interval_watcher'; @@ -51,7 +51,7 @@ uiRoutes .when('/explorer/?', { template, resolve: { - CheckLicense: checkLicense, + CheckLicense: checkFullLicense, privileges: checkGetJobsPrivilege, indexPatterns: loadIndexPatterns, initPromise: initPromise(true) diff --git a/x-pack/plugins/ml/public/file_datavisualizer/_file_datavisualizer.scss b/x-pack/plugins/ml/public/file_datavisualizer/_file_datavisualizer.scss new file mode 100644 index 000000000000..64d64ce85430 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/_file_datavisualizer.scss @@ -0,0 +1,7 @@ +@import 'components/index'; + +.file-datavisualizer-container { + padding: 20px; + background-color: $euiColorLightestShade; + min-height: calc(100vh - 70px); +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/_index.scss b/x-pack/plugins/ml/public/file_datavisualizer/_index.scss new file mode 100644 index 000000000000..9e91116dfcf6 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/_index.scss @@ -0,0 +1 @@ +@import 'file_datavisualizer'; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/_index.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/_index.scss new file mode 100644 index 000000000000..c1a39da59fdb --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/_index.scss @@ -0,0 +1,6 @@ +@import 'file_datavisualizer_view/index'; +@import 'results_view/index'; +@import 'analysis_summary/index'; +@import 'fields_stats/index'; +@import 'about_panel/index'; +@import 'import_summary/index'; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/_about_panel.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/_about_panel.scss new file mode 100644 index 000000000000..9654351a0ab6 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/_about_panel.scss @@ -0,0 +1,6 @@ +.file-datavisualizer-about-panel__icon { + width: $euiSizeXL * 3; + height: $euiSizeXL * 3; + margin-left: $euiSizeXL; + margin-right: $euiSizeL; +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/_index.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/_index.scss new file mode 100644 index 000000000000..e2edab76538c --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/_index.scss @@ -0,0 +1 @@ +@import 'about_panel' diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/about_panel.js b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/about_panel.js new file mode 100644 index 000000000000..cfd718399e7a --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/about_panel.js @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import React from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiSpacer, + EuiPanel, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +export function AboutPanel() { + + return ( + + + + + + + +

+ Visualize data from a log file +

+
+ + +

+ The Machine Learning Data Visualizer helps you understand the fields and metrics + in a log file as preparation for further analysis. After analyzing the data in the + file you can then choose to import the data into an elasticsearch index. +

+
+ + +

+ Select the file to visualize using the button at the top of the page, + and we will then attempt to analyze its structure. +

+
+ + +

+ The log file formats we currently support are: +

+
+ + + + + + + +

+ JSON +

+
+
+
+ + + + + + + +

+ Delimited text files such as CSV and TSV +

+
+
+
+ + + + + + + +

+ Log files consisting of semi-structured text with the timestamp in each message having a common format +

+
+
+
+ + +

+ Files up to 100MB in size can be uploaded. +

+
+ + +

+ This is an experimental feature. For any feedback please create an issue in  + + GitHub + . +

+
+
+
+ +
+ ); +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/index.js b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/index.js new file mode 100644 index 000000000000..7eba4a7974e6 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/index.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +export { AboutPanel } from './about_panel'; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/_analysis_summary.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/_analysis_summary.scss new file mode 100644 index 000000000000..50496ef9b23e --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/_analysis_summary.scss @@ -0,0 +1,11 @@ +.analysis-summary-list.euiDescriptionList { + // adding overrides for title and desciption + // these have to be overridden here as they are not + // accessable as overrides in the EuiDescriptionList component + .euiDescriptionList__title { + flex-basis: 15%; + } + .euiDescriptionList__description { + flex-basis: 85%; + } +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/_index.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/_index.scss new file mode 100644 index 000000000000..61cd703f5306 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/_index.scss @@ -0,0 +1 @@ +@import 'analysis_summary'; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/analysis_summary.js b/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/analysis_summary.js new file mode 100644 index 000000000000..4fae23add8d4 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/analysis_summary.js @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import React from 'react'; + +import { + EuiTitle, + EuiSpacer, + EuiDescriptionList, +} from '@elastic/eui'; + +export function AnalysisSummary({ results }) { + const items = createDisplayItems(results); + + return ( + + +

Summary

+
+ + + + +
+ ); +} + +function createDisplayItems(results) { + const items = [ + { + title: 'Number of lines analyzed', + description: results.num_lines_analyzed, + }, + // { + // title: 'Charset', + // description: results.charset, + // } + ]; + + if (results.format !== undefined) { + items.push({ + title: 'Format', + description: results.format, + }); + + if (results.format === 'delimited') { + items.push({ + title: 'Delimiter', + description: results.delimiter, + }); + + items.push({ + title: 'Has header row', + description: `${results.has_header_row}`, + }); + + } + } + + if (results.grok_pattern !== undefined) { + items.push({ + title: 'Grok pattern', + description: results.grok_pattern, + }); + } + + if (results.timestamp_field !== undefined) { + items.push({ + title: 'Time field', + description: results.timestamp_field, + }); + } + + if (results.joda_timestamp_formats !== undefined) { + const s = (results.joda_timestamp_formats.length > 1) ? 's' : ''; + items.push({ + title: `Time format${s}`, + description: results.joda_timestamp_formats.join(', '), + }); + } + + return items; +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/index.js b/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/index.js new file mode 100644 index 000000000000..e1f9ecfa01df --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/analysis_summary/index.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +export { AnalysisSummary } from './analysis_summary'; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/__snapshots__/overrides.test.js.snap b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/__snapshots__/overrides.test.js.snap new file mode 100644 index 000000000000..e1697b865ce1 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/__snapshots__/overrides.test.js.snap @@ -0,0 +1,465 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Overrides render overrides 1`] = ` + + + + json + , + "value": "json", + }, + Object { + "inputDisplay": + delimited + , + "value": "delimited", + }, + Object { + "inputDisplay": + semi_structured_text + , + "value": "semi_structured_text", + }, + Object { + "inputDisplay": + xml + , + "value": "xml", + }, + ] + } + /> + + + + dd/MMM/YYYY:HH:mm:ss Z + , + "value": "dd/MMM/YYYY:HH:mm:ss Z", + }, + Object { + "inputDisplay": + EEE MMM dd HH:mm zzz YYYY + , + "value": "EEE MMM dd HH:mm zzz YYYY", + }, + Object { + "inputDisplay": + EEE MMM dd HH:mm:ss YYYY + , + "value": "EEE MMM dd HH:mm:ss YYYY", + }, + Object { + "inputDisplay": + EEE MMM dd HH:mm:ss zzz YYYY + , + "value": "EEE MMM dd HH:mm:ss zzz YYYY", + }, + Object { + "inputDisplay": + EEE MMM dd YYYY HH:mm zzz + , + "value": "EEE MMM dd YYYY HH:mm zzz", + }, + Object { + "inputDisplay": + EEE MMM dd YYYY HH:mm:ss zzz + , + "value": "EEE MMM dd YYYY HH:mm:ss zzz", + }, + Object { + "inputDisplay": + EEE, dd MMM YYYY HH:mm Z + , + "value": "EEE, dd MMM YYYY HH:mm Z", + }, + Object { + "inputDisplay": + EEE, dd MMM YYYY HH:mm ZZ + , + "value": "EEE, dd MMM YYYY HH:mm ZZ", + }, + Object { + "inputDisplay": + EEE, dd MMM YYYY HH:mm:ss Z + , + "value": "EEE, dd MMM YYYY HH:mm:ss Z", + }, + Object { + "inputDisplay": + EEE, dd MMM YYYY HH:mm:ss ZZ + , + "value": "EEE, dd MMM YYYY HH:mm:ss ZZ", + }, + Object { + "inputDisplay": + ISO8601 + , + "value": "ISO8601", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss + , + "value": "MMM dd HH:mm:ss", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss,SSS + , + "value": "MMM dd HH:mm:ss,SSS", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss,SSSSSS + , + "value": "MMM dd HH:mm:ss,SSSSSS", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss,SSSSSSSSS + , + "value": "MMM dd HH:mm:ss,SSSSSSSSS", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss.SSS + , + "value": "MMM dd HH:mm:ss.SSS", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss.SSSSSS + , + "value": "MMM dd HH:mm:ss.SSSSSS", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss.SSSSSSSSS + , + "value": "MMM dd HH:mm:ss.SSSSSSSSS", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss:SSS + , + "value": "MMM dd HH:mm:ss:SSS", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss:SSSSSS + , + "value": "MMM dd HH:mm:ss:SSSSSS", + }, + Object { + "inputDisplay": + MMM dd HH:mm:ss:SSSSSSSSS + , + "value": "MMM dd HH:mm:ss:SSSSSSSSS", + }, + Object { + "inputDisplay": + MMM dd YYYY HH:mm:ss + , + "value": "MMM dd YYYY HH:mm:ss", + }, + Object { + "inputDisplay": + MMM dd, YYYY h:mm:ss a + , + "value": "MMM dd, YYYY h:mm:ss a", + }, + Object { + "inputDisplay": + TAI64N + , + "value": "TAI64N", + }, + Object { + "inputDisplay": + UNIX + , + "value": "UNIX", + }, + Object { + "inputDisplay": + UNIX_MS + , + "value": "UNIX_MS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss + , + "value": "YYYY-MM-dd HH:mm:ss", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSS + , + "value": "YYYY-MM-dd HH:mm:ss,SSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSSSS + , + "value": "YYYY-MM-dd HH:mm:ss,SSSSSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSSSSSSS + , + "value": "YYYY-MM-dd HH:mm:ss,SSSSSSSSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSS + , + "value": "YYYY-MM-dd HH:mm:ss.SSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSSSS + , + "value": "YYYY-MM-dd HH:mm:ss.SSSSSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSSSSSSS + , + "value": "YYYY-MM-dd HH:mm:ss.SSSSSSSSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSS + , + "value": "YYYY-MM-dd HH:mm:ss:SSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSSSS + , + "value": "YYYY-MM-dd HH:mm:ss:SSSSSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSSSSSSS + , + "value": "YYYY-MM-dd HH:mm:ss:SSSSSSSSS", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSS Z + , + "value": "YYYY-MM-dd HH:mm:ss,SSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSSSS Z + , + "value": "YYYY-MM-dd HH:mm:ss,SSSSSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSSSSSSS Z + , + "value": "YYYY-MM-dd HH:mm:ss,SSSSSSSSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSS Z + , + "value": "YYYY-MM-dd HH:mm:ss.SSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSSSS Z + , + "value": "YYYY-MM-dd HH:mm:ss.SSSSSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSSSSSSS Z + , + "value": "YYYY-MM-dd HH:mm:ss.SSSSSSSSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSS Z + , + "value": "YYYY-MM-dd HH:mm:ss:SSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSSSS Z + , + "value": "YYYY-MM-dd HH:mm:ss:SSSSSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSSSSSSS Z + , + "value": "YYYY-MM-dd HH:mm:ss:SSSSSSSSS Z", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSZ + , + "value": "YYYY-MM-dd HH:mm:ss,SSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSSSSZ + , + "value": "YYYY-MM-dd HH:mm:ss,SSSSSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSSSSSSSZ + , + "value": "YYYY-MM-dd HH:mm:ss,SSSSSSSSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSZ + , + "value": "YYYY-MM-dd HH:mm:ss.SSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSSSSZ + , + "value": "YYYY-MM-dd HH:mm:ss.SSSSSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSSSSSSSZ + , + "value": "YYYY-MM-dd HH:mm:ss.SSSSSSSSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSZ + , + "value": "YYYY-MM-dd HH:mm:ss:SSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSSSSZ + , + "value": "YYYY-MM-dd HH:mm:ss:SSSSSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSSSSSSSZ + , + "value": "YYYY-MM-dd HH:mm:ss:SSSSSSSSSZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss,SSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSSSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss,SSSSSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss,SSSSSSSSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss,SSSSSSSSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss.SSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSSSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss.SSSSSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss.SSSSSSSSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss.SSSSSSSSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss:SSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSSSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss:SSSSSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ss:SSSSSSSSSZZ + , + "value": "YYYY-MM-dd HH:mm:ss:SSSSSSSSSZZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ssZ + , + "value": "YYYY-MM-dd HH:mm:ssZ", + }, + Object { + "inputDisplay": + YYYY-MM-dd HH:mm:ssZZ + , + "value": "YYYY-MM-dd HH:mm:ssZZ", + }, + Object { + "inputDisplay": + YYYYMMddHHmmss + , + "value": "YYYYMMddHHmmss", + }, + ] + } + /> + + + + + +`; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/_edit_flyout.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/_edit_flyout.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/_index.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/_index.scss new file mode 100644 index 000000000000..9558e46aad55 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/_index.scss @@ -0,0 +1 @@ +@import 'edit_flyout' diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/edit_flyout.js b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/edit_flyout.js new file mode 100644 index 000000000000..ff1a42c82638 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/edit_flyout.js @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import React, { + Component, +} from 'react'; + +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiButtonEmpty, +} from '@elastic/eui'; + +import { Overrides } from './overrides'; + +export class EditFlyout extends Component { + constructor(props) { + super(props); + + this.state = { + isFlyoutVisible: false, + }; + + this.applyOverrides = () => {}; + } + + componentDidMount() { + if (typeof this.props.setShowFunction === 'function') { + this.props.setShowFunction(this.showFlyout); + } + } + + componentWillUnmount() { + if (typeof this.props.unsetShowFunction === 'function') { + this.props.unsetShowFunction(); + } + } + + closeFlyout = () => { + this.setState({ isFlyoutVisible: false }); + } + + showFlyout = () => { + this.setState({ isFlyoutVisible: true }); + } + + applyAndClose = () => { + this.applyOverrides(); + this.closeFlyout(); + } + + setApplyOverrides = (applyOverrides) => { + this.applyOverrides = applyOverrides; + } + unsetApplyOverrides = () => { + this.applyOverrides = () => {}; + } + + render() { + const { isFlyoutVisible } = this.state; + const { + setOverrides, + overrides, + originalSettings, + fields, + } = this.props; + + return ( + + { isFlyoutVisible && + + + + +

+ Override settings +

+
+
+ + + + + {/* { }} + /> */} + + + + + + + Close + + + + + Apply + + + + +
+ } +
+ ); + } +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/index.js b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/index.js new file mode 100644 index 000000000000..8985d4ef657a --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/index.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +export { EditFlyout } from './edit_flyout'; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/index.js b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/index.js new file mode 100644 index 000000000000..44cf5e0a46ce --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/index.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { + getCharsetOptions, + getFormatOptions, + getTimestampFormatOptions, + getDelimiterOptions, + getQuoteOptions, +} from './options'; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/option_lists.js b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/option_lists.js new file mode 100644 index 000000000000..8918850c3f97 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/option_lists.js @@ -0,0 +1,281 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +export const FORMAT_OPTIONS = [ + 'json', + 'delimited', + 'semi_structured_text', + 'xml', +]; + +export const TIMESTAMP_OPTIONS = [ + 'dd/MMM/YYYY:HH:mm:ss Z', + 'EEE MMM dd HH:mm zzz YYYY', + 'EEE MMM dd HH:mm:ss YYYY', + 'EEE MMM dd HH:mm:ss zzz YYYY', + 'EEE MMM dd YYYY HH:mm zzz', + 'EEE MMM dd YYYY HH:mm:ss zzz', + 'EEE, dd MMM YYYY HH:mm Z', + 'EEE, dd MMM YYYY HH:mm ZZ', + 'EEE, dd MMM YYYY HH:mm:ss Z', + 'EEE, dd MMM YYYY HH:mm:ss ZZ', + 'ISO8601', + // 'MMM d HH:mm:ss', + + // 'MMM d HH:mm:ss,SSS', + // 'MMM d HH:mm:ss,SSSSSS', + // 'MMM d HH:mm:ss,SSSSSSSSS', + // 'MMM d HH:mm:ss.SSS', + // 'MMM d HH:mm:ss.SSSSSS', + // 'MMM d HH:mm:ss.SSSSSSSSS', + // 'MMM d HH:mm:ss:SSS', + // 'MMM d HH:mm:ss:SSSSSS', + // 'MMM d HH:mm:ss:SSSSSSSSS', + + // 'MMM d YYYY HH:mm:ss', + 'MMM dd HH:mm:ss', + + 'MMM dd HH:mm:ss,SSS', + 'MMM dd HH:mm:ss,SSSSSS', + 'MMM dd HH:mm:ss,SSSSSSSSS', + 'MMM dd HH:mm:ss.SSS', + 'MMM dd HH:mm:ss.SSSSSS', + 'MMM dd HH:mm:ss.SSSSSSSSS', + 'MMM dd HH:mm:ss:SSS', + 'MMM dd HH:mm:ss:SSSSSS', + 'MMM dd HH:mm:ss:SSSSSSSSS', + + 'MMM dd YYYY HH:mm:ss', + 'MMM dd, YYYY h:mm:ss a', + 'TAI64N', + 'UNIX', + 'UNIX_MS', + 'YYYY-MM-dd HH:mm:ss', + + 'YYYY-MM-dd HH:mm:ss,SSS', + 'YYYY-MM-dd HH:mm:ss,SSSSSS', + 'YYYY-MM-dd HH:mm:ss,SSSSSSSSS', + 'YYYY-MM-dd HH:mm:ss.SSS', + 'YYYY-MM-dd HH:mm:ss.SSSSSS', + 'YYYY-MM-dd HH:mm:ss.SSSSSSSSS', + 'YYYY-MM-dd HH:mm:ss:SSS', + 'YYYY-MM-dd HH:mm:ss:SSSSSS', + 'YYYY-MM-dd HH:mm:ss:SSSSSSSSS', + + 'YYYY-MM-dd HH:mm:ss,SSS Z', + 'YYYY-MM-dd HH:mm:ss,SSSSSS Z', + 'YYYY-MM-dd HH:mm:ss,SSSSSSSSS Z', + 'YYYY-MM-dd HH:mm:ss.SSS Z', + 'YYYY-MM-dd HH:mm:ss.SSSSSS Z', + 'YYYY-MM-dd HH:mm:ss.SSSSSSSSS Z', + 'YYYY-MM-dd HH:mm:ss:SSS Z', + 'YYYY-MM-dd HH:mm:ss:SSSSSS Z', + 'YYYY-MM-dd HH:mm:ss:SSSSSSSSS Z', + + 'YYYY-MM-dd HH:mm:ss,SSSZ', + 'YYYY-MM-dd HH:mm:ss,SSSSSSZ', + 'YYYY-MM-dd HH:mm:ss,SSSSSSSSSZ', + 'YYYY-MM-dd HH:mm:ss.SSSZ', + 'YYYY-MM-dd HH:mm:ss.SSSSSSZ', + 'YYYY-MM-dd HH:mm:ss.SSSSSSSSSZ', + 'YYYY-MM-dd HH:mm:ss:SSSZ', + 'YYYY-MM-dd HH:mm:ss:SSSSSSZ', + 'YYYY-MM-dd HH:mm:ss:SSSSSSSSSZ', + + 'YYYY-MM-dd HH:mm:ss,SSSZZ', + 'YYYY-MM-dd HH:mm:ss,SSSSSSZZ', + 'YYYY-MM-dd HH:mm:ss,SSSSSSSSSZZ', + 'YYYY-MM-dd HH:mm:ss.SSSZZ', + 'YYYY-MM-dd HH:mm:ss.SSSSSSZZ', + 'YYYY-MM-dd HH:mm:ss.SSSSSSSSSZZ', + 'YYYY-MM-dd HH:mm:ss:SSSZZ', + 'YYYY-MM-dd HH:mm:ss:SSSSSSZZ', + 'YYYY-MM-dd HH:mm:ss:SSSSSSSSSZZ', + + 'YYYY-MM-dd HH:mm:ssZ', + 'YYYY-MM-dd HH:mm:ssZZ', + 'YYYYMMddHHmmss', +]; + +export const DELIMITER_OPTIONS = [ + 'comma', + 'tab', + 'semicolon', + 'pipe', + 'space', + 'other', +]; + +export const QUOTE_OPTIONS = [ + '\'', + '"', + '`', +]; + +export const CHARSET_OPTIONS = [ + 'IBM00858', + 'IBM437', + 'IBM775', + 'IBM850', + 'IBM852', + 'IBM855', + 'IBM857', + 'IBM862', + 'IBM866', + 'ISO-8859-1', + 'ISO-8859-2', + 'ISO-8859-4', + 'ISO-8859-5', + 'ISO-8859-7', + 'ISO-8859-9', + 'ISO-8859-13', + 'ISO-8859-15', + 'KOI8-R', + 'KOI8-U', + 'US-ASCII', + 'UTF-8', + 'UTF-16', + 'UTF-16BE', + 'UTF-16LE', + 'UTF-32', + 'UTF-32BE', + 'UTF-32LE', + 'x-UTF-32BE-BOM', + 'x-UTF-32LE-BOM', + 'windows-1250', + 'windows-1251', + 'windows-1252', + 'windows-1253', + 'windows-1254', + 'windows-1257', + 'Not available', + 'x-IBM737', + 'x-IBM874', + 'x-UTF-16LE-BOM', + 'Big5', + 'Big5-HKSCS', + 'EUC-JP', + 'EUC-KR', + 'GB18030', + 'GB2312', + 'GBK', + 'IBM-Thai', + 'IBM01140', + 'IBM01141', + 'IBM01142', + 'IBM01143', + 'IBM01144', + 'IBM01145', + 'IBM01146', + 'IBM01147', + 'IBM01148', + 'IBM01149', + 'IBM037', + 'IBM1026', + 'IBM1047', + 'IBM273', + 'IBM277', + 'IBM278', + 'IBM280', + 'IBM284', + 'IBM285', + 'IBM297', + 'IBM420', + 'IBM424', + 'IBM500', + 'IBM860', + 'IBM861', + 'IBM863', + 'IBM864', + 'IBM865', + 'IBM868', + 'IBM869', + 'IBM870', + 'IBM871', + 'IBM918', + 'ISO-2022-CN', + 'ISO-2022-JP', + 'ISO-2022-KR', + 'ISO-8859-3', + 'ISO-8859-6', + 'ISO-8859-8', + 'JIS_X0201', + 'JIS_X0212-1990', + 'Shift_JIS', + 'TIS-620', + 'windows-1255', + 'windows-1256', + 'windows-1258', + 'windows-31j', + 'x-Big5-Solaris', + 'x-euc-jp-linux', + 'x-EUC-TW', + 'x-eucJP-Open', + 'x-IBM1006', + 'x-IBM1025', + 'x-IBM1046', + 'x-IBM1097', + 'x-IBM1098', + 'x-IBM1112', + 'x-IBM1122', + 'x-IBM1123', + 'x-IBM1124', + 'x-IBM1381', + 'x-IBM1383', + 'x-IBM33722', + 'x-IBM834', + 'x-IBM856', + 'x-IBM875', + 'x-IBM921', + 'x-IBM922', + 'x-IBM930', + 'x-IBM933', + 'x-IBM935', + 'x-IBM937', + 'x-IBM939', + 'x-IBM942', + 'x-IBM942C', + 'x-IBM943', + 'x-IBM943C', + 'x-IBM948', + 'x-IBM949', + 'x-IBM949C', + 'x-IBM950', + 'x-IBM964', + 'x-IBM970', + 'x-ISCII91', + 'x-ISO2022-CN-CNS', + 'x-ISO2022-CN-GB', + 'x-iso-8859-11', + 'x-JIS0208', + 'x-JISAutoDetect', + 'x-Johab', + 'x-MacArabic', + 'x-MacCentralEurope', + 'x-MacCroatian', + 'x-MacCyrillic', + 'x-MacDingbat', + 'x-MacGreek', + 'x-MacHebrew', + 'x-MacIceland', + 'x-MacRoman', + 'x-MacRomania', + 'x-MacSymbol', + 'x-MacThai', + 'x-MacTurkish', + 'x-MacUkraine', + 'x-MS950-HKSCS', + 'x-mswin-936', + 'x-PCK', + 'x-SJIS_0213', + 'x-windows-50220', + 'x-windows-50221', + 'x-windows-874', + 'x-windows-949', + 'x-windows-950', + 'x-windows-iso2022jp', +]; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/options.js b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/options.js new file mode 100644 index 000000000000..a67197c7e96a --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/options/options.js @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import React from 'react'; +import { + FORMAT_OPTIONS, + TIMESTAMP_OPTIONS, + DELIMITER_OPTIONS, + QUOTE_OPTIONS, + CHARSET_OPTIONS, +} from './option_lists'; + +function getOptions(list) { + return list.map(o => ( + { + value: o, + inputDisplay: ({o}), + } + )); +} + +export function getFormatOptions() { + return getOptions(FORMAT_OPTIONS); +} + +export function getTimestampFormatOptions() { + return getOptions(TIMESTAMP_OPTIONS); +} + +export function getDelimiterOptions() { + return getOptions(DELIMITER_OPTIONS); +} + +export function getQuoteOptions() { + return getOptions(QUOTE_OPTIONS); +} + +export function getCharsetOptions() { + return getOptions(CHARSET_OPTIONS); +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/overrides.js b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/overrides.js new file mode 100644 index 000000000000..fba9496554b2 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/overrides.js @@ -0,0 +1,378 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import React, { + Component, +} from 'react'; + +import { + EuiForm, + EuiFormRow, + EuiFieldText, + EuiSuperSelect, + EuiCheckbox, + EuiSpacer, + EuiTitle, + EuiTextArea, +} from '@elastic/eui'; + +import { + getFormatOptions, + getTimestampFormatOptions, + getDelimiterOptions, + getQuoteOptions, + // getCharsetOptions, +} from './options'; + +const formatOptions = getFormatOptions(); +const timestampFormatOptions = getTimestampFormatOptions(); +const delimiterOptions = getDelimiterOptions(); +const quoteOptions = getQuoteOptions(); +// const charsetOptions = getCharsetOptions(); + +export class Overrides extends Component { + constructor(props) { + super(props); + + this.state = {}; + } + + static getDerivedStateFromProps(props) { + const { originalSettings } = props; + + const { + charset, + format, + hasHeaderRow, + columnNames, + delimiter, + quote, + shouldTrimFields, + grokPattern, + timestampField, + timestampFormat, + } = props.overrides; + + const { + delimiter: d, + customDelimiter: customD + } = convertDelimiter((delimiter === undefined) ? originalSettings.delimiter : delimiter); + + const { + newColumnNames, + originalColumnNames + } = getColumnNames(columnNames, originalSettings); + + return { + charset: (charset === undefined) ? originalSettings.charset : charset, + format: (format === undefined) ? originalSettings.format : format, + hasHeaderRow: (hasHeaderRow === undefined) ? originalSettings.hasHeaderRow : hasHeaderRow, + columnNames: newColumnNames, + originalColumnNames, + delimiter: d, + customDelimiter: (customD === undefined) ? '' : customD, + quote: (quote === undefined) ? originalSettings.quote : quote, + shouldTrimFields: (shouldTrimFields === undefined) ? originalSettings.shouldTrimFields : shouldTrimFields, + grokPattern: (grokPattern === undefined) ? originalSettings.grokPattern : grokPattern, + timestampFormat: (timestampFormat === undefined) ? originalSettings.timestampFormat : timestampFormat, + timestampField: (timestampField === undefined) ? originalSettings.timestampField : timestampField, + }; + } + + componentDidMount() { + if (typeof this.props.setApplyOverrides === 'function') { + this.props.setApplyOverrides(this.applyOverrides); + } + } + + componentWillUnmount() { + if (typeof this.props.unsetApplyOverrides === 'function') { + this.props.unsetApplyOverrides(); + } + } + + applyOverrides = () => { + const overrides = { ...this.state }; + overrides.delimiter = convertDelimiterBack(overrides); + delete overrides.customDelimiter; + delete overrides.originalColumnNames; + + this.props.setOverrides(overrides); + } + + onFormatChange = (format) => { + this.setState({ format }); + } + + onTimestampFormatChange = (timestampFormat) => { + this.setState({ timestampFormat }); + } + + onTimestampFieldChange = (timestampField) => { + this.setState({ timestampField }); + } + + onDelimiterChange = (delimiter) => { + this.setState({ delimiter }); + } + + onCustomDelimiterChange = (e) => { + this.setState({ customDelimiter: e.target.value }); + } + + onQuoteChange = (quote) => { + this.setState({ quote }); + } + + onHasHeaderRowChange = (e) => { + this.setState({ hasHeaderRow: e.target.checked }); + } + + onShouldTrimFieldsChange = (e) => { + this.setState({ shouldTrimFields: e.target.checked }); + } + + onCharsetChange = (charset) => { + this.setState({ charset }); + } + + onColumnNameChange = (e, i) => { + const columnNames = this.state.columnNames; + columnNames[i] = e.target.value; + this.setState({ columnNames }); + } + + grokPatternChange = (e) => { + this.setState({ grokPattern: e.target.value }); + } + + + render() { + const { fields } = this.props; + const { + timestampFormat, + timestampField, + format, + delimiter, + customDelimiter, + quote, + hasHeaderRow, + shouldTrimFields, + // charset, + columnNames, + originalColumnNames, + grokPattern, + } = this.state; + + const fieldOptions = fields.map(f => ({ value: f, inputDisplay: f })); + + return ( + + + + + + { + (this.state.format === 'delimited') && + + + + + { + (delimiter === 'other') && + + + + } + + + + + + + + + + + + + + + + } + { + (this.state.format === 'semi_structured_text') && + + + + + + } + + + + + + + + + {/* + + */} + { + (this.state.format === 'delimited') && + + + + +

Edit field names

+
+ + { + originalColumnNames.map((f, i) => ( + + this.onColumnNameChange(e, i)} + /> + + )) + } +
+ } + +
+ + ); + } +} + +// Some delimiter characters cannot be used as items in select list. +// so show a textual description of the character instead. +function convertDelimiter(d) { + switch (d) { + case ',': + return { + delimiter: 'comma', + }; + case '\t': + return { + delimiter: 'tab', + }; + case ';': + return { + delimiter: 'semicolon', + }; + case '|': + return { + delimiter: 'pipe', + }; + case ' ': + return { + delimiter: 'space', + }; + + default: + return { + delimiter: 'other', + customDelimiter: d, + }; + } +} + +// Convert the delimiter textual descriptions back to their real characters. +function convertDelimiterBack({ delimiter, customDelimiter }) { + switch (delimiter) { + case 'comma': + return ','; + case 'tab': + return '\t'; + case 'semicolon': + return ';'; + case 'pipe': + return '|'; + case 'space': + return ' '; + case 'other': + return customDelimiter; + + default: + return undefined; + } +} + +function getColumnNames(columnNames, originalSettings) { + const newColumnNames = (columnNames === undefined && originalSettings.columnNames !== undefined) ? + [...originalSettings.columnNames] : columnNames; + + const originalColumnNames = (newColumnNames !== undefined) ? [...newColumnNames] : []; + + return { + newColumnNames, + originalColumnNames, + }; +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/overrides.test.js b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/overrides.test.js new file mode 100644 index 000000000000..fcf092929bd6 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/edit_flyout/overrides.test.js @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { shallow } from 'enzyme'; +import React from 'react'; + +import { Overrides } from './overrides'; + +describe('Overrides', () => { + + test('render overrides', () => { + const props = { + setOverrides: () => {}, + overrides: {}, + defaultSettings: {}, + setApplyOverrides: () => {}, + fields: [], + }; + + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_field_stats_card.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_field_stats_card.scss new file mode 100644 index 000000000000..66d1ec2c16c1 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_field_stats_card.scss @@ -0,0 +1,13 @@ +.card-container { + display: inline-grid; + padding: 0px 10px 10px 0px; +} + +.ml-field-data-card { + height: 408px; + + .card-contents { + height: 378px; + line-height: 21px; + } +} diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_fields_stats.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_fields_stats.scss new file mode 100644 index 000000000000..ac04fad0ef3e --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_fields_stats.scss @@ -0,0 +1,8 @@ +.fields-stats { + padding: 10px; +} +.field { + margin-bottom: 10px; +} + + diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_index.scss b/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_index.scss new file mode 100644 index 000000000000..fe6a232f016a --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/_index.scss @@ -0,0 +1,2 @@ +@import 'fields_stats'; +@import 'field_stats_card'; diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/field_stats_card.js b/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/field_stats_card.js new file mode 100644 index 000000000000..9c7300363dc4 --- /dev/null +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/fields_stats/field_stats_card.js @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import React from 'react'; + +import { + EuiSpacer, + +} from '@elastic/eui'; + +import { FieldTypeIcon } from '../../../components/field_type_icon'; + +export function FieldStatsCard({ field }) { + + const percent = Math.round(field.percent * 100) / 100; + + let type = field.type; + if (type === 'double' || type === 'long') { + type = 'number'; + } + + return ( + +
+
+
+ +
{field.name}
+
+ +
+
+
+