Skip to content

Commit

Permalink
[ML] Restore missing job timepicker modal. (elastic#25288)
Browse files Browse the repository at this point in the history
* [ML] Restore missing job timepicker modal.
* [ML] Added a karma/mocha test to verify dependencies are loaded correctly for new_job_controller.
* [ML] Use consistent import style.
  • Loading branch information
walterra committed Nov 7, 2018
1 parent f55b39d commit 1f02e2c
Show file tree
Hide file tree
Showing 7 changed files with 418 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 jobTimePickerTemplate from './job_timepicker_modal.html';

import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');

module.service('mlDatafeedService', function ($modal) {

this.openJobTimepickerWindow = function (job) {
$modal.open({
template: jobTimePickerTemplate,
controller: 'MlJobTimepickerModal',
backdrop: 'static',
keyboard: false,
resolve: {
params: function () {
return {
job
};
}
}
});
};

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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 './datafeed_service';
import './job_timepicker_modal_controller';
import './styles/main.less';
import 'plugins/ml/jobs/new_job/simple/components/watcher';
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<div class="job-timepicker-modal">
<ml-message-bar ></ml-message-bar>
<h1 tooltip="Start datafeed for {{jobId}}" class="euiTitle">Start datafeed for {{jobId}}</h1>

<div class="euiSpacer euiSpacer--s"></div>

<div class="ml-timepicker-contents" >
<div class="row">
<div
class="ml-timepicker-section"
ng-class="{
'ml-timepicker-right-border':
(+ui.startRadio <= 1 && ui.endRadio === '0') ||
(ui.startRadio === '2' && +ui.endRadio <= 1)
}">
<label class="kuiFormLabel">Search start time</label>

<div class="ml-timepicker-radios" >
<ul class="nav nav-pills nav-stacked">
<li ng-class="{ active: ui.startRadio === '1' }">
<a ng-click="ui.startRadio = '1'" >{{ ( isNew?"Start at beginning of data" : "Continue from " + ui.lastTime ) }}</a>
</li>
<li ng-class="{ active: ui.startRadio === '0' }">
<a ng-click="ui.startRadio = '0'">{{ ( isNew?"Start now" : "Continue from now" ) }}</a>
</li>
<li ng-class="{ active: ui.startRadio === '2' }">
<a ng-click="ui.startRadio = '2'" ng-class="{'ml-timepicker-radio-bottom': ui.startRadio === '2'}">{{ ( isNew?"Specify start time" : "Continue from specified time" ) }}</a>
</li>
</ul>
</div>
<div class='ml-timepicker' ng-show="ui.startRadio == '2'">
<div>
<input type="text" class="form-control" input-datetime="YYYY-MM-DD HH:mm:ss" ng-model="ui.timepicker.from" >
</div>
<div>
<datepicker
offset-timezone
ng-model="ui.timepicker.from"
show-weeks="false">
</datepicker>
</div>
</div>
</div>

<div class="ml-timepicker-section"
ng-class="{
'ml-timepicker-left-border':
(+ui.startRadio <= 1 && ui.endRadio === '1')
}">
<label class="kuiFormLabel">Search end time</label>
<div class="ml-timepicker-radios" >
<ul class="nav nav-pills nav-stacked">
<li ng-class="{ active: ui.endRadio === '0' }">
<a ng-click="ui.endRadio = '0'">No end time (Real-time search)</a>
</li>
<li ng-class="{ active: ui.endRadio === '1' }">
<a ng-click="ui.endRadio = '1'" ng-class="{'ml-timepicker-radio-bottom': ui.endRadio === '1'}">Specify end time</a>
</li>
</ul>
</div>
<div class='ml-timepicker' ng-show="ui.endRadio == '1'">
<div>
<input type="text" class="form-control" input-datetime="{{format}}" ng-model="ui.timepicker.to">
</div>
<div>
<datepicker
offset-timezone
ng-model="ui.timepicker.to"
show-weeks="false">
</datepicker>
</div>
</div>
</div>
</div>

</div>

<div ng-if="ui.endRadio === '0' && watcherEnabled">
<hr class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium">
<label class='kuiCheckBoxLabel kuiVerticalRhythm'>
<input ng-model='ui.createWatch' type="checkbox" class='kuiCheckBox'/>
<span class="kuiCheckBoxLabel__text">
Create watch after datafeed has started
</span>
</label>
</div>
<div class="clearfix"></div>

<hr class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium">

<button
ng-click="save()"
ng-disabled="(
saveLock === true ||
( ui.startRadio==='2' && ui.timepicker.from==='' ) ||
( ui.endRadio==='1' && ui.timepicker.to==='' )
)"
class="kuiButton kuiButton--primary" >
Start
</button>
<button
ng-click="cancel()"
ng-disabled="(saveLock === true)"
class="kuiButton kuiButton--primary"
aria-label="Cancel">
Cancel
</button>

</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* 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 moment from 'moment';
import angular from 'angular';

import { mlJobService } from 'plugins/ml/services/job_service';
import { mlMessageBarService } from 'plugins/ml/components/messagebar/messagebar_service';
import { xpackFeatureProvider } from 'plugins/ml/license/check_license';

import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');

module.controller('MlJobTimepickerModal', function (
$scope,
$rootScope,
$modalInstance,
params,
Private) {
const msgs = mlMessageBarService;
$scope.saveLock = false;
const xpackFeature = Private(xpackFeatureProvider);
$scope.watcherEnabled = xpackFeature.isAvailable('watcher');

const job = angular.copy(params.job);
$scope.jobId = job.job_id;

$scope.datafeedId = mlJobService.getDatafeedId(job.job_id);

$scope.start = '';
$scope.end = '';

let lastTime = '';
if (job.data_counts && job.data_counts.latest_record_timestamp) {
const time = moment(job.data_counts.latest_record_timestamp);
lastTime = time.format('YYYY-MM-DD HH:mm:ss');
}

$scope.isNew = (job.data_counts && job.data_counts.input_record_count > 0) ? false : true;

$scope.ui = {
lastTime: lastTime,
startDateText: '',
startRadio: '1',
endDateText: '',
endRadio: '1',
timepicker: {
from: '',
to: moment()
},
setStartRadio: function (i) {
$scope.ui.startRadio = i;
},
createWatch: false
};

function extractForm() {
if ($scope.ui.startRadio === '0') {
$scope.start = 'now';
}
else if ($scope.ui.startRadio === '1') {
$scope.start = '0';
}
else if ($scope.ui.startRadio === '2') {
$scope.start = moment($scope.ui.timepicker.from).unix() * 1000;
}

if ($scope.ui.endRadio === '0') {
$scope.end = undefined;
} else if ($scope.ui.endRadio === '1') {
$scope.end = moment($scope.ui.timepicker.to).unix() * 1000;
}
}

$scope.save = function () {
$scope.saveLock = true;

extractForm();

let doStartCalled = false;
// in 10s call the function to start the datafeed.
// if the job has already opened and doStart has already been called, nothing will happen.
// However, if the job is still waiting to be opened, the datafeed can be started anyway.
window.setTimeout(doStart, 10000);

// Attempt to open the job first.
// If it's already open, ignore the 409 error
mlJobService.openJob($scope.jobId)
.then(() => {
doStart();
})
.catch((resp) => {
if (resp.statusCode === 409) {
doStart();
} else {
if (resp.statusCode === 500) {
if (doStartCalled === false) {
// doStart hasn't been called yet, this 500 has returned before 10s,
// so it's not due to a timeout
msgs.error(`Could not open ${$scope.jobId}`, resp);
}
} else {
// console.log(resp);
msgs.error(`Could not open ${$scope.jobId}`, resp);
}
$scope.saveLock = false;
}
});

// start the datafeed
function doStart() {
if (doStartCalled === false) {
doStartCalled = true;
mlJobService.startDatafeed($scope.datafeedId, $scope.jobId, $scope.start, $scope.end)
.then(() => {
$rootScope.$broadcast('jobsUpdated');

if ($scope.ui.createWatch) {
$rootScope.$broadcast('openCreateWatchWindow', job);
}
})
.catch(() => {
$scope.saveLock = false;
});
}
}

$modalInstance.close();
window.setTimeout(() => {
$rootScope.$broadcast('jobsUpdated');
}, 500);
};

$scope.cancel = function () {
$modalInstance.close();
};
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
.job-timepicker-modal {
font-size: 14px;
padding:20px;
cursor: auto;

h3 {
overflow: hidden;
text-overflow: ellipsis;
}

.date_container {
width: 200px;
display: inline-block;
}

.ml-timepicker-contents {
margin-top: 5px;

.btn-info.active, .kuiButton--primary.active {
color: #ffffff;
background-color: #154751;
border-color: #134049;
span {
color: #ffffff;
}
}

.btn-default, .kuiButton--basic {
background: transparent;
color: #444444;
border: 0px;
box-shadow: none;
text-shadow: none;
}

[ml-time-input] {
text-align: center;
}

label {
display: block;
}
}

.ml-timepicker-modes {
text-transform: capitalize;
}
.ml-timepicker-section {
float: left;
padding: 0px 15px;
min-width: 294px;
width: 294px;
border-left: 1px solid #FFFFFF;
border-right: 1px solid #FFFFFF;

.ml-timepicker {
padding: 13px;
padding-top: none;
border: 2px solid #ecf0f1;
border-radius: 4px;
border-radius: 4px;
border-top-left-radius: 0px;
border-top-right-radius: 0px;
border-top: none;

.btn, .kuiButton {
padding-left: 8px;
padding-right: 8px;
}
}

.ml-timepicker-radio-bottom {
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
}

.ml-timepicker-left-border {
border-left: 1px solid #ecf0f1;
}

.ml-timepicker-right-border {
border-right: 1px solid #ecf0f1;
}
}
Loading

0 comments on commit 1f02e2c

Please sign in to comment.