mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* fix merge conflicts in translation file * Get changes missed in conflict resolution * run i18n check to fix internationalization failing check
This commit is contained in:
parent
3452ba97ea
commit
4ef77f554d
12 changed files with 0 additions and 1430 deletions
26
NOTICE.txt
26
NOTICE.txt
|
@ -4,32 +4,6 @@ Copyright 2012-2019 Elasticsearch B.V.
|
|||
---
|
||||
This product has relied on ASTExplorer that is licensed under MIT.
|
||||
|
||||
---
|
||||
This product includes code that was extracted from angular-ui-bootstrap@0.13.1
|
||||
which is available under an "MIT" license
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2012-2016 the AngularUI Team, http://angular-ui.github.io/bootstrap/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---
|
||||
This product uses Noto fonts that are licensed under the SIL Open
|
||||
Font License, Version 1.1.
|
||||
|
|
|
@ -16,7 +16,6 @@ import 'ui/autoload/all';
|
|||
import 'ui/kbn_top_nav';
|
||||
|
||||
import 'plugins/ml/access_denied';
|
||||
import 'plugins/ml/lib/angular_bootstrap_patch';
|
||||
import 'plugins/ml/jobs';
|
||||
import 'plugins/ml/services/calendar_service';
|
||||
import 'plugins/ml/components/messagebar';
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
@import 'job_select_list';
|
|
@ -1,343 +0,0 @@
|
|||
// SASSTODO: This file needs a rewrite. Needs to be variablized and have actual calculations rather than pixels.
|
||||
// Lots of custom colors in here need to be replaced.
|
||||
|
||||
// SASSTODO: EXTREMELY DANGEROUS OVERWRITE
|
||||
// Little worried about touching it now
|
||||
navbar {
|
||||
padding: 10px;
|
||||
background-color: #EFF0F1;
|
||||
}
|
||||
|
||||
// SASSTODO: EXTREMELY DANGEROUS OVERWRITE
|
||||
// Little worried about touching it now
|
||||
navbar button[disabled] {
|
||||
color: #FFF;
|
||||
background-color: #0079a5;
|
||||
}
|
||||
|
||||
.ml-job-selector {
|
||||
margin: -9px -14px;
|
||||
|
||||
.header {
|
||||
padding: 9px 14px;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 9px;
|
||||
border-top: 1px solid #eee;
|
||||
|
||||
.kuiButton--primary:focus {
|
||||
background-color: #0079a5;
|
||||
}
|
||||
}
|
||||
|
||||
.select-all-controls {
|
||||
margin-bottom: 0px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #eee;
|
||||
& > div {
|
||||
width: 280px;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
// SASSTODO: Replace with proper selector
|
||||
hr {
|
||||
margin: 2px 0px;
|
||||
}
|
||||
|
||||
// SASSTODO: Replace with proper selector
|
||||
label {
|
||||
margin-bottom: 0px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
// SASSTODO: Replace with proper selector
|
||||
input {
|
||||
margin-right: 4px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.apply-time-range {
|
||||
margin-top: 16px;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 12px;
|
||||
|
||||
// SASSTODO: Replace with proper selector
|
||||
input {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
background-color: #F5F7FA;
|
||||
|
||||
.list-section-title {
|
||||
margin: 14px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.list-section {
|
||||
border: 1px solid #e0e0e0;
|
||||
background-color: #ffffff;
|
||||
margin: 14px;
|
||||
margin-top: 5px;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.list-section-single {
|
||||
margin: -1px;
|
||||
border-radius: 0px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
.expand-group-button {
|
||||
padding-right: 2px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.job-row {
|
||||
white-space: nowrap;
|
||||
vertical-align: top;
|
||||
|
||||
.use-time-range {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.job-row-inner {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
color: #444444;
|
||||
white-space: nowrap;
|
||||
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
line-height: 20px;
|
||||
|
||||
// SASSTODO: Replace with proper selector
|
||||
& > label > div {
|
||||
width: 280px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: 30px;
|
||||
padding-top: 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 4px;
|
||||
|
||||
.disabled-job {
|
||||
color: #cccccc;
|
||||
}
|
||||
}
|
||||
|
||||
// SASSTODO: Replace with proper selector
|
||||
& > label > div:nth-child(2) {
|
||||
width: 310px;
|
||||
border-left: 1px solid #eee;
|
||||
border-right: 1px solid #eee;
|
||||
padding-left: 4px;
|
||||
padding-top: 6px;
|
||||
margin-left: -3px;
|
||||
|
||||
.disabled-job {
|
||||
background-color: #cccccc;
|
||||
}
|
||||
}
|
||||
|
||||
.job-in-group {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.gant-bar {
|
||||
background-color: #79adda;
|
||||
height: 14px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
// SASSTODO: This needs to be rebuilt
|
||||
.gant-bar-running {
|
||||
background-image:-webkit-gradient(linear,
|
||||
0 100%, 100% 0,
|
||||
color-stop(0.25, rgba(255, 255, 255, 0.15)),
|
||||
color-stop(0.25, transparent),
|
||||
color-stop(0.5, transparent),
|
||||
color-stop(0.5, rgba(255, 255, 255, 0.15)),
|
||||
color-stop(0.75, rgba(255, 255, 255, 0.15)),
|
||||
color-stop(0.75, transparent),
|
||||
to(transparent));
|
||||
background-image:-webkit-linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%, transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
background-image:-moz-linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
background-image:-o-linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
background-image:linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
-webkit-background-size:40px 40px;
|
||||
-moz-background-size:40px 40px;
|
||||
-o-background-size:40px 40px;
|
||||
background-size:40px 40px;
|
||||
|
||||
-webkit-animation:progress-bar-stripes 2s linear infinite;
|
||||
-moz-animation:progress-bar-stripes 2s linear infinite;
|
||||
-ms-animation:progress-bar-stripes 2s linear infinite;
|
||||
-o-animation:progress-bar-stripes 2s linear infinite;
|
||||
animation:progress-bar-stripes 2s linear infinite;
|
||||
}
|
||||
|
||||
.gant-back-edge {
|
||||
height: 18px;
|
||||
border-left: 1px solid #d6d6d6;
|
||||
border-right: 1px solid #d6d6d6;
|
||||
margin-bottom: -16px;
|
||||
padding-top: 9px;
|
||||
|
||||
// SASSTODO: Needs a poper selector
|
||||
div {
|
||||
height: 1px;
|
||||
border-top: 1px dashed #d6d6d6;
|
||||
}
|
||||
}
|
||||
}
|
||||
.job-row:hover {
|
||||
.job-row-inner {
|
||||
|
||||
// SASSTODO: Needs a poper selector
|
||||
& > label > div {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ml-select-list-on {
|
||||
color: #ffffff;
|
||||
background-color: #000099;
|
||||
}
|
||||
|
||||
.ml-select-list-on {
|
||||
color: #ffffff;
|
||||
background-color: #000099;
|
||||
}
|
||||
|
||||
.ml-job-select-btn-container {
|
||||
.popover {
|
||||
min-width: 640px;
|
||||
max-width: 660px !important;
|
||||
left: 9px !important;
|
||||
.arrow {
|
||||
left: 150px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ml-job-select-btn-label {
|
||||
background-color:#9c9c9c;
|
||||
color: #ffffff;
|
||||
display:inline-block;
|
||||
padding:0px 10px;
|
||||
border: 0px solid #ecf0f1;
|
||||
border-top-style: solid;
|
||||
border-right-style: none;
|
||||
border-bottom-style: solid;
|
||||
border-left-style: solid;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
height: 34px;
|
||||
line-height:33px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ml-job-select-btn {
|
||||
width: 300px;
|
||||
height: 34px;
|
||||
text-align: left;
|
||||
color: #444444;
|
||||
background-color: white;
|
||||
border: 1px solid #ffffff;
|
||||
border-left-width: 0px;
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
padding: 6px 16px;
|
||||
padding-left: 10px;
|
||||
transition: border-color ease-in-out .15s;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
|
||||
.ml-job-select-btn-text {
|
||||
float: left;
|
||||
width: 260px;
|
||||
overflow-x: hidden;
|
||||
display: inline-flex;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.caret {
|
||||
float: right;
|
||||
margin-top: 6px;
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.ml-job-select-btn:hover, .ml-job-select-btn:focus, .ml-job-select-btn:active {
|
||||
color: #444444;
|
||||
background-color: #ffffff;
|
||||
border-color: #0079a5;
|
||||
border-left-width: 1px;
|
||||
padding-left: 9px;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.ml-job-select-btn:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* 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 './job_select_list_directive';
|
||||
import './job_select_button_directive.js';
|
||||
import './job_select_service.js';
|
|
@ -1,27 +0,0 @@
|
|||
<div style='display: flex;' class="ml-job-select-btn-container">
|
||||
<div
|
||||
class="ml-job-select-btn-label"
|
||||
i18n-id="xpack.ml.jobSelectButton.jobTitle"
|
||||
i18n-default-message="Job"
|
||||
></div>
|
||||
<div style="flex:0 0 auto;">
|
||||
<div popover-placement="bottom"
|
||||
popover-placement="bottom"
|
||||
popover-html-unsafe="{{unsafeHtml}}"
|
||||
popover-append-to-body="false"
|
||||
popover-trigger="click"
|
||||
class="ml-job-select-btn"
|
||||
tooltip="{{ 'xpack.ml.jobSelectButton.jobSelectionMenuButtonTooltip' | i18n: {
|
||||
defaultMessage: '{description} selected',
|
||||
values: {
|
||||
description: description.txt,
|
||||
}
|
||||
} }}"
|
||||
aria-label="{{:: 'xpack.ml.jobSelectButton.jobSelectionMenuButtonAriaLabel' | i18n: { defaultMessage: 'Job selection menu' } }}"
|
||||
ng-click='createMenu()'
|
||||
kbn-accessible-click>
|
||||
<span class="ml-job-select-btn-text">{{description.txt}}</span>
|
||||
<span class="caret"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ml-job-select-list directive for rendering a multi-select control for selecting
|
||||
* one or more jobs from the list of configured jobs.
|
||||
*/
|
||||
|
||||
import template from './job_select_button.html';
|
||||
|
||||
import 'ui/accessibility/kbn_accessible_click';
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
import { JobSelectServiceProvider } from 'plugins/ml/components/job_select_list/job_select_service';
|
||||
|
||||
module.directive('jobSelectButton', function (Private) {
|
||||
|
||||
const mlJobSelectService = Private(JobSelectServiceProvider);
|
||||
|
||||
function link(scope) {
|
||||
scope.selectJobBtnJobIdLabel = '';
|
||||
scope.unsafeHtml = '';
|
||||
scope.description = scope.singleSelection ? mlJobSelectService.singleJobDescription : mlJobSelectService.description;
|
||||
|
||||
scope.createMenu = function () {
|
||||
let txt = '<ml-job-select-list ';
|
||||
if (scope.timeseriesonly) {
|
||||
txt += 'timeseriesonly="true" ';
|
||||
}
|
||||
if (scope.singleSelection) {
|
||||
txt += 'single-selection="true" ';
|
||||
}
|
||||
txt += '></ml-job-select-list>';
|
||||
scope.unsafeHtml = txt;
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
scope: {
|
||||
timeseriesonly: '=',
|
||||
singleSelection: '='
|
||||
},
|
||||
link,
|
||||
replace: true,
|
||||
template
|
||||
};
|
||||
});
|
|
@ -1,147 +0,0 @@
|
|||
<div class='ml-job-selector'>
|
||||
<div
|
||||
class="header"
|
||||
i18n-id="xpack.ml.jobSelectList.jobSelectionTitle"
|
||||
i18n-default-message="Job Selection"
|
||||
></div>
|
||||
|
||||
<ml-loading-indicator
|
||||
label="{{ ::'xpack.ml.jobSelectList.loadingJobsLabel' | i18n: { defaultMessage: 'Loading jobs'} }}"
|
||||
is-loading="noJobsCreated===undefined"
|
||||
/>
|
||||
<div class="select-list" ng-show='noJobsCreated!==undefined'>
|
||||
<div ng-if="groups.length && singleSelection !== true">
|
||||
<div class="list-section-title">
|
||||
<input
|
||||
ng-if="singleSelection !== true"
|
||||
type="checkbox"
|
||||
ng-checked="allGroupsSelected"
|
||||
aria-label="{{ 'xpack.ml.jobSelectList.selectAllGroupsCheckboxAriaLabel' | i18n: {
|
||||
defaultMessage: 'Select all groups checkbox. Total count {selectedGroupsLength}.',
|
||||
values: { selectedGroupsLength: selected.groups.length }
|
||||
} }}"
|
||||
ng-click="toggleAllGroupsSelection()" />
|
||||
{{ ::'xpack.ml.jobSelectList.groupsTitle' | i18n: { defaultMessage: 'Groups' } }}
|
||||
</div>
|
||||
<div class="list-section">
|
||||
<div class='group' ng-repeat="group in selected.groups">
|
||||
<div class='group-title job-row'>
|
||||
<div class="job-row-inner">
|
||||
<label class="kuiFormLabel">
|
||||
<div >
|
||||
<input
|
||||
aria-label="{{ 'xpack.ml.jobSelectList.groupIdAriaLabel' | i18n: {
|
||||
defaultMessage: 'Group ID {groupId}',
|
||||
values: { groupId: group.id }
|
||||
} }}"
|
||||
ng-if="singleSelection !== true && group.selectable === true" type="checkbox"
|
||||
ng-model="group.selected"
|
||||
ng-click="toggleGroupSelection()" />
|
||||
{{group.id}}
|
||||
</div>
|
||||
<div ng-if="group.selectable !== false">
|
||||
<div class='gant-back-edge'>
|
||||
<div></div>
|
||||
</div>
|
||||
<div
|
||||
class='gant-bar'
|
||||
ng-class="{'disabled-job': job.disabled, 'gant-bar-running': job.running}"
|
||||
tooltip='{{group.timeRange.label}}'
|
||||
tooltip-placement="bottom"
|
||||
tooltip-append-to-body="true"
|
||||
ng-attr-style='margin-left:{{group.timeRange.fromPx}}px; width: {{group.timeRange.widthPx}}px;'>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-section-title" ng-if="singleSelection !== true">
|
||||
<input type="checkbox"
|
||||
ng-checked="allJobsSelected"
|
||||
aria-label="{{ 'xpack.ml.jobSelectList.selectAllJobsCheckboxAriaLabel' | i18n: {
|
||||
defaultMessage: 'Select all jobs checkbox. Total count {selectedJobsLength}.',
|
||||
values: { selectedJobsLength: selected.jobs.length }
|
||||
} }}"
|
||||
ng-click="toggleAllJobsSelection()" />
|
||||
{{ ::'xpack.ml.jobSelectList.jobsTitle' | i18n: { defaultMessage: 'Jobs' } }}
|
||||
</div>
|
||||
<div class="list-section" ng-class="{'list-section-single': singleSelection}">
|
||||
<div class='job-row' ng-repeat="job in selected.jobs">
|
||||
<div class='job-row-inner'>
|
||||
<label class="kuiFormLabel">
|
||||
<div>
|
||||
<span>
|
||||
<input ng-if="singleSelection !== true" type="checkbox"
|
||||
ng-model="job.selected"
|
||||
ng-disabled='job.disabled'
|
||||
ng-click="toggleSelection()" />
|
||||
<input ng-if="singleSelection === true" type="radio"
|
||||
name="job-group"
|
||||
value="{{job.id}}"
|
||||
ng-model="$parent.$parent.selectedJobRadio"
|
||||
ng-disabled='job.disabled' />
|
||||
<span
|
||||
ng-class="{'disabled-job': job.disabled}"
|
||||
aria-label="{{ 'xpack.ml.jobSelectList.jobIdAriaLabel' | i18n: {
|
||||
defaultMessage: 'Job ID {jobId}',
|
||||
values: { jobId: job.id }
|
||||
} }}"
|
||||
>{{job.id}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class='gant-back-edge' >
|
||||
<div ></div>
|
||||
</div>
|
||||
<div
|
||||
class='gant-bar'
|
||||
aria-label="{{ 'xpack.ml.jobSelectList.timeRangeAriaLabel' | i18n: {
|
||||
defaultMessage: 'time range {jobTimeRangeLabel}',
|
||||
values: { jobTimeRangeLabel: job.timeRange.label }
|
||||
} }}"
|
||||
ng-class="{'disabled-job': job.disabled, 'gant-bar-running': job.running}"
|
||||
tooltip='{{job.timeRange.label}}'
|
||||
tooltip-placement="bottom"
|
||||
tooltip-append-to-body="true"
|
||||
ng-attr-style='margin-left:{{job.timeRange.fromPx}}px; width: {{job.timeRange.widthPx}}px;'>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='footer'>
|
||||
<button
|
||||
ng-click="apply()"
|
||||
class="kuiButton kuiButton--primary"
|
||||
aria-label="{{ ::'xpack.ml.jobSelectList.applyButtonAriaLabel' | i18n: { defaultMessage: 'Apply'} }}"
|
||||
ng-disabled="selectedCount===0"
|
||||
i18n-id="xpack.ml.jobSelectList.applyButtonLabel"
|
||||
i18n-default-message="Apply"
|
||||
></button>
|
||||
<button
|
||||
ng-click="closePopover()"
|
||||
class="kuiButton kuiButton--primary"
|
||||
aria-label="{{ ::'xpack.ml.jobSelectList.cancelButtonAriaLabel' | i18n: { defaultMessage: 'Cancel'} }}"
|
||||
i18n-id="xpack.ml.jobSelectList.cancelButtonLabel"
|
||||
i18n-default-message="Cancel"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<div class='apply-time-range'>
|
||||
<label class="kuiFormLabel">
|
||||
<input type="checkbox"
|
||||
ng-model="applyTimeRange"/>
|
||||
{{ ::'xpack.ml.jobSelectList.applyTimeRangeLabel' | i18n: { defaultMessage: 'Also apply time range' } }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -1,407 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ml-job-select-list directive for rendering a multi-select control for selecting
|
||||
* one or more jobs from the list of configured jobs.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import moment from 'moment';
|
||||
import d3 from 'd3';
|
||||
|
||||
import template from './job_select_list.html';
|
||||
import { isTimeSeriesViewJob } from 'plugins/ml/../common/util/job_utils';
|
||||
import { mlJobService } from 'plugins/ml/services/job_service';
|
||||
import { JobSelectServiceProvider } from 'plugins/ml/components/job_select_list/job_select_service';
|
||||
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.directive('mlJobSelectList', function (Private) {
|
||||
return {
|
||||
restrict: 'AE',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
template,
|
||||
controller: function ($scope, i18n) {
|
||||
const mlJobSelectService = Private(JobSelectServiceProvider);
|
||||
$scope.jobs = [];
|
||||
$scope.groups = [];
|
||||
$scope.homelessJobs = [];
|
||||
$scope.singleSelection = false;
|
||||
$scope.timeSeriesOnly = false;
|
||||
$scope.noJobsCreated = undefined;
|
||||
$scope.applyTimeRange = mlJobSelectService.jobSelectListState.applyTimeRange;
|
||||
$scope.urlSelectedIds = {};
|
||||
$scope.selected = {};
|
||||
$scope.allGroupsSelected = false;
|
||||
$scope.allJobsSelected = false;
|
||||
$scope.selectedJobRadio = '';
|
||||
$scope.selectedCount = 0;
|
||||
|
||||
mlJobService.loadJobs()
|
||||
.then((resp) => {
|
||||
if (resp.jobs.length > 0) {
|
||||
$scope.noJobsCreated = false;
|
||||
const jobs = [];
|
||||
resp.jobs.forEach(job => {
|
||||
if (job.groups && job.groups.length) {
|
||||
job.groups.forEach(group => {
|
||||
jobs.push(createJob(`${group}.${job.job_id}`, group, job));
|
||||
});
|
||||
} else {
|
||||
jobs.push(createJob(job.job_id, null, job));
|
||||
}
|
||||
});
|
||||
normalizeTimes(jobs);
|
||||
$scope.jobs = jobs;
|
||||
const { groups, homeless } = createGroups($scope.jobs);
|
||||
$scope.groups = groups;
|
||||
$scope.homelessJobs = homeless;
|
||||
$scope.selected = {
|
||||
groups: [],
|
||||
jobs: []
|
||||
};
|
||||
|
||||
// count all jobs, including duplicates in groups.
|
||||
// if it's the same as the number of ids passed in, tick all jobs
|
||||
const jobCount = resp.jobs.reduce((sum, job) => (sum + ((job.groups === undefined) ? 1 : job.groups.length)), 0);
|
||||
const selectAll = (jobCount === $scope.urlSelectedIds.jobs.length);
|
||||
|
||||
// create the groups and jobs which are used in the menu
|
||||
groups.forEach(group => {
|
||||
$scope.selected.groups.push({
|
||||
id: group.id,
|
||||
selected: group.selected,
|
||||
// TODO: is the selectable property of a group still needed?
|
||||
selectable: group.selectable,
|
||||
timeRange: group.timeRange,
|
||||
isGroup: true,
|
||||
});
|
||||
});
|
||||
|
||||
jobs.forEach(job => {
|
||||
if ($scope.selected.jobs.find(j => j.id === job.name) === undefined) {
|
||||
$scope.selected.jobs.push({
|
||||
id: job.name,
|
||||
selected: selectAll || job.selected,
|
||||
disabled: job.disabled,
|
||||
timeRange: job.timeRange,
|
||||
running: job.running,
|
||||
isGroup: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.allJobsSelected = areAllJobsSelected();
|
||||
$scope.allGroupsSelected = areAllGroupsSelected();
|
||||
createSelectedCount();
|
||||
|
||||
// if in single selection mode, set the radio button controller ($scope.selectedJobRadio)
|
||||
// to the selected job id
|
||||
if ($scope.singleSelection === true) {
|
||||
$scope.jobs.forEach(j => {
|
||||
if (j.selected) {
|
||||
$scope.selectedJobRadio = j.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$scope.noJobsCreated = true;
|
||||
}
|
||||
$scope.$applyAsync();
|
||||
}).catch((resp) => {
|
||||
console.log('mlJobSelectList controller - error getting job info from ES:', resp);
|
||||
});
|
||||
|
||||
function createJob(jobId, groupId, job) {
|
||||
return {
|
||||
id: jobId,
|
||||
name: job.job_id,
|
||||
group: groupId,
|
||||
isGroup: false,
|
||||
selected: _.includes($scope.urlSelectedIds.jobs, job.job_id),
|
||||
disabled: !($scope.timeSeriesOnly === false || isTimeSeriesViewJob(job) === true),
|
||||
running: (job.datafeed_config && job.datafeed_config.state === 'started'),
|
||||
timeRange: {
|
||||
to: job.data_counts.latest_record_timestamp,
|
||||
from: job.data_counts.earliest_record_timestamp,
|
||||
fromPx: 0,
|
||||
toPx: 0,
|
||||
widthPx: 0,
|
||||
label: ''
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createGroups(jobsIn) {
|
||||
const jobGroups = {};
|
||||
const homeless = [];
|
||||
// first pull all of the groups out of all of the jobs
|
||||
// keeping homeless (groupless) jobs in a separate list
|
||||
jobsIn.forEach(job => {
|
||||
if (job.group !== null) {
|
||||
if (jobGroups[job.group] === undefined) {
|
||||
jobGroups[job.group] = [job];
|
||||
} else {
|
||||
jobGroups[job.group].push(job);
|
||||
}
|
||||
} else {
|
||||
homeless.push(job);
|
||||
}
|
||||
});
|
||||
|
||||
const groups = _.map(jobGroups, (jobs, id) => {
|
||||
const group = {
|
||||
id,
|
||||
selected: false,
|
||||
selectable: true,
|
||||
expanded: false,
|
||||
isGroup: true,
|
||||
jobs
|
||||
};
|
||||
// check to see whether all of the groups jobs have been selected,
|
||||
// if they have, select the group
|
||||
if ($scope.singleSelection === false) {
|
||||
group.selected = _.includes($scope.urlSelectedIds.groups, id);
|
||||
}
|
||||
|
||||
// create an over all time range for the group
|
||||
const timeRange = {
|
||||
to: null,
|
||||
toMoment: null,
|
||||
from: null,
|
||||
fromMoment: null,
|
||||
fromPx: null,
|
||||
toPx: null,
|
||||
widthPx: null,
|
||||
};
|
||||
|
||||
jobs.forEach(job => {
|
||||
job.group = group;
|
||||
|
||||
if (timeRange.to === null || job.timeRange.to > timeRange.to) {
|
||||
timeRange.to = job.timeRange.to;
|
||||
timeRange.toMoment = job.timeRange.toMoment;
|
||||
}
|
||||
if (timeRange.from === null || job.timeRange.from < timeRange.from) {
|
||||
timeRange.from = job.timeRange.from;
|
||||
timeRange.fromMoment = job.timeRange.fromMoment;
|
||||
}
|
||||
if (timeRange.toPx === null || job.timeRange.toPx > timeRange.toPx) {
|
||||
timeRange.toPx = job.timeRange.toPx;
|
||||
}
|
||||
if (timeRange.fromPx === null || job.timeRange.fromPx < timeRange.fromPx) {
|
||||
timeRange.fromPx = job.timeRange.fromPx;
|
||||
}
|
||||
});
|
||||
timeRange.widthPx = timeRange.toPx - timeRange.fromPx;
|
||||
timeRange.toMoment = moment(timeRange.to);
|
||||
timeRange.fromMoment = moment(timeRange.from);
|
||||
|
||||
const fromString = timeRange.fromMoment.format('MMM Do YYYY, HH:mm');
|
||||
const toString = timeRange.toMoment.format('MMM Do YYYY, HH:mm');
|
||||
timeRange.label = i18n('xpack.ml.jobSelectList.groupTimeRangeLabel', {
|
||||
defaultMessage: '{fromString} to {toString}',
|
||||
values: {
|
||||
fromString,
|
||||
toString,
|
||||
}
|
||||
});
|
||||
|
||||
group.timeRange = timeRange;
|
||||
return group;
|
||||
});
|
||||
|
||||
return {
|
||||
groups,
|
||||
homeless
|
||||
};
|
||||
}
|
||||
|
||||
// apply the selected jobs
|
||||
$scope.apply = function () {
|
||||
// if in single selection mode, get the job id from $scope.selectedJobRadio
|
||||
const selectedJobs = [];
|
||||
if ($scope.singleSelection) {
|
||||
selectedJobs.push(...$scope.selected.jobs.filter(j => j.id === $scope.selectedJobRadio));
|
||||
} else {
|
||||
selectedJobs.push(...$scope.selected.jobs.filter(j => j.selected));
|
||||
selectedJobs.push(...$scope.selected.groups.filter(g => g.selected));
|
||||
}
|
||||
|
||||
if (areAllJobsSelected()) {
|
||||
// if all jobs have been selected, just store '*' in the url
|
||||
mlJobSelectService.setJobIds(['*']);
|
||||
} else {
|
||||
const jobIds = selectedJobs.map(j => (j.isGroup ? `${j.id}.*` : j.id));
|
||||
mlJobSelectService.setJobIds(jobIds);
|
||||
}
|
||||
|
||||
// if the apply time range checkbox is ticked,
|
||||
// find the min and max times for all selected jobs
|
||||
// and apply them to the timefilter
|
||||
if ($scope.applyTimeRange) {
|
||||
const times = [];
|
||||
selectedJobs.forEach(job => {
|
||||
if (job.timeRange.from !== undefined) {
|
||||
times.push(job.timeRange.from);
|
||||
}
|
||||
if (job.timeRange.to !== undefined) {
|
||||
times.push(job.timeRange.to);
|
||||
}
|
||||
});
|
||||
if (times.length) {
|
||||
const min = _.min(times);
|
||||
const max = _.max(times);
|
||||
timefilter.setTime({
|
||||
from: moment(min).toISOString(),
|
||||
to: moment(max).toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
mlJobSelectService.jobSelectListState.applyTimeRange = $scope.applyTimeRange;
|
||||
$scope.closePopover();
|
||||
};
|
||||
|
||||
// ticking a job
|
||||
$scope.toggleSelection = function () {
|
||||
// check to see if all jobs are now selected
|
||||
$scope.allJobsSelected = areAllJobsSelected();
|
||||
$scope.allGroupsSelected = areAllGroupsSelected();
|
||||
createSelectedCount();
|
||||
};
|
||||
|
||||
// ticking the all jobs checkbox
|
||||
$scope.toggleAllJobsSelection = function () {
|
||||
const allJobsSelected = areAllJobsSelected();
|
||||
$scope.allJobsSelected = !allJobsSelected;
|
||||
|
||||
$scope.selected.jobs.forEach(job => {
|
||||
job.selected = $scope.allJobsSelected;
|
||||
});
|
||||
|
||||
createSelectedCount();
|
||||
};
|
||||
|
||||
// ticking a group
|
||||
$scope.toggleGroupSelection = function () {
|
||||
$scope.allGroupsSelected = areAllGroupsSelected();
|
||||
createSelectedCount();
|
||||
};
|
||||
|
||||
// ticking the all jobs checkbox
|
||||
$scope.toggleAllGroupsSelection = function () {
|
||||
const allGroupsSelected = areAllGroupsSelected();
|
||||
$scope.allGroupsSelected = !allGroupsSelected;
|
||||
|
||||
$scope.selected.groups.forEach(group => {
|
||||
group.selected = $scope.allGroupsSelected;
|
||||
});
|
||||
createSelectedCount();
|
||||
};
|
||||
|
||||
// check to see whether all jobs in the list have been selected
|
||||
function areAllJobsSelected() {
|
||||
let allSelected = true;
|
||||
$scope.selected.jobs.forEach(job => {
|
||||
if (job.selected === false) {
|
||||
allSelected = false;
|
||||
}
|
||||
});
|
||||
return allSelected;
|
||||
}
|
||||
|
||||
// check to see whether all groups in the list have been selected
|
||||
function areAllGroupsSelected() {
|
||||
let allSelected = true;
|
||||
$scope.selected.groups.forEach(group => {
|
||||
if (group.selected === false) {
|
||||
allSelected = false;
|
||||
}
|
||||
});
|
||||
return allSelected;
|
||||
}
|
||||
|
||||
function createSelectedCount() {
|
||||
$scope.selectedCount = 0;
|
||||
$scope.selected.jobs.forEach(job => {
|
||||
if (job.selected) {
|
||||
$scope.selectedCount++;
|
||||
}
|
||||
});
|
||||
$scope.selected.groups.forEach(group => {
|
||||
if (group.selected) {
|
||||
$scope.selectedCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// create the data used for the gant charts
|
||||
function normalizeTimes(jobs) {
|
||||
const min = _.min(jobs, job => +job.timeRange.from);
|
||||
const max = _.max(jobs, job => +job.timeRange.to);
|
||||
|
||||
const gantScale = d3.scale.linear().domain([min.timeRange.from, max.timeRange.to]).range([1, 299]);
|
||||
|
||||
jobs.forEach(job => {
|
||||
if (job.timeRange.to !== undefined && job.timeRange.from !== undefined) {
|
||||
job.timeRange.fromPx = gantScale(job.timeRange.from);
|
||||
job.timeRange.toPx = gantScale(job.timeRange.to);
|
||||
job.timeRange.widthPx = job.timeRange.toPx - job.timeRange.fromPx;
|
||||
|
||||
job.timeRange.toMoment = moment(job.timeRange.to);
|
||||
job.timeRange.fromMoment = moment(job.timeRange.from);
|
||||
|
||||
const fromString = job.timeRange.fromMoment.format('MMM Do YYYY, HH:mm');
|
||||
const toString = job.timeRange.toMoment.format('MMM Do YYYY, HH:mm');
|
||||
job.timeRange.label = i18n('xpack.ml.jobSelectList.jobTimeRangeLabel', {
|
||||
defaultMessage: '{fromString} to {toString}',
|
||||
values: {
|
||||
fromString,
|
||||
toString,
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$scope.useTimeRange = function (job) {
|
||||
timefilter.setTime({
|
||||
from: job.timeRange.fromMoment.toISOString(),
|
||||
to: job.timeRange.toMoment.toISOString()
|
||||
});
|
||||
};
|
||||
},
|
||||
link: function (scope, element, attrs) {
|
||||
const mlJobSelectService = Private(JobSelectServiceProvider);
|
||||
scope.timeSeriesOnly = false;
|
||||
if (attrs.timeseriesonly === 'true') {
|
||||
scope.timeSeriesOnly = true;
|
||||
}
|
||||
|
||||
if (attrs.singleSelection === 'true') {
|
||||
scope.singleSelection = true;
|
||||
}
|
||||
|
||||
// Make a copy of the list of jobs ids
|
||||
// '*' is passed to indicate 'All jobs'.
|
||||
scope.urlSelectedIds = {
|
||||
groups: [...mlJobSelectService.groupIds],
|
||||
jobs: [...mlJobSelectService.jobIdsWithGroup],
|
||||
};
|
||||
|
||||
// Giving the parent div focus fixes checkbox tick UI selection on IE.
|
||||
$('.ml-select-list', element).focus();
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,310 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Service with functions used for broadcasting job picker changes
|
||||
|
||||
import _ from 'lodash';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
||||
import { mlJobService } from 'plugins/ml/services/job_service';
|
||||
|
||||
let jobSelectService = undefined;
|
||||
|
||||
export function JobSelectServiceProvider($rootScope, globalState, i18n) {
|
||||
|
||||
function checkGlobalState() {
|
||||
if (globalState.ml === undefined) {
|
||||
globalState.ml = {};
|
||||
globalState.save();
|
||||
}
|
||||
}
|
||||
checkGlobalState();
|
||||
|
||||
function loadJobIdsFromGlobalState() {
|
||||
const jobIds = [];
|
||||
if (globalState.ml && globalState.ml.jobIds) {
|
||||
let tempJobIds = [];
|
||||
if (typeof globalState.ml.jobIds === 'string') {
|
||||
tempJobIds.push(globalState.ml.jobIds);
|
||||
} else {
|
||||
tempJobIds = globalState.ml.jobIds;
|
||||
}
|
||||
tempJobIds = tempJobIds.map(id => String(id));
|
||||
const invalidIds = getInvalidJobIds(removeGroupIds(tempJobIds));
|
||||
warnAboutInvalidJobIds(invalidIds);
|
||||
|
||||
let validIds = _.difference(tempJobIds, invalidIds);
|
||||
|
||||
// if there are no valid ids, warn and then select the first job
|
||||
if (validIds.length === 0) {
|
||||
toastNotifications.addWarning(i18n('xpack.ml.jobSelect.noJobsSelectedWarningMessage', {
|
||||
defaultMessage: 'No jobs selected, auto selecting first job',
|
||||
}));
|
||||
|
||||
if (mlJobService.jobs.length) {
|
||||
validIds = [mlJobService.jobs[0].job_id];
|
||||
}
|
||||
}
|
||||
jobIds.push(...validIds);
|
||||
|
||||
// replace the job ids in the url with the ones which are valid
|
||||
storeJobIdsInGlobalState(jobIds);
|
||||
} else {
|
||||
checkGlobalState();
|
||||
|
||||
// no jobs selected, use the first in the list
|
||||
if (mlJobService.jobs.length) {
|
||||
jobIds.push(mlJobService.jobs[0].job_id);
|
||||
}
|
||||
storeJobIdsInGlobalState(jobIds);
|
||||
}
|
||||
|
||||
return jobIds;
|
||||
}
|
||||
|
||||
function storeJobIdsInGlobalState(jobIds) {
|
||||
globalState.ml.jobIds = jobIds;
|
||||
globalState.save();
|
||||
}
|
||||
|
||||
// check that the ids read from the url exist by comparing them to the
|
||||
// jobs loaded via mlJobsService.
|
||||
function getInvalidJobIds(ids) {
|
||||
return ids.filter(id => {
|
||||
const job = _.find(mlJobService.jobs, { 'job_id': id });
|
||||
return (job === undefined && id !== '*');
|
||||
});
|
||||
}
|
||||
|
||||
function removeGroupIds(jobIds) {
|
||||
return jobIds.map(id => {
|
||||
const splitId = id.split('.');
|
||||
return (splitId.length > 1) ? splitId[1] : splitId[0];
|
||||
});
|
||||
}
|
||||
|
||||
function warnAboutInvalidJobIds(invalidIds) {
|
||||
if (invalidIds.length > 0) {
|
||||
toastNotifications.addWarning(i18n('xpack.ml.jobSelect.requestedJobsDoesNotExistWarningMessage', {
|
||||
defaultMessage: `Requested
|
||||
{invalidIdsLength, plural, one {job {invalidIds} does not exist} other {jobs {invalidIds} do not exist}}`,
|
||||
values: {
|
||||
invalidIdsLength: invalidIds.length,
|
||||
invalidIds,
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function createDescription(jobs) {
|
||||
let txt = '';
|
||||
// add up the number of jobs including duplicates if they belong to multiple groups
|
||||
const jobCount = mlJobService.jobs.length;
|
||||
|
||||
// add up how many jobs belong to groups and how many don't
|
||||
const selectedGroupJobs = [];
|
||||
const groupCounts = {};
|
||||
let groupLessJobs = 0;
|
||||
const splitJobs = jobs.map(job => {
|
||||
const obj = splitJobId(job);
|
||||
if (obj.group) {
|
||||
// keep track of selected jobs from group selection
|
||||
selectedGroupJobs.push(obj.job);
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
|
||||
splitJobs.forEach(jobObj => {
|
||||
if (jobObj.group) {
|
||||
groupCounts[jobObj.group] = (groupCounts[jobObj.group] || 0) + 1;
|
||||
} else {
|
||||
// if job has already been included via group selection don't add as groupless job
|
||||
if (selectedGroupJobs.includes(jobObj.job) === false) {
|
||||
groupLessJobs++;
|
||||
}
|
||||
}
|
||||
});
|
||||
// All jobs have been selected
|
||||
if ((_.uniq(selectedGroupJobs).length + groupLessJobs) === jobCount) {
|
||||
txt = i18n('xpack.ml.jobSelect.allJobsDescription', {
|
||||
defaultMessage: 'All jobs',
|
||||
});
|
||||
} else {
|
||||
const wholeGroups = [];
|
||||
const groups = mlJobService.getJobGroups();
|
||||
// work out how many groups have all of their jobs selected
|
||||
groups.forEach(group => {
|
||||
const groupCount = groupCounts[group.id];
|
||||
if (groupCount !== undefined && groupCount === group.jobs.length) {
|
||||
// this group has all of it's jobs selected
|
||||
wholeGroups.push(group.id);
|
||||
} else {
|
||||
if (groupCount !== undefined) {
|
||||
// this job doesn't so add it to the count of groupless jobs
|
||||
groupLessJobs += groupCount;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// show the whole groups first
|
||||
if (wholeGroups.length) {
|
||||
txt = wholeGroups[0];
|
||||
if (wholeGroups.length > 1 || groupLessJobs > 0) {
|
||||
const total = (wholeGroups.length - 1) + groupLessJobs;
|
||||
txt = i18n('xpack.ml.jobSelect.wholeGroupDescription', {
|
||||
defaultMessage: `{wholeGroup} (with {count, plural, zero {# job} one {# job} other {# jobs}}) and
|
||||
{total, plural, zero {# other} one {# other} other {# others}}`,
|
||||
values: {
|
||||
count: groupCounts[wholeGroups[0]],
|
||||
wholeGroup: wholeGroups[0],
|
||||
total,
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// otherwise just list the job ids
|
||||
txt = splitJobId(jobs[0]).job;
|
||||
if (jobs.length > 1) {
|
||||
txt = i18n('xpack.ml.jobSelect.jobDescription', {
|
||||
defaultMessage: '{jobId} and {jobsAmount, plural, zero {# other} one {# other} other {# others}}',
|
||||
values: {
|
||||
jobId: splitJobId(jobs[0]).job,
|
||||
jobsAmount: jobs.length - 1,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
// function to split the group from the job and return both or just the job
|
||||
function splitJobId(jobId) {
|
||||
let obj = {};
|
||||
const splitId = jobId.split('.');
|
||||
if (splitId.length === 2) {
|
||||
obj = { group: splitId[0], job: splitId[1] };
|
||||
} else {
|
||||
obj = { job: jobId };
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
this.splitJobId = splitJobId;
|
||||
|
||||
// expands `*` into groupId.jobId list
|
||||
// expands `groupId.*` into `groupId.jobId` list
|
||||
// returns list of expanded job ids
|
||||
function expandGroups(jobIds) {
|
||||
const newJobIds = [];
|
||||
const groups = mlJobService.getJobGroups();
|
||||
jobIds.forEach(jobId => {
|
||||
if (jobId === '*') {
|
||||
mlJobService.jobs.forEach(job => {
|
||||
if (job.groups === undefined) {
|
||||
newJobIds.push(job.job_id);
|
||||
} else {
|
||||
newJobIds.push(...job.groups.map(g => `${g}.${job.job_id}`));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const splitId = splitJobId(jobId);
|
||||
if (splitId.group !== undefined && splitId.job === '*') {
|
||||
const groupId = splitId.group;
|
||||
const group = groups.find(g => g.id === groupId);
|
||||
group.jobs.forEach(j => {
|
||||
newJobIds.push(`${groupId}.${j.job_id}`);
|
||||
});
|
||||
}
|
||||
else {
|
||||
newJobIds.push(jobId);
|
||||
}
|
||||
}
|
||||
});
|
||||
return newJobIds;
|
||||
}
|
||||
|
||||
function getGroupIds(jobIds) {
|
||||
const groupIds = [];
|
||||
jobIds.forEach(jobId => {
|
||||
const splitId = splitJobId(jobId);
|
||||
if (splitId.group !== undefined && splitId.job === '*') {
|
||||
groupIds.push(splitId.group);
|
||||
}
|
||||
});
|
||||
return groupIds;
|
||||
}
|
||||
|
||||
// takes an array of ids.
|
||||
// this could be a mixture of job ids, group ids or a *.
|
||||
// stores an expanded list of job ids (i.e. groupId.jobId) and a list of jobs ids only.
|
||||
// creates the description text used on the job picker button.
|
||||
function processIds(service, ids) {
|
||||
const expandedJobIds = expandGroups(ids);
|
||||
service.jobIdsWithGroup.length = 0;
|
||||
service.jobIdsWithGroup.push(...expandedJobIds);
|
||||
service.groupIds = getGroupIds(ids);
|
||||
service.jobIds.length = 0;
|
||||
service.jobIds.push(...removeGroupIds(expandedJobIds));
|
||||
service.description.txt = createDescription(service.jobIdsWithGroup);
|
||||
service.singleJobDescription.txt = ids[0];
|
||||
setBrowserTitle(service.description.txt);
|
||||
}
|
||||
|
||||
// display the job id in the tab title
|
||||
function setBrowserTitle(title) {
|
||||
document.title = `${title} - Kibana`;
|
||||
}
|
||||
|
||||
class JobSelectService {
|
||||
constructor() {
|
||||
this.jobIds = [];
|
||||
this.groupIds = [];
|
||||
this.description = { txt: '' };
|
||||
this.singleJobDescription = { txt: '' };
|
||||
this.jobSelectListState = {
|
||||
applyTimeRange: true
|
||||
};
|
||||
this.jobIdsWithGroup = [];
|
||||
this.splitJobId = splitJobId;
|
||||
}
|
||||
|
||||
// Broadcasts that a change has been made to the selected jobs.
|
||||
broadcastJobSelectionChange() {
|
||||
$rootScope.$broadcast('jobSelectionChange', this.getSelectedJobIds());
|
||||
}
|
||||
|
||||
// Add a listener for changes to the selected jobs.
|
||||
listenJobSelectionChange(scope, callback) {
|
||||
const handler = $rootScope.$on('jobSelectionChange', callback);
|
||||
scope.$on('$destroy', handler);
|
||||
}
|
||||
|
||||
// called externally to retrieve the selected jobs ids.
|
||||
// passing in `true` will load the jobs ids from the URL first
|
||||
getSelectedJobIds(loadFromURL) {
|
||||
if (loadFromURL) {
|
||||
processIds(this, loadJobIdsFromGlobalState());
|
||||
}
|
||||
return this.jobIds;
|
||||
}
|
||||
|
||||
// called externally to set the job ids.
|
||||
// job ids are added to the URL and an event is broadcast for anything listening.
|
||||
// e.g. the anomaly explorer or time series explorer.
|
||||
// currently only called by the jobs selection menu.
|
||||
setJobIds(jobIds) {
|
||||
processIds(this, jobIds);
|
||||
storeJobIdsInGlobalState(jobIds);
|
||||
this.broadcastJobSelectionChange();
|
||||
}
|
||||
}
|
||||
|
||||
if (jobSelectService === undefined) {
|
||||
jobSelectService = new JobSelectService();
|
||||
}
|
||||
return jobSelectService;
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/* eslint-disable @kbn/eslint/require-license-header */
|
||||
|
||||
/**
|
||||
* @notice
|
||||
*
|
||||
* This product includes code that was extracted from angular-ui-bootstrap@0.13.1
|
||||
* which is available under an "MIT" license
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2016 the AngularUI Team, http://angular-ui.github.io/bootstrap/
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// This file contains a section of code taken from angular-ui-bootstrap@0.13.1
|
||||
// and adds it to kibana's included version of 0.12.1
|
||||
// It adds the ability to allow html to be used as the content of the popover component
|
||||
|
||||
import 'ui/angular-bootstrap';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module
|
||||
.directive('popover', [ '$tooltip', function ($tooltip) {
|
||||
return $tooltip('popover', 'popover', 'click');
|
||||
}])
|
||||
.directive('popoverHtmlUnsafePopup', function ($compile) {
|
||||
let template = '<div class="popover {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">';
|
||||
template += '<div class="arrow"></div>';
|
||||
template += '<div class="popover-inner">';
|
||||
template += '<h3 class="popover-title" bind-html-unsafe="title" ng-show="title"></h3>';
|
||||
template += '<div class="popover-content" bind-html-unsafe="content" ></div>';
|
||||
template += '</div></div>';
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
scope: {
|
||||
title: '@',
|
||||
content: '@',
|
||||
placement: '@',
|
||||
animation: '&',
|
||||
isOpen: '&'
|
||||
},
|
||||
template: template,
|
||||
link: function (scope, element) {
|
||||
// The content of the popup is added as a string and does not run through angular's templating system.
|
||||
// therefore {{stuff}} substitutions don't happen.
|
||||
// we have to manually apply the template, compile it with this scope and then set it as the html
|
||||
scope.$apply();
|
||||
const cont = $compile(scope.content)(scope);
|
||||
element.find('.popover-content').html(cont);
|
||||
|
||||
// function to force the popover to close
|
||||
scope.closePopover = function () {
|
||||
scope.$parent.$parent.isOpen = false;
|
||||
scope.$parent.$parent.$applyAsync();
|
||||
element.remove();
|
||||
};
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive('popoverHtmlUnsafe', ['$tooltip', function ($tooltip) {
|
||||
return $tooltip('popoverHtmlUnsafe', 'popover', 'click');
|
||||
}]);
|
|
@ -6145,30 +6145,9 @@
|
|||
"xpack.ml.jobsBreadcrumbs.populationLabel": "填充",
|
||||
"xpack.ml.jobsBreadcrumbs.selectIndexOrSearchLabel": "选择索引或搜索",
|
||||
"xpack.ml.jobsBreadcrumbs.singleMetricLabel": "单一指标",
|
||||
"xpack.ml.jobSelect.allJobsDescription": "所有作业",
|
||||
"xpack.ml.jobSelect.jobDescription": "{jobId} 和 {jobsAmount, plural, zero {# 个其他作业} one {# 个其他作业} other {# 个其他作业}}",
|
||||
"xpack.ml.jobSelect.noJobsSelectedWarningMessage": "未选择作业,将自动选择第一个作业",
|
||||
"xpack.ml.jobSelect.requestedJobsDoesNotExistWarningMessage": "已请求\n{invalidIdsLength, plural, one { 个作业 {invalidIds} 不存在} other { 个作业 {invalidIds} 不存在}}",
|
||||
"xpack.ml.jobSelect.wholeGroupDescription": "{wholeGroup}(具有 {count, plural, zero {# 个作业} one {# 个作业} other {# 个作业}})以及\n {total, plural, zero {# 个其他} one {# 个其他} other {# 个其他}}",
|
||||
"xpack.ml.jobSelectButton.jobSelectionMenuButtonAriaLabel": "作业选择菜单",
|
||||
"xpack.ml.jobSelectButton.jobSelectionMenuButtonTooltip": "已选择 {description}",
|
||||
"xpack.ml.jobSelectButton.jobTitle": "作业",
|
||||
"xpack.ml.jobSelectList.applyButtonAriaLabel": "应用",
|
||||
"xpack.ml.jobSelectList.applyButtonLabel": "应用",
|
||||
"xpack.ml.jobSelectList.applyTimeRangeLabel": "同时应用时间范围",
|
||||
"xpack.ml.jobSelectList.cancelButtonAriaLabel": "取消",
|
||||
"xpack.ml.jobSelectList.cancelButtonLabel": "取消",
|
||||
"xpack.ml.jobSelectList.groupIdAriaLabel": "组 ID {groupId}",
|
||||
"xpack.ml.jobSelectList.groupsTitle": "组",
|
||||
"xpack.ml.jobSelectList.groupTimeRangeLabel": "{fromString} 到 {toString}",
|
||||
"xpack.ml.jobSelectList.jobIdAriaLabel": "作业 ID {jobId}",
|
||||
"xpack.ml.jobSelectList.jobSelectionTitle": "作业选择",
|
||||
"xpack.ml.jobSelectList.jobsTitle": "作业",
|
||||
"xpack.ml.jobSelectList.jobTimeRangeLabel": "{fromString} 到 {toString}",
|
||||
"xpack.ml.jobSelectList.loadingJobsLabel": "正在加载作业",
|
||||
"xpack.ml.jobSelectList.selectAllGroupsCheckboxAriaLabel": "选中所有组复选框。总计数 {selectedGroupsLength}。",
|
||||
"xpack.ml.jobSelectList.selectAllJobsCheckboxAriaLabel": "选中所有作业复选框。总计数 {selectedJobsLength}。",
|
||||
"xpack.ml.jobSelectList.timeRangeAriaLabel": "时间范围 {jobTimeRangeLabel}",
|
||||
"xpack.ml.jobSelector.applyFlyoutButton": "应用",
|
||||
"xpack.ml.jobSelector.applyTimerangeSwitchLabel": "应用时间范围",
|
||||
"xpack.ml.jobSelector.clearAllFlyoutButton": "全部清除",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue