You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by on...@apache.org on 2014/07/18 16:21:30 UTC
[1/3] AMBARI-6539. Create main page with table of jobs.
(onechiporenko)
Repository: ambari
Updated Branches:
refs/heads/trunk 39a92eb49 -> 366bcbb69
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/views/table_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/table_view.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/table_view.js
new file mode 100644
index 0000000..a404a49
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/table_view.js
@@ -0,0 +1,362 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+App.TableView = Em.View.extend({
+
+ /**
+ * Defines to show pagination or show all records
+ * @type {Boolean}
+ */
+ pagination: true,
+
+ /**
+ * Shows if all data is loaded and filtered
+ * @type {Boolean}
+ */
+ filteringComplete: false,
+
+ /**
+ * intermediary for filteringComplete
+ * @type {Boolean}
+ */
+ tableFilteringComplete: false,
+
+ /**
+ * The number of rows to show on every page
+ * The value should be a number converted into string type in order to support select element API
+ * Example: "10", "25"
+ * @type {String}
+ */
+ displayLength: '10',
+
+ /**
+ * default value of display length
+ * The value should be a number converted into string type in order to support select element API
+ * Example: "10", "25"
+ */
+ defaultDisplayLength: "10",
+
+ /**
+ * number of hosts in table after applying filters
+ */
+ filteredCount: function () {
+ return this.get('filteredContent.length');
+ }.property('filteredContent.length'),
+
+ /**
+ * Do filtering, using saved in the local storage filter conditions
+ */
+ willInsertElement:function () {
+ this.initFilters();
+ },
+
+ /**
+ * initialize filters
+ * restore values from local DB
+ * or clear filters in case there is no filters to restore
+ */
+ initFilters: function () {
+ this.clearFilters();
+ this.set('tableFilteringComplete', true);
+ },
+
+ /**
+ * Return pagination information displayed on the page
+ * @type {String}
+ */
+ paginationInfo: function () {
+ return this.t('tableView.filters.paginationInfo').format(this.get('startIndex'), this.get('endIndex'), this.get('filteredCount'));
+ }.property('filteredCount', 'endIndex'),
+
+ paginationLeft: Ember.View.extend({
+ tagName: 'a',
+ templateName: 'table/navigation/pagination_left',
+ classNameBindings: ['class'],
+ class: function () {
+ if (this.get("parentView.startIndex") > 1) {
+ return "paginate_previous";
+ }
+ return "paginate_disabled_previous";
+ }.property("parentView.startIndex", 'parentView.filteredCount'),
+
+ click: function () {
+ if (this.get('class') === "paginate_previous") {
+ this.get('parentView').previousPage();
+ }
+ }
+ }),
+
+ paginationRight: Ember.View.extend({
+ tagName: 'a',
+ templateName: 'table/navigation/pagination_right',
+ classNameBindings: ['class'],
+ class: function () {
+ if ((this.get("parentView.endIndex")) < this.get("parentView.filteredCount")) {
+ return "paginate_next";
+ }
+ return "paginate_disabled_next";
+ }.property("parentView.endIndex", 'parentView.filteredCount'),
+
+ click: function () {
+ if (this.get('class') === "paginate_next") {
+ this.get('parentView').nextPage();
+ }
+ }
+ }),
+
+ paginationFirst: Ember.View.extend({
+ tagName: 'a',
+ templateName: 'table/navigation/pagination_first',
+ classNameBindings: ['class'],
+ class: function () {
+ if ((this.get("parentView.endIndex")) > parseInt(this.get("parentView.displayLength"))) {
+ return "paginate_previous";
+ }
+ return "paginate_disabled_previous";
+ }.property("parentView.endIndex", 'parentView.filteredCount'),
+
+ click: function () {
+ if (this.get('class') === "paginate_previous") {
+ this.get('parentView').firstPage();
+ }
+ }
+ }),
+
+ paginationLast: Ember.View.extend({
+ tagName: 'a',
+ templateName: 'table/navigation/pagination_last',
+ classNameBindings: ['class'],
+ class: function () {
+ if (this.get("parentView.endIndex") !== this.get("parentView.filteredCount")) {
+ return "paginate_next";
+ }
+ return "paginate_disabled_next";
+ }.property("parentView.endIndex", 'parentView.filteredCount'),
+
+ click: function () {
+ if (this.get('class') === "paginate_next") {
+ this.get('parentView').lastPage();
+ }
+ }
+ }),
+
+ /**
+ * Select View with list of "rows-per-page" options
+ * @type {Ember.View}
+ */
+ rowsPerPageSelectView: Em.Select.extend({
+ content: ['10', '25', '50', '100'],
+ change: function () {
+ this.get('parentView').saveDisplayLength();
+ }
+ }),
+
+ /**
+ * Start index for displayed content on the page
+ */
+ startIndex: 1,
+
+ /**
+ * Calculate end index for displayed content on the page
+ */
+ endIndex: function () {
+ if (this.get('pagination') && this.get('displayLength')) {
+ return Math.min(this.get('filteredCount'), this.get('startIndex') + parseInt(this.get('displayLength')) - 1);
+ } else {
+ return this.get('filteredCount') || 0;
+ }
+ }.property('startIndex', 'displayLength', 'filteredCount'),
+
+ /**
+ * Onclick handler for previous page button on the page
+ */
+ previousPage: function () {
+ var result = this.get('startIndex') - parseInt(this.get('displayLength'));
+ this.set('startIndex', (result < 2) ? 1 : result);
+ },
+
+ /**
+ * Onclick handler for next page button on the page
+ */
+ nextPage: function () {
+ var result = this.get('startIndex') + parseInt(this.get('displayLength'));
+ if (result - 1 < this.get('filteredCount')) {
+ this.set('startIndex', result);
+ }
+ },
+ /**
+ * Onclick handler for first page button on the page
+ */
+ firstPage: function () {
+ this.set('startIndex', 1);
+ },
+ /**
+ * Onclick handler for last page button on the page
+ */
+ lastPage: function () {
+ var pagesCount = this.get('filteredCount') / parseInt(this.get('displayLength'));
+ var startIndex = (this.get('filteredCount') % parseInt(this.get('displayLength')) === 0) ?
+ (pagesCount - 1) * parseInt(this.get('displayLength')) :
+ Math.floor(pagesCount) * parseInt(this.get('displayLength'));
+ this.set('startIndex', ++startIndex);
+ },
+
+ /**
+ * Calculates default value for startIndex property after applying filter or changing displayLength
+ */
+ updatePaging: function (controller, property) {
+ var displayLength = this.get('displayLength');
+ var filteredContentLength = this.get('filteredCount');
+ if (property == 'displayLength') {
+ this.set('startIndex', Math.min(1, filteredContentLength));
+ }
+ else
+ if (!filteredContentLength) {
+ this.set('startIndex', 0);
+ }
+ else
+ if (this.get('startIndex') > filteredContentLength) {
+ this.set('startIndex', Math.floor((filteredContentLength - 1) / displayLength) * displayLength + 1);
+ }
+ else
+ if (!this.get('startIndex')) {
+ this.set('startIndex', 1);
+ }
+ }.observes('displayLength', 'filteredCount'),
+
+ /**
+ * Apply each filter to each row
+ *
+ * @param {Number} iColumn number of column by which filter
+ * @param {Object} value
+ * @param {String} type
+ */
+ updateFilter: function (iColumn, value, type) {
+ var filterCondition = this.get('filterConditions').findProperty('iColumn', iColumn);
+ if (filterCondition) {
+ filterCondition.value = value;
+ }
+ else {
+ filterCondition = {
+ iColumn: iColumn,
+ value: value,
+ type: type
+ };
+ this.get('filterConditions').push(filterCondition);
+ }
+ this.filtersUsedCalc();
+ this.filter();
+ },
+
+ /**
+ * Contain filter conditions for each column
+ * @type {Array}
+ */
+ filterConditions: [],
+
+ /**
+ * Contains content after implementing filters
+ * @type {Array}
+ */
+ filteredContent: [],
+
+ /**
+ * Determine if <code>filteredContent</code> is empty or not
+ * @type {Boolean}
+ */
+ hasFilteredItems: function() {
+ return !!this.get('filteredCount');
+ }.property('filteredCount'),
+
+ /**
+ * Contains content to show on the current page of data page view
+ * @type {Array}
+ */
+ pageContent: function () {
+ return this.get('filteredContent').slice(this.get('startIndex') - 1, this.get('endIndex'));
+ }.property('filteredCount', 'startIndex', 'endIndex'),
+
+ /**
+ * Filter table by filterConditions
+ */
+ filter: function () {
+ var content = this.get('content');
+ var filterConditions = this.get('filterConditions').filterProperty('value');
+ var result;
+ var assoc = this.get('colPropAssoc');
+ if (filterConditions.length) {
+ result = content.filter(function (item) {
+ var match = true;
+ filterConditions.forEach(function (condition) {
+ var filterFunc = App.Filters.getFilterByType(condition.type, false);
+ if (match) {
+ match = filterFunc(item.get(assoc[condition.iColumn]), condition.value);
+ }
+ });
+ return match;
+ });
+ this.set('filteredContent', result);
+ } else {
+ this.set('filteredContent', content.toArray());
+ }
+ }.observes('content.length'),
+
+ /**
+ * Does any filter is used on the page
+ * @type {Boolean}
+ */
+ filtersUsed: false,
+
+ /**
+ * Determine if some filters are used on the page
+ * Set <code>filtersUsed</code> value
+ */
+ filtersUsedCalc: function() {
+ var filterConditions = this.get('filterConditions');
+ if (!filterConditions.length) {
+ this.set('filtersUsed', false);
+ return;
+ }
+ var filtersUsed = false;
+ filterConditions.forEach(function(filterCondition) {
+ if (filterCondition.value.toString() !== '') {
+ filtersUsed = true;
+ }
+ });
+ this.set('filtersUsed', filtersUsed);
+ },
+
+ /**
+ * Run <code>clearFilter</code> in the each child filterView
+ */
+ clearFilters: function() {
+ this.set('filterConditions', []);
+ this.get('_childViews').forEach(function(childView) {
+ if (childView['clearFilter']) {
+ childView.clearFilter();
+ }
+ });
+ },
+
+ actions: {
+ actionClearFilters: function() {
+ this.clearFilters();
+ }
+ }
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/styles/main.less
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/styles/main.less b/contrib/views/jobs/src/main/resources/ui/app/styles/main.less
index c1997c2..8cd95dc 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/styles/main.less
+++ b/contrib/views/jobs/src/main/resources/ui/app/styles/main.less
@@ -16,4 +16,303 @@
* limitations under the License.
*/
-@import '../../app/bower_components/bootstrap/less/bootstrap';
\ No newline at end of file
+@import '../../app/bower_components/bootstrap/less/bootstrap';
+
+#jobs {
+
+ .jobs-type {
+ float: right;
+ margin-top: -24px;
+ }
+
+ .new-jobs-link {
+ float: left;
+ margin-left: 496px;
+ margin-top: -20px;
+ }
+
+ #filtered-jobs{
+ float: left;
+ margin-top: 8px;
+ }
+
+ .jobs_head{
+ height: 30px;
+ }
+
+ .page-bar {
+ border: 1px solid #E4E4E4;
+ color: #7B7B7B;
+ text-align: right;
+ font-size: 12px;
+ label {
+ font-size: 12px;
+ }
+ div {
+ display: inline-block;
+ margin:0 10px;
+ }
+ .items-on-page {
+ label {
+ display:inline;
+ }
+ select {
+ margin-bottom: 4px;
+ margin-top: 4px;
+ width:70px;
+ font-size: 12px;
+ height: 27px;
+ }
+ }
+
+ .paging_two_button {
+ a {
+ padding:0 5px;
+ }
+ a.paginate_disabled_next, a.paginate_disabled_previous {
+ color: gray;
+ &:hover i{
+ color: gray;
+ text-decoration: none;
+ cursor: default;
+ }
+ }
+
+ a.paginate_next, a.paginate_previous {
+ &:hover {
+ text-decoration: none;
+ cursor: pointer;
+ }
+ }
+ }
+ }
+
+ #jobs-table {
+
+ .is-not-link{
+ cursor: default;
+ color: #000000;
+ text-decoration: none;
+ }
+
+ .apply-btn {
+ font-size: 12px;
+ padding: 0px 8px;
+ margin-left: 6px;
+ margin-top: -8px;
+ line-height: 22px;
+ }
+
+ .input-120{
+ width: 120px;
+ }
+
+ .label-row {
+ font-size: 0.9em;
+ th {
+ padding: 4px 4px 4px 8px;
+ }
+ .active-sort {
+ color: #555555;
+ text-decoration: none;
+ background-color: #e5e5e5;
+ -webkit-box-shadow: inset 0 5px 8px rgba(0, 0, 0, 0.100);
+ -moz-box-shadow: inset 0 5px 8px rgba(0, 0, 0, 0.100);
+ box-shadow: inset 0 5px 8px rgba(0, 0, 0, 0.100);
+ }
+ }
+ thead {
+ background: none repeat scroll 0 0 #F8F8F8;
+ }
+ #filter-row {
+ th {
+ padding: 0px;
+ padding-left: 8px;
+ }
+ .active-filter {
+ color: #555555;
+ text-decoration: none;
+ background-color: #e5e5e5;
+ -webkit-box-shadow: inset 0 -5px 8px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 -5px 8px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 -5px 8px rgba(0, 0, 0, 0.05);
+ }
+ input {
+ font-size: 12px;
+ height: 14px;
+ }
+ select {
+ height: 27px;
+ font-size: 12px;
+ }
+ .start-time a.ui-icon-circle-close {
+ margin-top: 7px;
+ }
+ .filter-btn {
+ color: #999999;
+ font-size: 12px;
+ line-height: 14px;
+ padding-left: 6px;
+ text-align: left;
+ width: 100px;
+ .icon-filter {
+ color: #999999;
+ }
+ }
+ }
+ th {
+ border-top: none;
+ }
+ th, td {
+ border-left-width: 0;
+ }
+ .no-data{
+ text-align: center;
+ }
+ a.job-link {
+ width: 100%;
+ overflow: auto;
+ word-wrap: break-word;
+ display: inline-block;
+ }
+ .tooltip-inner {
+ text-align: left;
+ max-width: 400px !important;
+ }
+ td:first-child,
+ th:first-child {
+ border-left-width: 1px;
+ width: 14px;
+ }
+ td:first-child + td,
+ th:first-child + th {
+ width: 36%;
+ }
+ td:first-child + td + td,
+ th:first-child + th + th{
+ width: 20%;
+ }
+ td:first-child + td + td + td,
+ th:first-child + th + th + th,
+ td:first-child + td + td + td + td,
+ th:first-child + th + th + th + th{
+ width: 16%;
+ }
+ td:first-child + td + td + td + td + td,
+ th:first-child + th + th + th + th + th{
+ width: 12%;
+ }
+ }
+ .table {
+ table-layout: fixed;
+ th {
+ border-top: none;
+ }
+ ul.filter-components {
+ padding: 5px 0;
+ background: #777777;
+ color: #ffffff;
+ font-weight: normal;
+ font-size: 12px;
+ label {
+ font-size: 12px;
+ }
+ li {
+ display: block;
+ padding: 3px 0 3px 5px;
+ line-height: 20px;
+ label.checkbox {
+ padding-left: 3px;
+ }
+ input[type="checkbox"] {
+ margin: 4px 4px 2px 2px;
+ }
+ }
+ li#title-bar {
+ text-align: left;
+ border-bottom: 1px solid #e4e4e4;
+ a.close {
+ background: #777777;
+ display: inline;
+ color: #ffffff;
+ padding-left: 35px;
+ padding-right: 12px;
+ text-shadow: 0 1px 0 #ffffff;
+ float: none;
+ font-size: 10px;
+ opacity: 0.6;
+ }
+ a.close:hover {
+ background: #777777;
+ opacity: 1.0;
+ }
+ }
+ li#selector-bar {
+ text-align: left;
+ border-bottom: 1px solid #e4e4e4;
+ font-size: 6px;
+ }
+ li#list-area {
+ font-weight: normal;
+ text-align: left;
+ }
+ li#button-bar {
+ text-align: center;
+ button {
+ font-size: 12px;
+ }
+ }
+ ul {
+ margin-left: 10px;
+ }
+ &>li {
+ &>ul {
+ height: 150px;
+ margin-left: 0;
+ overflow-y: scroll;
+ }
+ }
+ }
+
+ .sorting_asc {
+ background:
+ url(data:image/png;base64,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) no-repeat right 50%;
+ }
+ .sorting_desc {
+ background: url(data:image/png;base64,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) no-repeat right 50%;
+ }
+ .sorting {
+ background: url( data:image/png;base64,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) no-repeat right 50%;
+ }
+ div.view-wrapper {
+ input[type="checkbox"], .btn-group {
+ margin-bottom: 9px;
+ }
+ }
+
+ a.ui-icon-circle-close {
+ float: right;
+ opacity: 0.2;
+ padding: 1px 0;
+ position: relative;
+ right: 0px;
+ margin-top: 3px;
+ z-index: 10;
+ &:hover {
+ opacity: 0.7;
+ }
+ }
+ .notActive {
+ a.ui-icon-circle-close {
+ visibility: hidden;
+ }
+ }
+ }
+}
+
+.sort-wrapper {
+ .column-name {
+ cursor: pointer;
+ padding-right: 18px;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs
new file mode 100644
index 0000000..d43f5f0
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/jobs.hbs
@@ -0,0 +1,90 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+
+<div id="jobs">
+ <div class="jobs_head">
+ {{#if controller.hasNewJobs}}
+ <div class="new-jobs-link">
+ <a href="javascript:void(null);" {{action updateJobsByClick target="controller"}}>{{t jobs.new_jobs.info}}</a>
+ </div>
+ {{/if}}
+ <div class="jobs-type">
+ {{t jobs.type}} : <span class="label label-info">{{t jobs.type.hive}}</span>
+ </div>
+ </div>
+ <table id="jobs-table" class="table table-bordered table-striped">
+ <thead>
+ {{#view view.sortView classNames="label-row" contentBinding="view.content"}}
+ <th></th>
+ {{view view.parentView.idSort}}
+ {{view view.parentView.userSort}}
+ {{view view.parentView.startTimeSort}}
+ {{view view.parentView.endTimeSort}}
+ {{view view.parentView.durationSort}}
+ {{/view}}
+
+ <tr id="filter-row" class="first">
+ <th></th>
+ <th>{{view view.jobsIdFilterView}}</th>
+ <th>{{view view.userFilterView}}</th>
+ <th class="start-time">{{view view.startTimeFilterView}}</th>
+ <th></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ {{#if view.noDataToShow}}
+ <tr>
+ <td class="no-data" colspan="6">
+ {{controller.jobsMessage}}
+ </td>
+ </tr>
+ {{else}}
+ {{#each job in controller.sortedContent}}
+ <tr>
+ <td>
+ {{#if job.failed}}
+ <i class="icon-remove-sign job-link" {{bind-attr title="view.jobFailMessage"}}></i>
+ {{/if}}
+ </td>
+ <td>{{view view.jobNameView jobBinding="job"}}</td>
+ <td>{{job.user}}</td>
+ <td>{{job.startTimeDisplay}}</td>
+ <td>{{job.endTimeDisplay}}</td>
+ <td>{{job.durationDisplay}}</td>
+ </tr>
+ {{/each}}
+ {{/if}}
+ </tbody>
+ </table>
+
+ <div class="page-bar">
+ <div id="filtered-jobs">
+ {{view.filteredJobs}} - <a href="javascript:void(null);" {{action actionClearFilters target="view"}}>{{t jobs.filtered.clear}}</a>
+ </div>
+ <div class="items-on-page">
+ <label>{{t jobs.show.up.to}}: {{view view.rowsPerPageSelectView selectionBinding="view.displayLength"}}</label>
+ </div>
+ <div class="paging_two_button">
+ {{view view.jobsPaginationLeft}}
+ {{view view.jobsPaginationRight}}
+ </div>
+ </div>
+</div>
+
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/jobs/jobs_name.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/jobs/jobs_name.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/jobs/jobs_name.hbs
new file mode 100644
index 0000000..37690ef
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/jobs/jobs_name.hbs
@@ -0,0 +1,19 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<a href="#" rel="tooltip" {{bind-attr class="view.isLink :job-link" data-original-title="job.queryText"}}>{{job.name}}</a>
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/sort_field_template.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/sort_field_template.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/sort_field_template.hbs
new file mode 100644
index 0000000..6fc49fe
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/sort_field_template.hbs
@@ -0,0 +1,19 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<span {{bind-attr class="view.status :column-name"}}>{{view.displayName}}</span>
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_first.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_first.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_first.hbs
new file mode 100644
index 0000000..d538654
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_first.hbs
@@ -0,0 +1,19 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<i class="icon-step-backward"></i>
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_last.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_last.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_last.hbs
new file mode 100644
index 0000000..99dbd68
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_last.hbs
@@ -0,0 +1,19 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<i class="icon-step-forward"></i>
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_left.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_left.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_left.hbs
new file mode 100644
index 0000000..683a180
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_left.hbs
@@ -0,0 +1,19 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<i class="icon-arrow-left"></i>
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_right.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_right.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_right.hbs
new file mode 100644
index 0000000..a6b67cd
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/table/navigation/pagination_right.hbs
@@ -0,0 +1,19 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<i class="icon-arrow-right"></i>
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/wrapper_layout.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/wrapper_layout.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/wrapper_layout.hbs
new file mode 100644
index 0000000..c2904de
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/wrapper_layout.hbs
@@ -0,0 +1,19 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<a href="#" {{action "clearFilter" target="view"}} class="ui-icon ui-icon-circle-close"></a> {{yield}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/templates/wrapper_template.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/templates/wrapper_template.hbs b/contrib/views/jobs/src/main/resources/ui/app/templates/wrapper_template.hbs
new file mode 100644
index 0000000..0529100
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/templates/wrapper_template.hbs
@@ -0,0 +1,25 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+{{#if view.fieldId}}
+ <input type="hidden" id="{{unbound view.fieldId}}" value="" />
+{{/if}}
+{{view view.filterView}}
+{{#if view.showApply}}
+ <button {{action "actionSetValueOnApply" target="view"}} class="apply-btn btn"><span>{{t apply}}</span></button>
+{{/if}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/bower.json
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/bower.json b/contrib/views/jobs/src/main/resources/ui/bower.json
index 0619e51..9b123b2 100644
--- a/contrib/views/jobs/src/main/resources/ui/bower.json
+++ b/contrib/views/jobs/src/main/resources/ui/bower.json
@@ -3,9 +3,11 @@
"version": "0.0.1",
"dependencies": {
"ember": "1.5.0",
+ "moment": ">=2.7.0",
"handlebars": "1.2.1",
"ember-data": "1.0.0-beta.7",
- "bootstrap": ">3.0",
+ "ember-i18n": "1.6.*",
+ "bootstrap": "2.3.*",
"ember-addons.bs_for_ember": ">=0.7",
"ember-json-mapper": "master"
},
[2/3] git commit: AMBARI-6539. Create main page with table of jobs.
(onechiporenko)
Posted by on...@apache.org.
AMBARI-6539. Create main page with table of jobs. (onechiporenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/5659f0a2
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/5659f0a2
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/5659f0a2
Branch: refs/heads/trunk
Commit: 5659f0a23b2cb8346d496246d0cce635aeefa87d
Parents: 7bdb1e4
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Fri Jul 18 17:17:03 2014 +0300
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Fri Jul 18 17:17:03 2014 +0300
----------------------------------------------------------------------
.../jobs/src/main/resources/ui/Gruntfile.js | 2 +-
.../ui/app/img/glyphicons-halflings.png | Bin 0 -> 13826 bytes
.../jobs/src/main/resources/ui/app/index.html | 7 +-
.../src/main/resources/ui/app/scripts/app.js | 15 +-
.../ui/app/scripts/assets/hive-queries.json | 156 ++++++
.../app/scripts/controllers/job_controller.js | 19 +
.../app/scripts/controllers/jobs_controller.js | 489 +++++++++++++++++++
.../resources/ui/app/scripts/helpers/ajax.js | 16 +-
.../resources/ui/app/scripts/helpers/misc.js | 28 +-
.../scripts/mappers/jobs/hive_jobs_mapper.js | 92 ++--
.../ui/app/scripts/mixins/run_periodically.js | 78 +++
.../ui/app/scripts/models/jobs/hive_job.js | 2 -
.../resources/ui/app/scripts/translations.js | 81 +++
.../ui/app/scripts/views/filter_view.js | 488 ++++++++++++++++++
.../resources/ui/app/scripts/views/job_view.js | 19 +
.../resources/ui/app/scripts/views/jobs_view.js | 305 ++++++++++++
.../resources/ui/app/scripts/views/sort_view.js | 253 ++++++++++
.../ui/app/scripts/views/table_view.js | 362 ++++++++++++++
.../src/main/resources/ui/app/styles/main.less | 301 +++++++++++-
.../main/resources/ui/app/templates/jobs.hbs | 90 ++++
.../ui/app/templates/jobs/jobs_name.hbs | 19 +
.../ui/app/templates/sort_field_template.hbs | 19 +
.../table/navigation/pagination_first.hbs | 19 +
.../table/navigation/pagination_last.hbs | 19 +
.../table/navigation/pagination_left.hbs | 19 +
.../table/navigation/pagination_right.hbs | 19 +
.../ui/app/templates/wrapper_layout.hbs | 19 +
.../ui/app/templates/wrapper_template.hbs | 25 +
.../views/jobs/src/main/resources/ui/bower.json | 4 +-
29 files changed, 2910 insertions(+), 55 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/Gruntfile.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/Gruntfile.js b/contrib/views/jobs/src/main/resources/ui/Gruntfile.js
index b5ce07b..7dc777d 100644
--- a/contrib/views/jobs/src/main/resources/ui/Gruntfile.js
+++ b/contrib/views/jobs/src/main/resources/ui/Gruntfile.js
@@ -257,7 +257,7 @@ module.exports = function (grunt) {
src: [
'*.{ico,txt}',
'.htaccess',
- 'images/{,*/}*.{webp,gif}',
+ 'img/*',
'styles/fonts/*',
'scripts/assets/**/*'
]
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/img/glyphicons-halflings.png
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/img/glyphicons-halflings.png b/contrib/views/jobs/src/main/resources/ui/app/img/glyphicons-halflings.png
new file mode 100644
index 0000000..79bc568
Binary files /dev/null and b/contrib/views/jobs/src/main/resources/ui/app/img/glyphicons-halflings.png differ
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/index.html
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/index.html b/contrib/views/jobs/src/main/resources/ui/app/index.html
index 850126e..ea74dcf 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/index.html
+++ b/contrib/views/jobs/src/main/resources/ui/app/index.html
@@ -27,11 +27,14 @@
</head>
<body>
<!-- build:js(app) scripts/components.js -->
- <script src="bower_components/jquery/dist/jquery.js"></script>
- <script src="bower_components/handlebars/handlebars.runtime.js"></script>
+ <script src="bower_components/jquery/jquery.js"></script>
+ <script src="bower_components/bootstrap/js/bootstrap-tooltip.js"></script>
+ <script src="bower_components/moment/moment.js"></script>
+ <script src="bower_components/handlebars/handlebars.js"></script>
<script src="@@ember"></script>
<script src="@@ember_data"></script>
<script src="bower_components/ember-json-mapper/ember-json-mapper.js"></script>
+ <script src="bower_components/ember-i18n/lib/i18n.js"></script>
<!-- endbuild -->
<!-- build:js(.tmp) scripts/templates.js -->
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js
index 5622f4a..c5d68f2 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/app.js
@@ -27,11 +27,19 @@ App.initializer({
initialize: function(container, application) {
application.reopen({
+
/**
* Test mode is automatically enabled if running on localhost
* @type {bool}
*/
- testMode: (location.hostname == 'localhost')
+ testMode: (location.hostname == 'localhost'),
+
+ /**
+ * Prefix for API-requests
+ * @type {string}
+ */
+ urlPrefix: '/api/v1'
+
});
}
@@ -39,8 +47,10 @@ App.initializer({
/* Order and include as you please. */
+require('scripts/translations');
require('scripts/router');
require('scripts/store');
+require('scripts/mixins/*');
require('scripts/helpers/*');
require('scripts/models/**/*');
require('scripts/mappers/server_data_mapper.js');
@@ -48,4 +58,7 @@ require('scripts/mappers/**/*');
require('scripts/controllers/*');
require('scripts/routes/*');
require('scripts/components/*');
+require('scripts/views/sort_view');
+require('scripts/views/filter_view');
+require('scripts/views/table_view');
require('scripts/views/*');
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/assets/hive-queries.json
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/assets/hive-queries.json b/contrib/views/jobs/src/main/resources/ui/app/scripts/assets/hive-queries.json
index b601670..8bd8f58 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/assets/hive-queries.json
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/assets/hive-queries.json
@@ -227,6 +227,162 @@
},
"entity": "root_20140221171313_c9710dd6-0d1c-4d9c-9dff-031edbd20b66",
"entitytype": "HIVE_QUERY_ID"
+ },
+ {
+ "starttime": 1393443850756,
+ "events": [
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_COMPLETED",
+ "eventinfo": {}
+ },
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_SUBMITTED",
+ "eventinfo": {}
+ }
+ ],
+ "otherinfo": {
+ "status": false,
+ "query": "{}"
+ },
+ "primaryfilters": {
+ "user": [
+ "hive"
+ ]
+ },
+ "entity": "hive_20188952544444_6301b51e-d52c-4618-995f-573e3f59006c",
+ "entitytype": "HIVE_QUERY_ID"
+ },
+ {
+ "starttime": 1393443850756,
+ "events": [
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_COMPLETED",
+ "eventinfo": {}
+ },
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_SUBMITTED",
+ "eventinfo": {}
+ }
+ ],
+ "otherinfo": {
+ "status": false,
+ "query": "{}"
+ },
+ "primaryfilters": {
+ "user": [
+ "hive"
+ ]
+ },
+ "entity": "hive_20196139444444_6301b51e-d52c-4618-995f-573e3f59006c",
+ "entitytype": "HIVE_QUERY_ID"
+ },
+ {
+ "starttime": 1393443850756,
+ "events": [
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_COMPLETED",
+ "eventinfo": {}
+ },
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_SUBMITTED",
+ "eventinfo": {}
+ }
+ ],
+ "otherinfo": {
+ "status": false,
+ "query": "{}"
+ },
+ "primaryfilters": {
+ "user": [
+ "hive"
+ ]
+ },
+ "entity": "hive_20127273144444_6301b51e-d52c-4618-995f-573e3f59006c",
+ "entitytype": "HIVE_QUERY_ID"
+ },
+ {
+ "starttime": 1393443850756,
+ "events": [
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_COMPLETED",
+ "eventinfo": {}
+ },
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_SUBMITTED",
+ "eventinfo": {}
+ }
+ ],
+ "otherinfo": {
+ "status": false,
+ "query": "{}"
+ },
+ "primaryfilters": {
+ "user": [
+ "hive"
+ ]
+ },
+ "entity": "hive_20113100844444_6301b51e-d52c-4618-995f-573e3f59006c",
+ "entitytype": "HIVE_QUERY_ID"
+ },
+ {
+ "starttime": 1393443850756,
+ "events": [
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_COMPLETED",
+ "eventinfo": {}
+ },
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_SUBMITTED",
+ "eventinfo": {}
+ }
+ ],
+ "otherinfo": {
+ "status": false,
+ "query": "{}"
+ },
+ "primaryfilters": {
+ "user": [
+ "hive"
+ ]
+ },
+ "entity": "hive_20167400444444_6301b51e-d52c-4618-995f-573e3f59006c",
+ "entitytype": "HIVE_QUERY_ID"
+ },
+ {
+ "starttime": 1393443850756,
+ "events": [
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_COMPLETED",
+ "eventinfo": {}
+ },
+ {
+ "timestamp": 1393443850756,
+ "eventtype": "QUERY_SUBMITTED",
+ "eventinfo": {}
+ }
+ ],
+ "otherinfo": {
+ "status": false,
+ "query": "{}"
+ },
+ "primaryfilters": {
+ "user": [
+ "hive"
+ ]
+ },
+ "entity": "hive_20110915544444_6301b51e-d52c-4618-995f-573e3f59006c",
+ "entitytype": "HIVE_QUERY_ID"
}
]
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js
new file mode 100644
index 0000000..e300323
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/job_controller.js
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+App.JobController = Ember.Controller.extend({});
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
new file mode 100644
index 0000000..c2b6560
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
@@ -0,0 +1,489 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+App.JobsController = Ember.ArrayController.extend(App.RunPeriodically, {
+
+ name:'mainJobsController',
+
+ /**
+ * Sorted ArrayProxy
+ */
+ sortedContent: [],
+
+ contentAndSortObserver : function() {
+ Ember.run.once(this, 'contentAndSortUpdater');
+ }.observes('content.length', 'content.@each.id', 'content.@each.startTime', 'content.@each.endTime', 'sortProperties', 'sortAscending'),
+
+ contentAndSortUpdater: function() {
+ this.set('sortingDone', false);
+ var content = this.get('content');
+ var sortedContent = content.toArray();
+ var sortProperty = this.get('sortProperty');
+ var sortAscending = this.get('sortAscending');
+ sortedContent.sort(function(r1, r2) {
+ var r1id = r1.get(sortProperty);
+ var r2id = r2.get(sortProperty);
+ if (r1id < r2id)
+ return sortAscending ? -1 : 1;
+ if (r1id > r2id)
+ return sortAscending ? 1 : -1;
+ return 0;
+ });
+ var sortedArray = this.get('sortedContent');
+ var count = 0;
+ sortedContent.forEach(function(sortedJob){
+ if(sortedArray.length <= count) {
+ sortedArray.pushObject(Ember.Object.create());
+ }
+ sortedArray[count].set('failed', sortedJob.get('failed'));
+ sortedArray[count].set('hasTezDag', sortedJob.get('hasTezDag'));
+ sortedArray[count].set('queryText', sortedJob.get('queryText'));
+ sortedArray[count].set('name', sortedJob.get('name'));
+ sortedArray[count].set('user', sortedJob.get('user'));
+ sortedArray[count].set('id', sortedJob.get('id'));
+ sortedArray[count].set('startTimeDisplay', sortedJob.get('startTimeDisplay'));
+ sortedArray[count].set('endTimeDisplay', sortedJob.get('endTimeDisplay'));
+ sortedArray[count].set('durationDisplay', sortedJob.get('durationDisplay'));
+ count ++;
+ });
+ if(sortedArray.length > count) {
+ for(var c = sortedArray.length-1; c >= count; c--){
+ sortedArray.removeObject(sortedArray[c]);
+ }
+ }
+ sortedContent.length = 0;
+ this.set('sortingDone', true);
+ },
+
+ navIDs: {
+ backIDs: [],
+ nextID: ''
+ },
+
+ lastJobID: '',
+
+ hasNewJobs: false,
+
+ loaded : false,
+
+ loading : false,
+
+ resetPagination: false,
+
+ loadJobsTimeout: null,
+
+ loadTimeout: null,
+
+ jobsUpdateInterval: 6000,
+
+ jobsUpdate: null,
+
+ sortingColumn: null,
+
+ sortProperty: 'id',
+
+ sortAscending: true,
+
+ sortingDone: true,
+
+ jobsMessage: Em.I18n.t('jobs.loadingTasks'),
+
+ sortingColumnObserver: function () {
+ if(this.get('sortingColumn')){
+ this.set('sortProperty', this.get('sortingColumn').get('name'));
+ this.set('sortAscending', this.get('sortingColumn').get('status') !== "sorting_desc");
+ }
+ }.observes('sortingColumn.name','sortingColumn.status'),
+
+ updateJobsByClick: function () {
+ this.set('navIDs.backIDs', []);
+ this.set('navIDs.nextID', '');
+ this.get('filterObject').set('nextFromId', '');
+ this.get('filterObject').set('backFromId', '');
+ this.get('filterObject').set('fromTs', '');
+ this.set('hasNewJobs', false);
+ this.set('resetPagination', true);
+ this.loadJobs();
+ },
+
+ updateJobs: function (controllerName, funcName) {
+ clearInterval(this.get('jobsUpdate'));
+ var self = this;
+ var interval = setInterval(function () {
+ App.router.get(controllerName)[funcName]();
+ }, this.jobsUpdateInterval);
+ this.set('jobsUpdate', interval);
+ },
+
+ totalOfJobs: 0,
+
+ setTotalOfJobs: function () {
+ if(this.get('totalOfJobs') < this.get('content.length')){
+ this.set('totalOfJobs', this.get('content.length'));
+ }
+ }.observes('content.length'),
+
+ filterObject: Ember.Object.create({
+ id: "",
+ isIdFilterApplied: false,
+ jobsLimit: '10',
+ user: "",
+ windowStart: "",
+ windowEnd: "",
+ nextFromId: "",
+ backFromId: "",
+ fromTs: "",
+ isAnyFilterApplied: false,
+
+ onApplyIdFilter: function () {
+ this.set('isIdFilterApplied', this.get('id') != "");
+ }.observes('id'),
+
+ /**
+ * Direct binding to startTime filter field
+ */
+ startTime: "",
+
+ onStartTimeChange:function(){
+ var time = "";
+ var curTime = new Date().getTime();
+ switch (this.get('startTime')) {
+ case 'Past 1 hour':
+ time = curTime - 3600000;
+ break;
+ case 'Past 1 Day':
+ time = curTime - 86400000;
+ break;
+ case 'Past 2 Days':
+ time = curTime - 172800000;
+ break;
+ case 'Past 7 Days':
+ time = curTime - 604800000;
+ break;
+ case 'Past 14 Days':
+ time = curTime - 1209600000;
+ break;
+ case 'Past 30 Days':
+ time = curTime - 2592000000;
+ break;
+ case 'Custom':
+ this.showCustomDatePopup();
+ break;
+ case 'Any':
+ time = "";
+ break;
+ }
+ if(this.get('startTime') != "Custom"){
+ this.set("windowStart", time);
+ this.set("windowEnd", "");
+ }
+ }.observes("startTime"),
+
+ // Fields values from Select Custom Dates form
+ customDateFormFields: Ember.Object.create({
+ startDate: null,
+ hoursForStart: null,
+ minutesForStart: null,
+ middayPeriodForStart: null,
+ endDate: null,
+ hoursForEnd: null,
+ minutesForEnd: null,
+ middayPeriodForEnd: null
+ }),
+
+ errors: Ember.Object.create({
+ isStartDateError: false,
+ isEndDateError: false
+ }),
+
+ errorMessages: Ember.Object.create({
+ startDate: '',
+ endDate: ''
+ }),
+
+ showCustomDatePopup: function () {
+ var self = this,
+ windowEnd = "",
+ windowStart = "";
+ /*App.ModalPopup.show({
+ header: Em.I18n.t('jobs.table.custom.date.header'),
+ onPrimary: function () {
+ self.validate();
+ if(self.get('errors.isStartDateError') || self.get('errors.isEndDateError')){
+ return;
+ }
+
+ var windowStart = self.createCustomStartDate();
+ var windowEnd = self.createCustomEndDate();
+
+ self.set("windowStart", windowStart.getTime());
+ self.set("windowEnd", windowEnd.getTime());
+ this.hide();
+ },
+ onSecondary: function () {
+ self.set('startTime','Any');
+ this.hide();
+ },
+ bodyClass: App.JobsCustomDatesSelectView.extend({
+ controller: self
+ })
+ });*/
+ },
+
+ createCustomStartDate : function () {
+ var startDate = this.get('customDateFormFields.startDate'),
+ hoursForStart = this.get('customDateFormFields.hoursForStart'),
+ minutesForStart = this.get('customDateFormFields.minutesForStart'),
+ middayPeriodForStart = this.get('customDateFormFields.middayPeriodForStart');
+ if (startDate && hoursForStart && minutesForStart && middayPeriodForStart) {
+ return new Date(startDate + ' ' + hoursForStart + ':' + minutesForStart + ' ' + middayPeriodForStart);
+ }
+ return null;
+ },
+
+ createCustomEndDate : function () {
+ var endDate = this.get('customDateFormFields.endDate'),
+ hoursForEnd = this.get('customDateFormFields.hoursForEnd'),
+ minutesForEnd = this.get('customDateFormFields.minutesForEnd'),
+ middayPeriodForEnd = this.get('customDateFormFields.middayPeriodForEnd');
+ if (endDate && hoursForEnd && minutesForEnd && middayPeriodForEnd) {
+ return new Date(endDate + ' ' + hoursForEnd + ':' + minutesForEnd + ' ' + middayPeriodForEnd);
+ }
+ return null;
+ },
+
+ clearErrors: function () {
+ var errorMessages = this.get('errorMessages');
+ Em.keys(errorMessages).forEach(function (key) {
+ errorMessages.set(key, '');
+ }, this);
+ var errors = this.get('errors');
+ Em.keys(errors).forEach(function (key) {
+ errors.set(key, false);
+ }, this);
+ },
+
+ // Validation for every field in customDateFormFields
+ validate: function () {
+ var formFields = this.get('customDateFormFields'),
+ errors = this.get('errors'),
+ errorMessages = this.get('errorMessages');
+ this.clearErrors();
+ // Check if feild is empty
+ Em.keys(errorMessages).forEach(function (key) {
+ if (!formFields.get(key)) {
+ errors.set('is' + key.capitalize() + 'Error', true);
+ errorMessages.set(key, Em.I18n.t('jobs.customDateFilter.error.required'));
+ }
+ }, this);
+ // Check that endDate is after startDate
+ var startDate = this.createCustomStartDate(),
+ endDate = this.createCustomEndDate();
+ if (startDate && endDate && (startDate > endDate)) {
+ errors.set('isEndDateError', true);
+ errorMessages.set('endDate', Em.I18n.t('jobs.customDateFilter.error.date.order'));
+ }
+ },
+
+ /**
+ * Create link for server request
+ * @return {String}
+ */
+ createJobsFiltersLink: function() {
+ var link = "?fields=events,primaryfilters,otherinfo&secondaryFilter=tez:true",
+ numberOfAppliedFilters = 0;
+
+ if(this.get("id") !== "") {
+ link = "/" + this.get("id") + link;
+ numberOfAppliedFilters++;
+ }
+
+ link += "&limit=" + (parseInt(this.get("jobsLimit")) + 1);
+
+ if(this.get("user") !== ""){
+ link += "&primaryFilter=user:" + this.get("user");
+ numberOfAppliedFilters++;
+ }
+ if(this.get("backFromId") != ""){
+ link += "&fromId=" + this.get("backFromId");
+ }
+ if(this.get("nextFromId") != ""){
+ link += "&fromId=" + this.get("nextFromId");
+ }
+ if(this.get("fromTs") != ""){
+ link += "&fromTs=" + this.get("fromTs");
+ }
+ if(this.get("startTime") !== "" && this.get("startTime") !== "Any"){
+ link += this.get("windowStart") !== "" ? ("&windowStart=" + this.get("windowStart")) : "";
+ link += this.get("windowEnd") !== "" ? ("&windowEnd=" + this.get("windowEnd")) : "";
+ numberOfAppliedFilters++;
+ }
+
+ this.set('isAnyFilterApplied', numberOfAppliedFilters > 0);
+
+ return link;
+ }
+ }),
+
+ /*columnsName: Ember.ArrayController.create({
+ content: [
+ { name: Em.I18n.t('jobs.column.id'), index: 0 },
+ { name: Em.I18n.t('jobs.column.user'), index: 1 },
+ { name: Em.I18n.t('jobs.column.start.time'), index: 2 },
+ { name: Em.I18n.t('jobs.column.end.time'), index: 3 },
+ { name: Em.I18n.t('jobs.column.duration'), index: 4 }
+ ],
+ columnsCount: function () {
+ return this.get('content.length') + 1;
+ }.property('content.length')
+ }),*/
+
+ lastIDSuccessCallback: function(data) {
+ if(!data.entities[0]){
+ return;
+ }
+ var lastReceivedID = data.entities[0].entity;
+ if(this.get('lastJobID') == '') {
+ this.set('lastJobID', lastReceivedID);
+ if (this.get('loaded') && App.HiveJob.find().get('length') < 1) {
+ this.set('hasNewJobs', true);
+ }
+ }
+ else
+ if (this.get('lastJobID') !== lastReceivedID) {
+ this.set('lastJobID', lastReceivedID);
+ if(!App.HiveJob.find().findProperty('id', lastReceivedID)) {
+ this.set('hasNewJobs', true);
+ }
+ }
+ },
+
+ lastIDErrorCallback: function(data, jqXHR, textStatus) {
+ console.debug(jqXHR);
+ },
+
+ checkDataLoadingError: function (jqXHR){
+ /*var atsComponent = App.HostComponent.find().findProperty('componentName','APP_TIMELINE_SERVER');
+ if(atsComponent && atsComponent.get('workStatus') != "STARTED") {
+ this.set('jobsMessage', Em.I18n.t('jobs.error.ats.down'));
+ }else if (jqXHR && jqXHR.status == 400) {
+ this.set('jobsMessage', Em.I18n.t('jobs.error.400'));
+ }else if ((!jqXHR && this.get('loaded') && !this.get('loading')) || (jqXHR && jqXHR.status == 500)) {
+ this.set('jobsMessage', Em.I18n.t('jobs.nothingToShow'));
+ }else{
+ this.set('jobsMessage', Em.I18n.t('jobs.loadingTasks'));
+ }*/
+ },
+
+ init: function() {
+ this.set('interval', 6000);
+ this.loop('loadJobs');
+ },
+
+ loadJobs : function() {
+ //var yarnService = App.YARNService.find().objectAt(0),
+ //atsComponent = App.HostComponent.find().findProperty('componentName','APP_TIMELINE_SERVER'),
+ //atsInValidState = !!atsComponent && atsComponent.get('workStatus') === "STARTED",
+ //retryLoad = this.checkDataLoadingError();
+ //if (yarnService != null && atsInValidState) {
+ this.set('loading', true);
+ /*var historyServerHostName = yarnService.get('appTimelineServer.hostName'),
+ filtersLink = this.get('filterObject').createJobsFiltersLink(),
+ hiveQueriesUrl = App.get('testMode') ? "/scripts/assets/hive-queries.json" : "/proxy?url=http://" + historyServerHostName
+ + ":" + yarnService.get('ahsWebPort') + "/ws/v1/timeline/HIVE_QUERY_ID" + filtersLink;*/
+ /*App.ajax.send({
+ name: 'jobs.lastID',
+ sender: self,
+ data: {
+ historyServerHostName: '',//historyServerHostName,
+ ahsWebPort: ''//yarnService.get('ahsWebPort')
+ },
+ success: 'lastIDSuccessCallback',
+ error : 'lastIDErrorCallback'
+ });*/
+ App.ajax.send({
+ name: 'load_jobs',
+ sender: this,
+ data: {
+ historyServerHostName: '',
+ ahsWebPort: '',
+ filtersLink: this.get('filterObject').createJobsFiltersLink()
+ },
+ success: 'loadJobsSuccessCallback',
+ error : 'loadJobsErrorCallback'
+ });
+ },
+
+ loadJobsSuccessCallback: function(data) {
+ App.hiveJobsMapper.map(data);
+ this.set('loading', false);
+ if(this.get('loaded') == false || this.get('resetPagination') == true) {
+ this.initializePagination();
+ this.set('resetPagination', false);
+ }
+ this.set('loaded', true);
+ },
+
+ loadJobsErrorCallback: function(jqXHR) {
+ App.hiveJobsMapper.map({entities : []});
+ this.checkDataLoadingError(jqXHR);
+ },
+
+ initializePagination: function() {
+ var back_link_IDs = this.get('navIDs.backIDs.[]');
+ if(!back_link_IDs.contains(this.get('lastJobID'))) {
+ back_link_IDs.push(this.get('lastJobID'));
+ }
+ this.set('filterObject.backFromId', this.get('lastJobID'));
+ this.get('filterObject').set('fromTs', new Date().getTime());
+ },
+
+ navigateNext: function() {
+ this.set("filterObject.backFromId", '');
+ var back_link_IDs = this.get('navIDs.backIDs.[]');
+ var lastBackID = this.get('navIDs.nextID');
+ if(!back_link_IDs.contains(lastBackID)) {
+ back_link_IDs.push(lastBackID);
+ }
+ this.set('navIDs.backIDs.[]', back_link_IDs);
+ this.set("filterObject.nextFromId", this.get('navIDs.nextID'));
+ this.set('navIDs.nextID', '');
+ this.loadJobs();
+ },
+
+ navigateBack: function() {
+ this.set("filterObject.nextFromId", '');
+ var back_link_IDs = this.get('navIDs.backIDs.[]');
+ back_link_IDs.pop();
+ var lastBackID = back_link_IDs[back_link_IDs.length - 1];
+ this.set('navIDs.backIDs.[]', back_link_IDs);
+ this.set("filterObject.backFromId", lastBackID);
+ this.loadJobs();
+ },
+
+ refreshLoadedJobs : function() {
+ this.loadJobs();
+ }.observes(
+ 'filterObject.id',
+ 'filterObject.jobsLimit',
+ 'filterObject.user',
+ 'filterObject.windowStart',
+ 'filterObject.windowEnd'
+ )
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/ajax.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/ajax.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/ajax.js
index 42bae63..2c301ad 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/ajax.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/ajax.js
@@ -28,7 +28,21 @@
*
* @type {Object}
*/
-var urls = {};
+var urls = {
+
+ 'load_jobs': {
+ real: '/proxy?url=http://{historyServerHostName}:{ahsWebPort}/ws/v1/timeline/HIVE_QUERY_ID{filtersLink}',
+ mock: '/scripts/assets/hive-queries.json',
+ apiPrefix: ''
+ },
+
+ 'jobs_lastID': {
+ real: '/proxy?url=http://{historyServerHostName}:{ahsWebPort}/ws/v1/timeline/HIVE_QUERY_ID?limit=1&secondaryFilter=tez:true',
+ mock: '/scripts/assets/hive-queries.json',
+ apiPrefix: ''
+ }
+
+};
/**
* Replace data-placeholders to its values
*
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/misc.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/misc.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/misc.js
index 25af752..f26d658 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/misc.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/misc.js
@@ -28,7 +28,7 @@ App.Helpers.misc = {
} else {
if (value < 1048576) {
value = (value / 1024).toFixed(1) + 'KB';
- } else if (value >= 1048576 && value < 1073741824){
+ } else if (value >= 1048576 && value < 1073741824) {
value = (value / 1048576).toFixed(1) + 'MB';
} else {
value = (value / 1073741824).toFixed(2) + 'GB';
@@ -36,6 +36,32 @@ App.Helpers.misc = {
}
}
return value;
+ },
+
+ /**
+ * Convert ip address to integer
+ * @param ip
+ * @return integer
+ */
+ ipToInt: function (ip) {
+ // * example 1: ipToInt('192.0.34.166');
+ // * returns 1: 3221234342
+ // * example 2: ipToInt('255.255.255.256');
+ // * returns 2: false
+ // Verify IP format.
+ if (!/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip)) {
+ return false; // Invalid format.
+ }
+ // Reuse ip variable for component counter.
+ var d = ip.split('.');
+ return ((((((+d[0]) * 256) + (+d[1])) * 256) + (+d[2])) * 256) + (+d[3]);
}
};
+
+App.tooltip = function (self, options) {
+ self.tooltip(options);
+ self.on("remove DOMNodeRemoved", function () {
+ $(this).trigger('mouseleave');
+ });
+};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
index 9a1fc07..49b373c 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/mappers/jobs/hive_jobs_mapper.js
@@ -17,12 +17,46 @@
App.hiveJobsMapper = App.QuickDataMapper.create({
- model: App.HiveJob,
+ json_map: {
+ id: 'entity',
+ name: 'entity',
+ user: 'primaryfilters.user',
+ hasTezDag: {
+ custom: function(source) {
+ var query = Ember.get(source, 'otherinfo.query');
+ return Ember.isNone(query) ? false : query.match("\"Tez\".*\"DagName:\"");
+ }
+ },
+ queryText: {
+ custom: function(source) {
+ var query = Ember.get(source, 'otherinfo.query');
+ return Ember.isNone(query) ? '' : $.parseJSON(query).queryText;
+ }
+ },
+ failed: {
+ custom: function(source) {
+ return Ember.get(source ,'otherinfo.status') === false;
+ }
+ },
+ startTime: {
+ custom: function(source) {
+ return source.starttime > 0 ? source.starttime : null
+ }
+ },
+ endTime: {
+ custom: function(source) {
+ return source.endtime > 0 ? source.endtime : null
+ }
+ }
+ },
map: function (json) {
var model = this.get('model'),
+ jobsToDelete = App.HiveJob.store.all('hiveJob').get('content').mapProperty('id'),
+ map = this.get('json_map'),
hiveJobs = [];
+
if (json) {
if (!json.entities) {
json.entities = [];
@@ -30,67 +64,35 @@ App.hiveJobsMapper = App.QuickDataMapper.create({
json.entities = [json];
}
}
- var currentEntityMap = {};
+
json.entities.forEach(function (entity) {
- currentEntityMap[entity.entity] = entity.entity;
- var hiveJob = {
- id: entity.entity,
- name: entity.entity,
- user: entity.primaryfilters.user
- };
- hiveJob.has_tez_dag = false;
- hiveJob.query_text = '';
- if (entity.otherinfo && entity.otherinfo.query) {
- // Explicit false match needed for when failure hook not set
- hiveJob.failed = entity.otherinfo.status === false;
- hiveJob.has_tez_dag = entity.otherinfo.query.match("\"Tez\".*\"DagName:\"");
- var queryJson = $.parseJSON(entity.otherinfo.query);
- if (queryJson && queryJson.queryText) {
- hiveJob.query_text = queryJson.queryText;
- }
- }
+ var hiveJob = Ember.JsonMapper.map(entity, map);
+
if (entity.events != null) {
entity.events.forEach(function (event) {
switch (event.eventtype) {
case "QUERY_SUBMITTED":
- hiveJob.start_time = event.timestamp;
+ hiveJob.startTime = event.timestamp;
break;
case "QUERY_COMPLETED":
- hiveJob.end_time = event.timestamp;
+ hiveJob.endTime = event.timestamp;
break;
default:
break;
}
});
}
- if (!hiveJob.start_time && entity.starttime > 0) {
- hiveJob.start_time = entity.starttime;
- }
- if (!hiveJob.end_time && entity.endtime > 0) {
- hiveJob.end_time = entity.endtime;
- }
hiveJobs.push(hiveJob);
- hiveJob = null;
- entity = null;
+ jobsToDelete = jobsToDelete.without(hiveJob.id);
});
- /*if(hiveJobs.length > App.router.get('mainJobsController.filterObject.jobsLimit')) {
- var lastJob = hiveJobs.pop();
- if(App.router.get('mainJobsController.navIDs.nextID') != lastJob.id) {
- App.router.set('mainJobsController.navIDs.nextID', lastJob.id);
- }
- currentEntityMap[lastJob.id] = null;
- }*/
+ jobsToDelete.forEach(function (id) {
+ var r = App.HiveJob.store.getById('hiveJob', id);
+ if(r) r.destroyRecord();
+ });
- // Delete IDs not seen from server
- /*var hiveJobsModel = model.find().toArray();
- hiveJobsModel.forEach(function(job) {
- if (job && !currentEntityMap[job.get('id')]) {
- this.deleteRecord(job);
- }
- }, this);*/
}
App.HiveJob.store.pushMany('hiveJob', hiveJobs);
- },
- config: {}
+ }
+
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/mixins/run_periodically.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/mixins/run_periodically.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/mixins/run_periodically.js
new file mode 100644
index 0000000..a6c4bbf
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/mixins/run_periodically.js
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Allow to run object method periodically and stop it
+ * Example:
+ * <code>
+ * var obj = Ember.Object.createWithMixins(App.RunPeriodically, {
+ * method: Ember.K
+ * });
+ * obj.set('interval', 10000); // override default value
+ * obj.loop('method'); // run periodically
+ * obj.stop(); // stop running
+ * </code>
+ * @type {Ember.Mixin}
+ */
+App.RunPeriodically = Ember.Mixin.create({
+
+ /**
+ * Interval for loop
+ * @type {number}
+ */
+ interval: 5000,
+
+ /**
+ * setTimeout's return value
+ * @type {number}
+ */
+ timer: null,
+
+ /**
+ * Run <code>methodName</code> periodically with <code>interval</code>
+ * @param {string} methodName method name to run periodically
+ * @param {bool} initRun should methodName be run before setInterval call (default - true)
+ * @method run
+ */
+ loop: function(methodName, initRun) {
+ initRun = Em.isNone(initRun) ? true : initRun;
+ var self = this,
+ interval = this.get('interval');
+ Ember.assert('Interval should be numeric and greated than 0', $.isNumeric(interval) && interval > 0);
+ if (initRun) {
+ this[methodName]();
+ }
+ this.set('timer',
+ setInterval(function () {
+ self[methodName]();
+ }, interval)
+ );
+ },
+
+ /**
+ * Stop running <code>timer</code>
+ * @method stop
+ */
+ stop: function() {
+ var timer = this.get('timer');
+ if (!Em.isNone(timer)) {
+ clearTimeout(timer);
+ }
+ }
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/models/jobs/hive_job.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/models/jobs/hive_job.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/models/jobs/hive_job.js
index a3784e8..53d309e 100644
--- a/contrib/views/jobs/src/main/resources/ui/app/scripts/models/jobs/hive_job.js
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/models/jobs/hive_job.js
@@ -21,8 +21,6 @@ App.HiveJob = App.AbstractJob.extend({
queryText : DS.attr('string'),
- stages : DS.attr('array'),
-
hasTezDag: DS.attr('boolean'),
tezDag : DS.belongsTo('tezDag', {async:true}),
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/translations.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/translations.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/translations.js
new file mode 100644
index 0000000..a656c97
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/translations.js
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+Ember.I18n.translations = {
+
+ 'any': 'Any',
+ 'apply': 'Apply',
+
+ 'jobs.type':'Jobs Type',
+ 'jobs.type.hive':'Hive',
+ 'jobs.show.up.to':'Show up to',
+ 'jobs.filtered.jobs':'%@ jobs showing',
+ 'jobs.filtered.clear':'clear filters',
+ 'jobs.column.id':'Id',
+ 'jobs.column.user':'User',
+ 'jobs.column.start.time':'Start Time',
+ 'jobs.column.end.time':'End Time',
+ 'jobs.column.duration':'Duration',
+ 'jobs.new_jobs.info':'New jobs available on server.',
+ 'jobs.loadingTasks': 'Loading...',
+
+ 'jobs.nothingToShow': 'No jobs to display',
+ 'jobs.error.ats.down': 'Jobs data cannot be shown since YARN App Timeline Server is not running.',
+ 'jobs.error.400': 'Unable to load data.',
+ 'jobs.table.custom.date.am':'AM',
+ 'jobs.table.custom.date.pm':'PM',
+ 'jobs.table.custom.date.header':'Select Custom Dates',
+ 'jobs.table.job.fail':'Job failed to run',
+ 'jobs.customDateFilter.error.required':'This field is required',
+ 'jobs.customDateFilter.error.date.order':'End Date must be after Start Date',
+ 'jobs.customDateFilter.startTime':'Start Time',
+ 'jobs.customDateFilter.endTime':'End Time',
+ 'jobs.hive.failed':'JOB FAILED',
+ 'jobs.hive.more':'show more',
+ 'jobs.hive.less':'show less',
+ 'jobs.hive.query':'Hive Query',
+ 'jobs.hive.stages':'Stages',
+ 'jobs.hive.yarnApplication':'YARN Application',
+ 'jobs.hive.tez.tasks':'Tez Tasks',
+ 'jobs.hive.tez.hdfs':'HDFS',
+ 'jobs.hive.tez.localFiles':'Local Files',
+ 'jobs.hive.tez.spilledRecords':'Spilled Records',
+ 'jobs.hive.tez.records':'Records',
+ 'jobs.hive.tez.reads':'{0} reads',
+ 'jobs.hive.tez.writes':'{0} writes',
+ 'jobs.hive.tez.records.count':'{0} Records',
+ 'jobs.hive.tez.operatorPlan':'Operator Plan',
+ 'jobs.hive.tez.dag.summary.metric':'Summary Metric',
+ 'jobs.hive.tez.dag.error.noDag.title':'No Tez Information',
+ 'jobs.hive.tez.dag.error.noDag.message':'This job does not identify any Tez information.',
+ 'jobs.hive.tez.dag.error.noDagId.title':'No Tez Information',
+ 'jobs.hive.tez.dag.error.noDagId.message':'No Tez information was found for this job. Either it is waiting to be run, or has exited unexpectedly.',
+ 'jobs.hive.tez.dag.error.noDagForId.title':'No Tez Information',
+ 'jobs.hive.tez.dag.error.noDagForId.message':'No details were found for the Tez ID given to this job.',
+ 'jobs.hive.tez.metric.input':'Input',
+ 'jobs.hive.tez.metric.output':'Output',
+ 'jobs.hive.tez.metric.recordsRead':'Records Read',
+ 'jobs.hive.tez.metric.recordsWrite':'Records Written',
+ 'jobs.hive.tez.metric.tezTasks':'Tez Tasks',
+ 'jobs.hive.tez.metric.spilledRecords':'Spilled Records',
+ 'jobs.hive.tez.edge.':'Unknown',
+ 'jobs.hive.tez.edge.contains':'Contains',
+ 'jobs.hive.tez.edge.broadcast':'Broadcast',
+ 'jobs.hive.tez.edge.scatter_gather':'Shuffle',
+
+};
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/views/filter_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/filter_view.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/filter_view.js
new file mode 100644
index 0000000..0506286
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/filter_view.js
@@ -0,0 +1,488 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Wrapper View for all filter components. Layout template and common actions are located inside of it.
+ * Logic specific for data component(input, select, or custom multi select, which fire any changes on interface) are
+ * located in inner view - <code>filterView</code>.
+ *
+ * If we want to have input filter, put <code>textFieldView</code> to it.
+ * All inner views implemented below this view.
+ * @type {*}
+ */
+
+var wrapperView = Ember.View.extend({
+ classNames: ['view-wrapper'],
+ layoutName: 'wrapper_layout',
+ templateName: 'wrapper_template',
+
+ value: null,
+
+ /**
+ * Column index
+ */
+ column: null,
+
+ /**
+ * If this field is exists we dynamically create hidden input element and set value there.
+ * Used for some cases, where this values will be used outside of component
+ */
+ fieldId: null,
+
+ clearFilter: function(){
+ this.set('value', this.get('emptyValue'));
+ if(this.get('setPropertyOnApply')){
+ this.setValueOnApply();
+ }
+ return false;
+ },
+
+ setValueOnApply: function() {
+ if(this.get('value') == null){
+ this.set('value', '')
+ }
+ this.set(this.get('setPropertyOnApply'), this.get('value'));
+ return false;
+ },
+
+ actions: {
+ actionSetValueOnApply: function() {
+ this.setValueOnApply();
+ }
+ },
+
+ /**
+ * Use to determine whether filter is clear or not. Also when we want to set empty value
+ */
+ emptyValue: '',
+
+ /**
+ * Whether our <code>value</code> is empty or not
+ * @return {Boolean}
+ */
+ isEmpty: function(){
+ if(this.get('value') === null){
+ return true;
+ }
+ return this.get('value').toString() === this.get('emptyValue').toString();
+ },
+
+ /**
+ * Show/Hide <code>Clear filter</code> button.
+ * Also this method updates computed field related to <code>fieldId</code> if it exists.
+ * Call <code>onChangeValue</code> callback when everything is done.
+ */
+ showClearFilter: function(){
+ if(!this.get('parentNode')){
+ return;
+ }
+ // get the sort view element in the same column to current filter view to highlight them together
+ var relatedSort = $(this.get('element')).parents('thead').find('.sort-view-' + this.get('column'));
+ if(this.isEmpty()){
+ this.get('parentNode').removeClass('active-filter');
+ this.get('parentNode').addClass('notActive');
+ relatedSort.removeClass('active-sort');
+ } else {
+ this.get('parentNode').removeClass('notActive');
+ this.get('parentNode').addClass('active-filter');
+ relatedSort.addClass('active-sort');
+ }
+
+ if(this.get('fieldId')){
+ this.$('> input').eq(0).val(this.get('value'));
+ }
+
+ this.onChangeValue();
+ }.observes('value'),
+
+ /**
+ * Callback for value changes
+ */
+ onChangeValue: function(){
+
+ },
+
+ /**
+ * Filter components is located here. Should be redefined
+ */
+ filterView: Em.View,
+
+ /**
+ * Update class of parentNode(hide clear filter button) on page load
+ */
+ didInsertElement: function(){
+ var parent = this.$().parent();
+ this.set('parentNode', parent);
+ parent.addClass('notActive');
+ }
+});
+
+/**
+ * Simple input control for wrapperView
+ */
+var textFieldView = Ember.TextField.extend({
+ type:'text',
+ placeholder: Em.I18n.t('any'),
+ valueBinding: "parentView.value"
+});
+
+/**
+ * Simple multiselect control for wrapperView.
+ * Used to render blue button and popup, which opens on button click.
+ * All content related logic should be implemented manually outside of it
+ */
+var componentFieldView = Ember.View.extend({
+ classNames: ['btn-group'],
+ classNameBindings: ['isFilterOpen:open:'],
+
+ /**
+ * Whether popup is shown or not
+ */
+ isFilterOpen: false,
+
+ /**
+ * We have <code>value</code> property similar to inputs <code>value</code> property
+ */
+ valueBinding: 'parentView.value',
+
+ /**
+ * Clear filter to initial state
+ */
+ clearFilter: function(){
+ this.set('value', '');
+ },
+
+ /**
+ * Onclick handler for <code>cancel filter</code> button
+ */
+ closeFilter:function () {
+ $(document).unbind('click');
+ this.set('isFilterOpen', false);
+ },
+
+ /**
+ * Onclick handler for <code>apply filter</code> button
+ */
+ applyFilter:function() {
+ this.closeFilter();
+ },
+
+ /**
+ * Onclick handler for <code>show component filter</code> button.
+ * Also this function is used in some other places
+ */
+ clickFilterButton:function () {
+ var self = this;
+ this.set('isFilterOpen', !this.get('isFilterOpen'));
+ if (this.get('isFilterOpen')) {
+
+ var dropDown = this.$('.filter-components');
+ var firstClick = true;
+ $(document).bind('click', function (e) {
+ if (!firstClick && $(e.target).closest(dropDown).length == 0) {
+ self.set('isFilterOpen', false);
+ $(document).unbind('click');
+ }
+ firstClick = false;
+ });
+ }
+ }
+});
+
+/**
+ * Simple select control for wrapperView
+ */
+var selectFieldView = Ember.Select.extend({
+ selectionBinding: 'parentView.value',
+ contentBinding: 'parentView.content'
+});
+
+/**
+ * Result object, which will be accessible outside
+ * @type {Object}
+ */
+App.Filters = {
+ /**
+ * You can access wrapperView outside
+ */
+ wrapperView : wrapperView,
+
+ /**
+ * And also controls views if need it
+ */
+ textFieldView : textFieldView,
+ selectFieldView: selectFieldView,
+ componentFieldView: componentFieldView,
+
+ /**
+ * Quick create input filters
+ * @param config parameters of <code>wrapperView</code>
+ */
+ createTextView : function(config){
+ config.fieldType = config.fieldType || 'input-medium';
+ config.filterView = textFieldView.extend({
+ classNames : [ config.fieldType ]
+ });
+
+ return wrapperView.extend(config);
+ },
+
+ /**
+ * Quick create multiSelect filters
+ * @param config parameters of <code>wrapperView</code>
+ */
+ createComponentView : function(config){
+ config.clearFilter = function(){
+ this.forEachChildView(function(item){
+ if(item.clearFilter){
+ item.clearFilter();
+ }
+ });
+ return false;
+ };
+
+ return wrapperView.extend(config);
+ },
+
+ /**
+ * Quick create select filters
+ * @param config parameters of <code>wrapperView</code>
+ */
+ createSelectView: function(config){
+
+ config.fieldType = config.fieldType || 'input-medium';
+ config.filterView = selectFieldView.extend({
+ classNames : [ config.fieldType ],
+ attributeBindings: ['disabled','multiple'],
+ disabled: false
+ });
+ config.emptyValue = Em.I18n.t('any');
+
+ return wrapperView.extend(config);
+ },
+ /**
+ * returns the filter function, which depends on the type of property
+ * @param type
+ * @param isGlobal check is search global
+ * @return {Function}
+ */
+ getFilterByType: function(type, isGlobal){
+ switch (type){
+ case 'ambari-bandwidth':
+ return function(rowValue, rangeExp){
+ var compareChar = isNaN(rangeExp.charAt(0)) ? rangeExp.charAt(0) : false;
+ var compareScale = rangeExp.charAt(rangeExp.length - 1);
+ var compareValue = compareChar ? parseFloat(rangeExp.substr(1, rangeExp.length)) : parseFloat(rangeExp.substr(0, rangeExp.length));
+ var match = false;
+ if (rangeExp.length == 1 && compareChar !== false) {
+ // User types only '=' or '>' or '<', so don't filter column values
+ match = true;
+ return match;
+ }
+ switch (compareScale) {
+ case 'g':
+ compareValue *= 1073741824;
+ break;
+ case 'm':
+ compareValue *= 1048576;
+ break;
+ case 'k':
+ compareValue *= 1024;
+ break;
+ default:
+ //default value in GB
+ compareValue *= 1073741824;
+ }
+ rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
+
+ var convertedRowValue;
+ if (rowValue === '<1KB') {
+ convertedRowValue = 1;
+ } else {
+ var rowValueScale = rowValue.substr(rowValue.length - 2, 2);
+ switch (rowValueScale) {
+ case 'KB':
+ convertedRowValue = parseFloat(rowValue)*1024;
+ break;
+ case 'MB':
+ convertedRowValue = parseFloat(rowValue)*1048576;
+ break;
+ case 'GB':
+ convertedRowValue = parseFloat(rowValue)*1073741824;
+ break;
+ }
+ }
+
+ switch (compareChar) {
+ case '<':
+ if (compareValue > convertedRowValue) match = true;
+ break;
+ case '>':
+ if (compareValue < convertedRowValue) match = true;
+ break;
+ case false:
+ case '=':
+ if (compareValue == convertedRowValue) match = true;
+ break;
+ }
+ return match;
+ };
+ break;
+ case 'duration':
+ return function (rowValue, rangeExp) {
+ var compareChar = isNaN(rangeExp.charAt(0)) ? rangeExp.charAt(0) : false;
+ var compareScale = rangeExp.charAt(rangeExp.length - 1);
+ var compareValue = compareChar ? parseFloat(rangeExp.substr(1, rangeExp.length)) : parseFloat(rangeExp.substr(0, rangeExp.length));
+ var match = false;
+ if (rangeExp.length == 1 && compareChar !== false) {
+ // User types only '=' or '>' or '<', so don't filter column values
+ match = true;
+ return match;
+ }
+ switch (compareScale) {
+ case 's':
+ compareValue *= 1000;
+ break;
+ case 'm':
+ compareValue *= 60000;
+ break;
+ case 'h':
+ compareValue *= 3600000;
+ break;
+ default:
+ compareValue *= 1000;
+ }
+ rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
+
+ switch (compareChar) {
+ case '<':
+ if (compareValue > rowValue) match = true;
+ break;
+ case '>':
+ if (compareValue < rowValue) match = true;
+ break;
+ case false:
+ case '=':
+ if (compareValue == rowValue) match = true;
+ break;
+ }
+ return match;
+ };
+ break;
+ case 'date':
+ return function (rowValue, rangeExp) {
+ var match = false;
+ var timePassed = App.dateTime() - rowValue;
+ switch (rangeExp) {
+ case 'Past 1 hour':
+ match = timePassed <= 3600000;
+ break;
+ case 'Past 1 Day':
+ match = timePassed <= 86400000;
+ break;
+ case 'Past 2 Days':
+ match = timePassed <= 172800000;
+ break;
+ case 'Past 7 Days':
+ match = timePassed <= 604800000;
+ break;
+ case 'Past 14 Days':
+ match = timePassed <= 1209600000;
+ break;
+ case 'Past 30 Days':
+ match = timePassed <= 2592000000;
+ break;
+ case 'Any':
+ match = true;
+ break;
+ }
+ return match;
+ };
+ break;
+ case 'number':
+ return function(rowValue, rangeExp){
+ var compareChar = rangeExp.charAt(0);
+ var compareValue;
+ var match = false;
+ if (rangeExp.length == 1) {
+ if (isNaN(parseInt(compareChar))) {
+ // User types only '=' or '>' or '<', so don't filter column values
+ match = true;
+ return match;
+ }
+ else {
+ compareValue = parseFloat(parseFloat(rangeExp).toFixed(2));
+ }
+ }
+ else {
+ if (isNaN(parseInt(compareChar))) {
+ compareValue = parseFloat(parseFloat(rangeExp.substr(1, rangeExp.length)).toFixed(2));
+ }
+ else {
+ compareValue = parseFloat(parseFloat(rangeExp.substr(0, rangeExp.length)).toFixed(2));
+ }
+ }
+ rowValue = parseFloat((jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue);
+ match = false;
+ switch (compareChar) {
+ case '<':
+ if (compareValue > rowValue) match = true;
+ break;
+ case '>':
+ if (compareValue < rowValue) match = true;
+ break;
+ case '=':
+ if (compareValue == rowValue) match = true;
+ break;
+ default:
+ if (rangeExp == rowValue) match = true;
+ }
+ return match;
+ };
+ break;
+ case 'multiple':
+ return function(origin, compareValue){
+ var options = compareValue.split(','),
+ rowValue = (typeof (origin) === "string") ? origin : origin.mapProperty('componentName').join(" ");
+ var str = new RegExp(compareValue, "i");
+ for (var i = 0; i < options.length; i++) {
+ if(!isGlobal) {
+ str = new RegExp('(\\W|^)' + options[i] + '(\\W|$)');
+ }
+ if (rowValue.search(str) !== -1) {
+ return true;
+ }
+ }
+ return false;
+ };
+ break;
+ case 'boolean':
+ return function (origin, compareValue){
+ return origin === compareValue;
+ };
+ break;
+ case 'string':
+ default:
+ return function(origin, compareValue){
+ var regex = new RegExp(compareValue,"i");
+ return regex.test(origin);
+ }
+ }
+ }
+
+};
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job_view.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job_view.js
new file mode 100644
index 0000000..eae86c2
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/job_view.js
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+App.JobView = Ember.View.extend({});
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js
new file mode 100644
index 0000000..26124b5
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/jobs_view.js
@@ -0,0 +1,305 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+App.JobsView = App.TableView.extend({
+
+ templateName: 'jobs',
+
+ content: [],
+
+
+ /**
+ * If no jobs table rows to show.
+ */
+ noDataToShow: true,
+
+ filterCondition:[],
+
+ /*
+ If no jobs to display set noDataToShow to true, else set emptyData to false.
+ */
+ noDataToShowObserver: function () {
+ if(this.get("controller.content.length") > 0){
+ this.set("noDataToShow",false);
+ }else{
+ this.set("noDataToShow",true);
+ }
+ }.observes("controller.content.length"),
+
+ willInsertElement: function () {
+ this._super();
+ this.clearFilters();
+ this.onApplyIdFilter();
+ this.set('tableFilteringComplete', true);
+ },
+
+ didInsertElement: function () {
+ if(!this.get('controller.sortingColumn')){
+ var columns = this.get('childViews')[0].get('childViews');
+ if(columns && columns.findProperty('name', 'startTime')){
+ columns.findProperty('name','startTime').set('status', 'sorting_desc');
+ this.get('controller').set('sortingColumn', columns.findProperty('name','startTime'))
+ }
+ }
+ },
+
+ onApplyIdFilter: function() {
+ var isIdFilterApplied = this.get('controller.filterObject.isIdFilterApplied');
+ this.get('childViews').forEach(function(childView) {
+ if (childView['clearFilter'] && childView.get('column') != 1) {
+ if(isIdFilterApplied){
+ childView.clearFilter();
+ }
+ var childOfChild = childView.get('childViews')[0];
+ if(childOfChild){
+ Em.run.next(function() {
+ childOfChild.set('disabled', isIdFilterApplied);
+ })
+ }
+ }
+ });
+ }.observes('controller.filterObject.isIdFilterApplied'),
+
+ saveFilter: function () {
+ if(this.get('tableFilteringComplete')){
+ this.updateFilter(1, this.get('controller.filterObject.id'), 'string');
+ this.updateFilter(2, this.get('controller.filterObject.user'), 'string');
+ this.updateFilter(4, this.get('controller.filterObject.windowEnd'), 'date');
+ }
+ }.observes(
+ 'controller.filterObject.id',
+ 'controller.filterObject.user',
+ 'controller.filterObject.windowEnd'
+ ),
+
+ sortView: App.Sorts.wrapperView,
+
+ idSort: App.Sorts.fieldView.extend({
+ column: 1,
+ name: 'id',
+ displayName: Em.I18n.t('jobs.column.id'),
+ type: 'string'
+ }),
+
+ userSort: App.Sorts.fieldView.extend({
+ column: 2,
+ name: 'user',
+ displayName: Em.I18n.t('jobs.column.user'),
+ type: 'string'
+ }),
+
+ startTimeSort: App.Sorts.fieldView.extend({
+ column: 3,
+ name: 'startTime',
+ displayName: Em.I18n.t('jobs.column.start.time'),
+ type: 'number'
+ }),
+
+ endTimeSort: App.Sorts.fieldView.extend({
+ column: 4,
+ name: 'endTime',
+ displayName: Em.I18n.t('jobs.column.end.time'),
+ type: 'number'
+ }),
+
+ durationSort: App.Sorts.fieldView.extend({
+ column: 5,
+ name: 'duration',
+ displayName: Em.I18n.t('jobs.column.duration'),
+ type: 'number'
+ }),
+
+ /**
+ * Select View with list of "rows-per-page" options
+ * @type {Ember.View}
+ */
+ rowsPerPageSelectView: Ember.Select.extend({
+ content: ['10', '25', '50', '100', "250", "500"],
+ valueBinding: "controller.filterObject.jobsLimit",
+ attributeBindings: ['disabled'],
+ disabled: false,
+ disabledObserver: function () {
+ this.set('disabled', !!this.get("parentView.hasBackLinks"));
+ }.observes('parentView.hasBackLinks'),
+ change: function () {
+ this.get('controller').set('navIDs.nextID', '');
+ }
+ }),
+
+ /**
+ * return filtered number of all content number information displayed on the page footer bar
+ * @returns {String}
+ */
+ filteredJobs: function () {
+ return Em.I18n.t('jobs.filtered.jobs').fmt(this.get('controller.content.length'));
+ }.property('controller.content.length', 'controller.totalOfJobs'),
+
+ pageContentObserver: function () {
+ if (!this.get('controller.loading')) {
+ var tooltip = $('.tooltip');
+ if (tooltip.length) {
+ Ember.run.later(this, function() {
+ if (tooltip.length > 1) {
+ tooltip.first().remove();
+ }
+ }, 500);
+ }
+ }
+ }.observes('controller.loading'),
+
+ init: function() {
+ this._super();
+ App.tooltip($('body'), {
+ selector: '[rel="tooltip"]'
+ });
+ },
+
+ willDestroyElement : function() {
+ $('.tooltip').remove();
+ },
+
+ /**
+ * Filter-field for Jobs ID.
+ * Based on <code>filters</code> library
+ */
+ jobsIdFilterView: App.Filters.createTextView({
+ column: 1,
+ showApply: true,
+ setPropertyOnApply: 'controller.filterObject.id'
+ }),
+
+ /**
+ * Filter-list for User.
+ * Based on <code>filters</code> library
+ */
+ userFilterView: App.Filters.createTextView({
+ column: 2,
+ fieldType: 'input-small',
+ showApply: true,
+ setPropertyOnApply: 'controller.filterObject.user'
+ }),
+
+ /**
+ * Filter-field for Start Time.
+ * Based on <code>filters</code> library
+ */
+ startTimeFilterView: App.Filters.createSelectView({
+ fieldType: 'input-120',
+ column: 3,
+ content: ['Any', 'Past 1 hour', 'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days', 'Custom'],
+ valueBinding: "controller.filterObject.startTime",
+ onChangeValue: function () {
+ this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'date');
+ }
+ }),
+
+ jobNameView: Em.View.extend({
+
+ isLink: 'is-not-link',
+
+ isLinkObserver: function () {
+ this.refreshLinks();
+ }.observes('controller.sortingDone'),
+
+ refreshLinks: function () {
+ this.set('isLink', this.get('job.hasTezDag') ? "" : "is-not-link");
+ },
+
+ templateName: 'jobs/jobs_name',
+
+ click: function(event) {
+ /*if (this.get('job.hasTezDag')) {
+ App.router.transitionTo('main.jobs.jobDetails', this.get('job'));
+ }*/
+ return false;
+ },
+
+ didInsertElement: function () {
+ this.refreshLinks();
+ }
+ }),
+
+ /**
+ * associations between content (jobs list) property and column index
+ */
+ colPropAssoc: function () {
+ var associations = [];
+ associations[1] = 'id';
+ associations[2] = 'user';
+ associations[3] = 'startTime';
+ associations[4] = 'endTime';
+ return associations;
+ }.property(),
+
+ clearFilters: function() {
+ this.get('childViews').forEach(function(childView) {
+ if (childView['clearFilter']) {
+ childView.clearFilter();
+ }
+ });
+ },
+
+ jobFailMessage: function() {
+ return Em.I18n.t('jobs.table.job.fail');
+ }.property(),
+
+ jobsPaginationLeft: Ember.View.extend({
+ tagName: 'a',
+ templateName: 'table/navigation/pagination_left',
+ classNameBindings: ['class'],
+ class: function () {
+ if (this.get("parentView.hasBackLinks") && !this.get('controller.filterObject.isAnyFilterApplied')) {
+ return "paginate_previous";
+ }
+ return "paginate_disabled_previous";
+ }.property('parentView.hasBackLinks', 'controller.filterObject.isAnyFilterApplied'),
+
+ click: function () {
+ if (this.get("parentView.hasBackLinks") && !this.get('controller.filterObject.isAnyFilterApplied')) {
+ this.get('controller').navigateBack();
+ }
+ }
+ }),
+
+ jobsPaginationRight: Ember.View.extend({
+ tagName: 'a',
+ templateName: 'table/navigation/pagination_right',
+ classNameBindings: ['class'],
+ class: function () {
+ if (this.get("parentView.hasNextJobs") && !this.get('controller.filterObject.isAnyFilterApplied')) {
+ return "paginate_next";
+ }
+ return "paginate_disabled_next";
+ }.property("parentView.hasNextJobs", 'controller.filterObject.isAnyFilterApplied'),
+
+ click: function () {
+ if (this.get("parentView.hasNextJobs") && !this.get('controller.filterObject.isAnyFilterApplied')) {
+ this.get('controller').navigateNext();
+ }
+ }
+ }),
+
+ hasNextJobs: function() {
+ return (this.get("controller.navIDs.nextID.length") > 0);
+ }.property('controller.navIDs.nextID'),
+
+ hasBackLinks: function() {
+ return (this.get("controller.navIDs.backIDs").length > 1);
+ }.property('controller.navIDs.backIDs.[].length')
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/5659f0a2/contrib/views/jobs/src/main/resources/ui/app/scripts/views/sort_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/jobs/src/main/resources/ui/app/scripts/views/sort_view.js b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/sort_view.js
new file mode 100644
index 0000000..a8d5d39
--- /dev/null
+++ b/contrib/views/jobs/src/main/resources/ui/app/scripts/views/sort_view.js
@@ -0,0 +1,253 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Wrapper View for all sort components. Layout template and common actions are located inside of it.
+ * Logic specific for sort fields
+ * located in inner view - <code>fieldView</code>.
+ *
+ * @type {*}
+ */
+var wrapperView = Em.View.extend({
+ tagName: 'tr',
+
+ classNames: ['sort-wrapper'],
+
+ willInsertElement: function () {
+ if (this.get('parentView.tableFilteringComplete')) {
+ this.get('parentView').set('filteringComplete', true);
+ }
+ },
+
+ /**
+ * Load sort statuses from local storage
+ * Works only after finish filtering in the parent View
+ */
+ loadSortStatuses: function () {
+
+ }.observes('parentView.filteringComplete'),
+
+ /**
+ * Save sort statuses to local storage
+ * Works only after finish filtering in the parent View
+ */
+ saveSortStatuses: function () {
+ if (!this.get('parentView.filteringComplete')) return;
+
+ var statuses = [];
+ this.get('childViews').forEach(function (childView) {
+ statuses.push({
+ name: childView.get('name'),
+ status: childView.get('status')
+ });
+ });
+ },
+
+ /**
+ * sort content by property
+ * @param property {object}
+ * @param order {Boolean} true - DESC, false - ASC
+ * @param returnSorted {Boolean}
+ */
+ sort: function (property, order, returnSorted) {
+ var content = this.get('content').toArray();
+ var sortFunc = this.getSortFunc(property, order);
+ var status = order ? 'sorting_desc' : 'sorting_asc';
+
+ this.resetSort();
+ this.get('childViews').findProperty('name', property.get('name')).set('status', status);
+ this.saveSortStatuses(property, order);
+ content.sort(sortFunc);
+
+ if (!!returnSorted) {
+ return content;
+ } else {
+ this.set('content', content);
+ }
+ },
+
+ isSorting: false,
+
+ onContentChange: function () {
+ if (!this.get('isSorting') && this.get('content.length')) {
+ this.get('childViews').forEach(function (view) {
+ if (view.status !== 'sorting') {
+ var status = view.get('status');
+ this.set('isSorting', true);
+ this.sort(view, status == 'sorting_desc');
+ this.set('isSorting', false);
+ view.set('status', status);
+ }
+ }, this);
+ }
+ }.observes('content.length'),
+
+ /**
+ * reset all sorts fields
+ */
+ resetSort: function () {
+ this.get('childViews').setEach('status', 'sorting');
+ },
+ /**
+ * determines sort function depending on the type of sort field
+ * @param property
+ * @param order
+ * @return {*}
+ */
+ getSortFunc: function (property, order) {
+ var func;
+ switch (property.get('type')) {
+ case 'ip':
+ func = function (a, b) {
+ a = App.Helpers.misc.ipToInt(a.get(property.get('name')));
+ b = App.Helpers.misc.ipToInt(b.get(property.get('name')));
+ return order ? (b - a) : (a - b);
+ };
+ break;
+ case 'number':
+ func = function (a, b) {
+ a = parseFloat(a.get(property.get('name')));
+ b = parseFloat(b.get(property.get('name')));
+ return order ? (b - a) : (a - b);
+ };
+ break;
+ default:
+ func = function (a, b) {
+ if (order) {
+ if (a.get(property.get('name')) > b.get(property.get('name')))
+ return -1;
+ if (a.get(property.get('name')) < b.get(property.get('name')))
+ return 1;
+ return 0;
+ } else {
+ if (a.get(property.get('name')) < b.get(property.get('name')))
+ return -1;
+ if (a.get(property.get('name')) > b.get(property.get('name')))
+ return 1;
+ return 0;
+ }
+ }
+ }
+ return func;
+ }
+});
+
+/**
+ * view that carry on sorting on server-side via <code>refresh()</code> in parentView
+ * @type {*}
+ */
+var serverWrapperView = Em.View.extend({
+ tagName: 'tr',
+
+ classNames: ['sort-wrapper'],
+
+ willInsertElement: function () {
+ this.loadSortStatuses();
+ },
+
+ /**
+ * Initialize and save sorting statuses: publicHostName sorting_asc
+ */
+ loadSortStatuses: function () {
+ var statuses = [];
+ var childViews = this.get('childViews');
+ childViews.forEach(function (childView) {
+ var sortStatus = (childView.get('name') == 'publicHostName' && childView.get('status') == 'sorting') ? 'sorting_asc' : childView.get('status');
+ statuses.push({
+ name: childView.get('name'),
+ status: sortStatus
+ });
+ childView.set('status', sortStatus);
+ });
+ this.get('controller').set('sortingColumn', childViews.findProperty('name', 'publicHostName'));
+ },
+
+ /**
+ * Save sort statuses to local storage
+ * Works only after finish filtering in the parent View
+ */
+ saveSortStatuses: function () {
+ var statuses = [];
+ this.get('childViews').forEach(function (childView) {
+ statuses.push({
+ name: childView.get('name'),
+ status: childView.get('status')
+ });
+ });
+ },
+
+ /**
+ * sort content by property
+ * @param property {object}
+ * @param order {Boolean} true - DESC, false - ASC
+ */
+ sort: function (property, order) {
+ var status = order ? 'sorting_desc' : 'sorting_asc';
+
+ this.resetSort();
+ this.get('childViews').findProperty('name', property.get('name')).set('status', status);
+ this.saveSortStatuses();
+ this.get('parentView').refresh();
+ },
+
+ /**
+ * reset all sorts fields
+ */
+ resetSort: function () {
+ this.get('childViews').setEach('status', 'sorting');
+ }
+});
+
+/**
+ * particular view that contain sort field properties:
+ * name - name of property in content table
+ * type(optional) - specific type to sort
+ * displayName - label to display
+ * @type {*}
+ */
+var fieldView = Em.View.extend({
+ templateName: 'sort_field_template',
+ classNameBindings: ['viewNameClass'],
+ tagName: 'th',
+ name: null,
+ displayName: null,
+ status: 'sorting',
+ viewNameClass: function () {
+ return 'sort-view-' + this.get('column');
+ }.property(),
+ type: null,
+ column: 0,
+ /**
+ * callback that run sorting and define order of sorting
+ * @param event
+ */
+ click: function (event) {
+ this.get('parentView').sort(this, (this.get('status') !== 'sorting_desc'));
+ this.get('controller').set('sortingColumn', this);
+ }
+});
+
+/**
+ * Result object, which will be accessible outside
+ * @type {Object}
+ */
+App.Sorts = {
+ serverWrapperView: serverWrapperView,
+ wrapperView: wrapperView,
+ fieldView: fieldView
+};
[3/3] git commit: Merge remote-tracking branch 'origin/trunk' into
trunk
Posted by on...@apache.org.
Merge remote-tracking branch 'origin/trunk' into trunk
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/366bcbb6
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/366bcbb6
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/366bcbb6
Branch: refs/heads/trunk
Commit: 366bcbb6998a87dbd59bd4defabff266079c9461
Parents: 5659f0a 39a92eb
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Fri Jul 18 17:21:11 2014 +0300
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Fri Jul 18 17:21:11 2014 +0300
----------------------------------------------------------------------
.../resources/AlertDefResourceDefinition.java | 44 ++++
.../resources/ClusterResourceDefinition.java | 1 +
.../resources/ResourceInstanceFactoryImpl.java | 4 +
.../api/services/AlertDefinitionService.java | 93 ++++++++
.../server/api/services/AmbariMetaInfo.java | 43 ++++
.../server/api/services/ClusterService.java | 9 +
.../server/api/util/StackExtensionHelper.java | 12 +-
.../ambari/server/controller/AmbariServer.java | 3 +
.../AbstractControllerResourceProvider.java | 2 +
.../AlertDefinitionResourceProvider.java | 178 ++++++++++++++
.../ambari/server/controller/spi/Resource.java | 6 +-
.../server/orm/dao/AlertDefinitionDAO.java | 27 ++-
.../ambari/server/orm/dao/AlertDispatchDAO.java | 230 ++++++++++++++++++-
.../apache/ambari/server/orm/dao/AlertsDAO.java | 138 +++++++++++
.../server/orm/entities/AlertCurrentEntity.java | 27 +++
.../orm/entities/AlertDefinitionEntity.java | 78 ++++++-
.../server/orm/entities/AlertGroupEntity.java | 46 +++-
.../server/orm/entities/AlertHistoryEntity.java | 36 ++-
.../server/orm/entities/AlertNoticeEntity.java | 29 +++
.../server/orm/entities/AlertTargetEntity.java | 38 ++-
.../apache/ambari/server/state/ServiceInfo.java | 18 +-
.../server/state/alert/AlertDefinition.java | 117 ++++++++++
.../ambari/server/state/alert/MetricAlert.java | 57 +++++
.../apache/ambari/server/state/alert/Scope.java | 33 +++
.../ambari/server/state/alert/SourceType.java | 40 ++++
.../resources/Ambari-DDL-Postgres-CREATE.sql | 1 +
.../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql | 1 +
.../src/main/resources/key_properties.json | 4 +
.../src/main/resources/properties.json | 11 +
.../stacks/HDP/2.0.6/services/HDFS/alerts.json | 59 +++++
.../ClusterResourceDefinitionTest.java | 3 +-
.../server/api/services/AmbariMetaInfoTest.java | 22 ++
.../AlertDefinitionResourceProviderTest.java | 162 +++++++++++++
.../server/orm/dao/AlertDefinitionDAOTest.java | 6 +-
.../stacks/HDP/2.0.5/services/HDFS/alerts.json | 52 +++++
35 files changed, 1594 insertions(+), 36 deletions(-)
----------------------------------------------------------------------