You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by sh...@apache.org on 2018/06/01 05:43:11 UTC
[kylin] 01/08: KYLIN-3373 Some improvements for lookup table - UI
part change
This is an automated email from the ASF dual-hosted git repository.
shaofengshi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kylin.git
commit f0f588f31f26d0f35c789ec79212f2df0cfa6e42
Author: liapan <li...@ebay.com>
AuthorDate: Thu May 10 11:54:27 2018 +0800
KYLIN-3373 Some improvements for lookup table - UI part change
---
webapp/app/js/controllers/cubeAdvanceSetting.js | 56 +++++++++
webapp/app/js/controllers/cubes.js | 127 +++++++++++++++++++++
webapp/app/js/controllers/sourceMeta.js | 28 +++++
webapp/app/js/directives/select.js | 2 +-
webapp/app/js/model/cubeConfig.js | 4 +
webapp/app/js/model/tableConfig.js | 10 +-
webapp/app/js/services/cubes.js | 3 +-
webapp/app/js/services/tables.js | 3 +-
webapp/app/less/app.less | 7 ++
.../partials/cubeDesigner/advanced_settings.html | 99 ++++++++++++++++
webapp/app/partials/cubes/cubes.html | 2 +
webapp/app/partials/jobs/lookup_refresh.html | 71 ++++++++++++
webapp/app/partials/tables/table_detail.html | 50 +++++++-
13 files changed, 457 insertions(+), 5 deletions(-)
diff --git a/webapp/app/js/controllers/cubeAdvanceSetting.js b/webapp/app/js/controllers/cubeAdvanceSetting.js
index 5687e12..211b4c8 100755
--- a/webapp/app/js/controllers/cubeAdvanceSetting.js
+++ b/webapp/app/js/controllers/cubeAdvanceSetting.js
@@ -445,4 +445,60 @@ KylinApp.controller('CubeAdvanceSettingCtrl', function ($scope, $modal,cubeConfi
$scope.$emit('AdvancedSettingEdited');
});
}
+
+ $scope.newSnapshot = {
+ select: {}
+ };
+
+ $scope.removeSnapshotTable = function(index) {
+ $scope.cubeMetaFrame.snapshot_table_desc_list.splice(index, 1);
+ };
+
+ $scope.addSnapshot = function(newSnapshot) {
+ if (!newSnapshot.table_name || !newSnapshot.storage_type) {
+ swal('Oops...', 'Snapshot table name or storage should not be empty', 'warning');
+ return;
+ } else if ($scope.cubeMetaFrame.snapshot_table_desc_list.length){
+ var existSnapshot = _.find($scope.cubeMetaFrame.snapshot_table_desc_list, function(snapshot){ return snapshot.table_name === newSnapshot.table_name;});
+ if (!!existSnapshot) {
+ swal('Oops...', 'Snapshot table already existed', 'warning');
+ return;
+ }
+ }
+ $scope.cubeMetaFrame.snapshot_table_desc_list.push(angular.copy(newSnapshot));
+ $scope.newSnapshot.select = {};
+ };
+
+ $scope.changeSnapshotStorage = function(snapshot) {
+ if (snapshot.storage_type == 'hbase') {
+ snapshot.global = true;
+ }
+ };
+
+ $scope.changeSnapshotTable = function(changeSnapshot, beforeTableName, snapshotTableDescList) {
+ var existSnapshot = _.find(snapshotTableDescList, function(snapshot) {
+ return snapshot.table_name === changeSnapshot.table_name;
+ });
+ if (!!existSnapshot) {
+ changeSnapshot.table_name = beforeTableName;
+ swal('Oops...', 'Snapshot table already existed', 'warning');
+ }
+ };
+
+ $scope.getCubeLookups = function() {
+ var modelDesc = modelsManager.getModel($scope.cubeMetaFrame.model_name);
+ var modelLookups = modelDesc ? modelDesc.lookups : [];
+ var cubeLookups = [];
+ angular.forEach(modelLookups, function(modelLookup, index) {
+ var dimensionLookup = _.find($scope.cubeMetaFrame.dimensions, function(dimension){ return dimension.table === modelLookup.alias;});
+ if (!!dimensionLookup) {
+ if (cubeLookups.indexOf(modelLookup.table) === -1) {
+ cubeLookups.push(modelLookup.table);
+ }
+ }
+ });
+ return cubeLookups;
+ };
+
+ $scope.cubeLookups = $scope.getCubeLookups();
});
diff --git a/webapp/app/js/controllers/cubes.js b/webapp/app/js/controllers/cubes.js
index cbd6fad..136d86e 100644
--- a/webapp/app/js/controllers/cubes.js
+++ b/webapp/app/js/controllers/cubes.js
@@ -542,6 +542,27 @@ KylinApp.controller('CubesCtrl', function ($scope, $q, $routeParams, $location,
});
};
+ $scope.startLookupRefresh = function(cube) {
+ $scope.loadDetail(cube).then(function () {
+ $scope.metaModel={
+ model:cube.model
+ };
+ $modal.open({
+ templateUrl: 'lookupRefresh.html',
+ controller: lookupRefreshCtrl,
+ resolve: {
+ cube: function () {
+ return cube;
+ },
+ scope:function(){
+ return $scope;
+ }
+ }
+ });
+ }
+ );
+ };
+
});
@@ -780,3 +801,109 @@ var deleteSegmentCtrl = function($scope, $modalInstance, CubeService, SweetAlert
});
};
};
+
+var lookupRefreshCtrl = function($scope, scope, CubeList, $modalInstance, CubeService, cube, SweetAlert, loadingRequest) {
+ $scope.cubeList = CubeList;
+ $scope.cube = cube;
+ $scope.dispalySegment = false;
+
+ $scope.getLookups = function() {
+ var modelLookups = cube.model ? cube.model.lookups : [];
+ var cubeLookups = [];
+ angular.forEach(modelLookups, function(modelLookup, index) {
+ var dimensionTables = _.find(cube.detail.dimensions, function(dimension){ return dimension.table === modelLookup.alias;});
+ if (!!dimensionTables) {
+ if (cubeLookups.indexOf(modelLookup.table) === -1) {
+ cubeLookups.push(modelLookup.table);
+ }
+ }
+ });
+ return cubeLookups;
+ };
+
+ $scope.cubeLookups = $scope.getLookups();
+
+ $scope.lookup = {
+ select: {}
+ };
+
+ $scope.getReadySegment = function(segment) {
+ return segment.status === 'READY';
+ };
+
+ $scope.cancel = function () {
+ $modalInstance.dismiss('cancel');
+ };
+
+ $scope.updateLookupTable = function(tableName) {
+ var lookupTable = _.find(cube.detail.snapshot_table_desc_list, function(table){ return table.table_name == tableName});
+ if (!!lookupTable && lookupTable.global) {
+ $scope.dispalySegment = false;
+ $scope.lookup.select.segments = [];
+ } else {
+ $scope.dispalySegment = true;
+ }
+ };
+
+ $scope.selectAllSegments = function(allSegments) {
+ if (allSegments) {
+ $scope.lookup.select.segments = $scope.cube.segments;
+ } else {
+ $scope.lookup.select.segments = [];
+ }
+ };
+
+ $scope.refresh = function() {
+ if (!$scope.lookup.select.table_name) {
+ SweetAlert.swal('Warning', 'Lookup table should not be empty', 'warning');
+ return;
+ }
+
+ // cube advance lookup table
+ var lookupTable = _.find(cube.detail.snapshot_table_desc_list, function(table){ return table.table_name == $scope.lookup.select.table_name});
+ if (!!lookupTable) {
+ if (!lookupTable.global && $scope.lookup.select.segments.length == 0) {
+ SweetAlert.swal('Warning', 'Segment should not be empty', 'warning');
+ return;
+ }
+ } else {
+ // cube lookup table
+ lookupTable = _.find($scope.cubeLookups, function(table){ return table == $scope.lookup.select.table_name});
+ if (!lookupTable) {
+ SweetAlert.swal('Warning', 'Lookup table not existed in cube', 'warning');
+ return;
+ } else {
+ if ($scope.lookup.select.segments.length == 0) {
+ SweetAlert.swal('Warning', 'Segment should not be empty', 'warning');
+ return;
+ }
+ }
+ }
+
+ var lookupSnapshotBuildRequest = {
+ lookupTableName: $scope.lookup.select.table_name,
+ segmentIDs: _.map($scope.lookup.select.segments, function(segment){ return segment.uuid})
+ };
+
+ loadingRequest.show();
+ CubeService.lookupRefresh({cubeId: cube.name}, lookupSnapshotBuildRequest, function (job) {
+ loadingRequest.hide();
+ $modalInstance.dismiss('cancel');
+ SweetAlert.swal('Success!', 'Lookup refresh job was submitted successfully', 'success');
+ scope.refreshCube(cube).then(function(_cube){
+ $scope.cubeList.cubes[$scope.cubeList.cubes.indexOf(cube)] = _cube;
+ });
+ }, function (e) {
+ loadingRequest.hide();
+ if (e.data && e.data.exception) {
+ var message = e.data.exception;
+
+ var msg = !!(message) ? message : 'Failed to take action.';
+ SweetAlert.swal('Oops...', msg, 'error');
+ } else {
+ SweetAlert.swal('Oops...', "Failed to take action.", 'error');
+ }
+ });
+ };
+
+};
diff --git a/webapp/app/js/controllers/sourceMeta.js b/webapp/app/js/controllers/sourceMeta.js
index 8a795d5..49d8998 100755
--- a/webapp/app/js/controllers/sourceMeta.js
+++ b/webapp/app/js/controllers/sourceMeta.js
@@ -925,3 +925,31 @@ KylinApp
});
+/*snapshot controller*/
+KylinApp
+ .controller('TableSnapshotCtrl', function ($scope, TableService, CubeService, uiGridConstants) {
+ $scope.initSnapshots = function() {
+ var tableFullName = $scope.tableModel.selectedSrcTable.database + '.' + $scope.tableModel.selectedSrcTable.name
+ TableService.getSnapshots({tableName: tableFullName, pro: $scope.projectModel.selectedProject}, {}, function (data) {
+ var orgData = JSON.parse(angular.toJson(data));
+ angular.forEach(orgData, function(snapshot) {
+ if(!!snapshot.cubesAndSegmentsUsage && snapshot.cubesAndSegmentsUsage.length > 0) {
+ snapshot.usageInfo = '';
+ angular.forEach(snapshot.cubesAndSegmentsUsage, function(info) {
+ snapshot.usageInfo += info;
+ snapshot.usageInfo += '</br>';
+ });
+ } else {
+ snapshot.usageInfo = 'No Usage Info';
+ }
+ });
+ $scope.tableSnapshots = orgData;
+ });
+ };
+ $scope.$watch('tableModel.selectedSrcTable', function (newValue, oldValue) {
+ if (!newValue || !newValue.name) {
+ return;
+ }
+ $scope.initSnapshots();
+ });
+ });
\ No newline at end of file
diff --git a/webapp/app/js/directives/select.js b/webapp/app/js/directives/select.js
index 7327af9..038cf57 100644
--- a/webapp/app/js/directives/select.js
+++ b/webapp/app/js/directives/select.js
@@ -1627,7 +1627,7 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
}
}
}
- if (angular.equals(result.toUpperCase(),value.toUpperCase())){
+ if (angular.equals((typeof result =='string') ? result.toUpperCase() : result, (typeof value == 'string') ? value.toUpperCase() : value)) {
resultMultiple.unshift(list[p]);
return true;
}
diff --git a/webapp/app/js/model/cubeConfig.js b/webapp/app/js/model/cubeConfig.js
index e163d75..eacb915 100644
--- a/webapp/app/js/model/cubeConfig.js
+++ b/webapp/app/js/model/cubeConfig.js
@@ -114,6 +114,10 @@ KylinApp.constant('cubeConfig', {
{name:"Segment Dictionary", value:"org.apache.kylin.dict.global.SegmentAppendTrieDictBuilder"}
],
needSetLengthEncodingList:['fixed_length','fixed_length_hex','int','integer'],
+ snapshotStorageTypes: [
+ {name: 'Meta Store', value: 'metaStore'},
+ {name: 'HBase', value: 'hbase'}
+ ],
baseChartOptions: {
chart: {
type: 'sunburstChart',
diff --git a/webapp/app/js/model/tableConfig.js b/webapp/app/js/model/tableConfig.js
index 3989531..0dc4a52 100644
--- a/webapp/app/js/model/tableConfig.js
+++ b/webapp/app/js/model/tableConfig.js
@@ -121,7 +121,15 @@ KylinApp.constant('tableConfig', {
"fixed_length_hex",
"integer"
]
- }
+ },
+ snapshotTheaditems: [
+ {attr: 'snapshotID', name: 'ID'},
+ {attr: 'storageType', name: 'Storage Type'},
+ {attr: 'lastBuildTime', name: 'Last Build Time'},
+ {attr: 'sourceTableLastModifyTime', name: 'Source Table Last Modify Time'},
+ {attr: 'sourceTableSize', name: 'Size'},
+ {attr: 'usageInfo', name: 'Useage Info'}
+ ]
});
diff --git a/webapp/app/js/services/cubes.js b/webapp/app/js/services/cubes.js
index 6140521..537e5c1 100644
--- a/webapp/app/js/services/cubes.js
+++ b/webapp/app/js/services/cubes.js
@@ -79,6 +79,7 @@ KylinApp.factory('CubeService', ['$resource', function ($resource, config) {
}
},
optimize: {method: 'PUT', params: {action: 'optimize'}, isArray: false},
- autoMigrate: {method: 'POST', params: {action: 'migrate'}, isArray: false}
+ autoMigrate: {method: 'POST', params: {action: 'migrate'}, isArray: false},
+ lookupRefresh: {method: 'PUT', params: {action: 'refresh_lookup'}, isArray: false}
});
}]);
diff --git a/webapp/app/js/services/tables.js b/webapp/app/js/services/tables.js
index 7d8bc1a..9a62cf8 100755
--- a/webapp/app/js/services/tables.js
+++ b/webapp/app/js/services/tables.js
@@ -24,6 +24,7 @@ KylinApp.factory('TableService', ['$resource', function ($resource, config) {
unLoadHiveTable: {method: 'DELETE', params: {}, isArray: false},
genCardinality: {method: 'PUT', params: {action: 'cardinality'}, isArray: false},
showHiveDatabases: {method: 'GET', params: {action:'hive'}, cache: true, isArray: true},
- showHiveTables: {method: 'GET', params: {action:'hive'}, cache: true, isArray: true}
+ showHiveTables: {method: 'GET', params: {action:'hive'}, cache: true, isArray: true},
+ getSnapshots: {method: 'GET', params: {action: 'snapshots'}, isArray: true}
});
}]);
diff --git a/webapp/app/less/app.less b/webapp/app/less/app.less
index eca76ff..7aa7283 100644
--- a/webapp/app/less/app.less
+++ b/webapp/app/less/app.less
@@ -929,4 +929,11 @@ pre {
div[title="Cube Info Detail"].popover {
max-width: 1024px;
with: min-content;
+}
+/*snapshot usage info tooltip*/
+td.snapshot-usage .tooltip {
+ font-size: 16px;
+}
+td.snapshot-usage .tooltip-inner {
+ max-width: 1024px;
}
\ No newline at end of file
diff --git a/webapp/app/partials/cubeDesigner/advanced_settings.html b/webapp/app/partials/cubeDesigner/advanced_settings.html
index 7b9193b..fb7a1b1 100755
--- a/webapp/app/partials/cubeDesigner/advanced_settings.html
+++ b/webapp/app/partials/cubeDesigner/advanced_settings.html
@@ -503,6 +503,99 @@
<button class="btn btn-link" ng-click="clearNewDictionaries()">Cancel</button>
</div>
</div>
+ <!-- Advanced Lookup Table-->
+ <div class="form-group large-popover" style="margin-bottom:30px;">
+ <h3 style="margin-left:42px;margin-bottom:30px;">Advanced Snapshot Table <i kylinpopover placement="right" title="Cube Engine" template="AdvanceSnapshotTableTip.html" class="fa fa-info-circle"></i></h3>
+ <div style="margin-left:42px">
+ <!-- edit mode-->
+ <div ng-if="state.mode=='edit'" class="box-body">
+ <table class="table table-hover table-bordered list" style="table-layout: fixed;margin-left:42px;width:92%;">
+ <thead>
+ <tr>
+ <th style="width:60%">Snapshot Table</th>
+ <th style="width:25%">Type</th>
+ <th style="width:10%">Global</th>
+ <th style="width:5%"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="snapshot in cubeMetaFrame.snapshot_table_desc_list track by $index">
+ <td>
+ <select style="width:95%" chosen ng-model="snapshot.table_name"
+ ng-change="changeSnapshotTable(snapshot, '{{snapshot.table_name}}', {{cubeMetaFrame.snapshot_table_desc_list}})"
+ ng-options="tableName as tableName for tableName in cubeLookups">
+ <option value=""></option>
+ </select>
+ </td>
+ <td>
+ <select style="width:95%" chosen ng-model="snapshot.storage_type"
+ ng-change="changeSnapshotStorage(snapshot)"
+ ng-options="storageType.value as storageType.name for storageType in cubeConfig.snapshotStorageTypes">
+ <option value=""></option>
+ </select>
+ </td>
+ <td>
+ <input type="checkbox" ng-model="snapshot.global" ng-disabled="(snapshot.storage_type == 'hbase')">
+ </td>
+ <td>
+ <button class="btn btn-xs btn-info" ng-click="removeSnapshotTable($index)">
+ <i class="fa fa-minus"></i>
+ </button>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <select style="width:95%" chosen ng-model="newSnapshot.select.table_name"
+ ng-options="tableName as tableName for tableName in cubeLookups">
+ <option value=""></option>
+ </select>
+ </td>
+ <td>
+ <select style="width:95%" chosen ng-model="newSnapshot.select.storage_type"
+ ng-change="changeSnapshotStorage(newSnapshot.select)"
+ ng-options="storageType.value as storageType.name for storageType in cubeConfig.snapshotStorageTypes">
+ <option value=""></option>
+ </select>
+ </td>
+ <td>
+ <input type="checkbox" ng-model="newSnapshot.select.global" ng-disabled="(newSnapshot.select.storage_type == 'hbase')">
+ </td>
+ <td>
+ <button class="btn btn-xs btn-info" ng-click="addSnapshot(newSnapshot.select)">
+ <i class="fa fa-plus"></i>
+ </button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!-- view-->
+ <div ng-if="state.mode=='view'" class="box-body">
+ <table class="table table-hover table-bordered list" style="table-layout: fixed;margin-left:42px;width:92%;">
+ <thead>
+ <tr>
+ <th style="width:60%">Snapshot Table</th>
+ <th style="width:25%">Type</th>
+ <th style="width:10%">Global</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="snapshot in cubeMetaFrame.snapshot_table_desc_list track by $index">
+ <td>
+ <p>{{snapshot.table_name}}</p>
+ </td>
+ <td>
+ <p>{{snapshot.storage_type}}</p>
+ </td>
+ <td>
+ <input type="checkbox" ng-model="snapshot.global" disabled="true">
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
<!--Edit ColumnFamily-->
<div class="form-group large-popover" >
<h3 style="margin-left:42px">Advanced ColumnFamily <i kylinpopover placement="right" title="Advanced ColumnFamily" template="AdvancedColumnFamilyTip.html" class="fa fa-info-circle"></i></h3>
@@ -647,3 +740,9 @@
</h4>
</div>
</script>
+
+<script type="text/ng-template" id="AdvanceSnapshotTableTip.html">
+ <div>
+ <h4>Advance snapshot design for global lookup table and provide different storage type.</h4>
+ </div>
+</script>
diff --git a/webapp/app/partials/cubes/cubes.html b/webapp/app/partials/cubes/cubes.html
index 3fd5e61..21b7a34 100644
--- a/webapp/app/partials/cubes/cubes.html
+++ b/webapp/app/partials/cubes/cubes.html
@@ -95,6 +95,7 @@
<li ng-if="cube.status!='DESCBROKEN'"><a ng-click="startJobSubmit(cube);">Build</a></li>
<li ng-if="cube.status!='DESCBROKEN'"><a ng-click="startRefresh(cube)">Refresh</a></li>
<li ng-if="cube.status!='DESCBROKEN'"><a ng-click="startMerge(cube)">Merge</a></li>
+ <li ng-if="cube.status!='DESCBROKEN'"><a ng-click="startLookupRefresh(cube);">Lookup Refresh</a></li>
<li ng-if="cube.status=='READY' && (userService.hasRole('ROLE_ADMIN') || hasPermission('cube',cube, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask))"><a ng-click="disable(cube)">Disable</a></li>
<li ng-if="cube.status=='DISABLED' && (userService.hasRole('ROLE_ADMIN') || hasPermission('cube',cube, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask))"><a ng-click="enable(cube)">Enable</a></li>
<li ng-if="cube.status=='DISABLED' && (userService.hasRole('ROLE_ADMIN') || hasPermission('cube',cube, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask))"><a ng-click="startDeleteSegment(cube)">Delete Segment</a></li>
@@ -146,4 +147,5 @@
<div ng-include="'partials/models/model_detail.html'"></div>
<div ng-include="'partials/cubes/cube_clone.html'"></div>
<div ng-include="'partials/cubes/cube_delete_segment.html'"></div>
+<div ng-include="'partials/jobs/lookup_refresh.html'"></div>
</div>
diff --git a/webapp/app/partials/jobs/lookup_refresh.html b/webapp/app/partials/jobs/lookup_refresh.html
new file mode 100644
index 0000000..ce8bbf9
--- /dev/null
+++ b/webapp/app/partials/jobs/lookup_refresh.html
@@ -0,0 +1,71 @@
+<!--
+* 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.
+-->
+
+<script type="text/ng-template" id="lookupRefresh.html">
+ <div class="modal-header">
+ <h4 tooltip="refresh">LOOKUP REFRESH CONFIRM</h4>
+ </div>
+ <div class="modal-body" style="background-color: white">
+ <div ng-if="!!cube.detail.snapshot_table_desc_list" class="row">
+ <div class="col-md-2"></div>
+ <div class="col-md-8">
+ <table class="table table-striped list">
+ <tbody>
+ <tr>
+ <td style="width:30%">Lookup Table</td>
+ <td style="width:70%" colspan="2">
+ <select style="width:95%" chosen ng-model="lookup.select.table_name"
+ ng-change="updateLookupTable(lookup.select.table_name)"
+ ng-options="lookup as lookup for lookup in cubeLookups">
+ <option value=""></option>
+ </select>
+ </td>
+ </tr>
+ <tr ng-if="dispalySegment">
+ <td style="width:30%">Segments</td>
+ <td style="width:60%">
+ <ui-select multiple ng-model="lookup.select.segments" theme="bootstrap" sortable="true" close-on-select="false" class="form-control">
+ <ui-select-match placeholder="Select Segments...">{{$item.name}}</ui-select-match>
+ <ui-select-choices repeat="segment in cube.segments | filter:getReadySegment">
+ <div ng-bind-html="segment.name | highlight: $select.search"></div>
+ </ui-select-choices>
+ </ui-select>
+ </td>
+ <td style="width:10%">
+ <input type="checkbox" ng-change="selectAllSegments(allSegments)" ng-model="allSegments"> All
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div class="col-md-2"></div>
+ </div>
+ <div ng-if="!cube.detail.snapshot_table_desc_list" class="row">
+ <div class="col-md-2"></div>
+ <div class="col-md-8">
+ <span>No lookup table defined. Please configurate lookup in model</span>
+ </div>
+ <div class="col-md-2"></div>
+ </div>
+ </div>
+
+ <div class="modal-footer">
+ <button class="btn btn-primary" ng-click="cancel()">Close</button>
+ <button ng-if="!!cube.detail.snapshot_table_desc_list" class="btn btn-success" ng-click="refresh()">Submit</button>
+ </div>
+</script>
\ No newline at end of file
diff --git a/webapp/app/partials/tables/table_detail.html b/webapp/app/partials/tables/table_detail.html
index d8209a8..441e093 100644
--- a/webapp/app/partials/tables/table_detail.html
+++ b/webapp/app/partials/tables/table_detail.html
@@ -35,6 +35,9 @@
<li>
<a data-toggle="tab" href="#access">Access</a>
</li>
+ <li>
+ <a data-toggle="tab" href="#snapshot">Snapshot</a>
+ </li>
</ul>
<div class="tab-content">
<!--Schema-->
@@ -184,8 +187,53 @@
</div>
</div>
</div>
+ </div>
-
+ <!--snapshot-->
+ <div id="snapshot" class="tab-pane" ng-controller="TableSnapshotCtrl">
+ <div ng-if="tableSnapshots.length > 0">
+ <table class="table table-hover table-striped list">
+ <thead>
+ <tr style="cursor: pointer">
+ <th ng-repeat="theaditem in tableConfig.snapshotTheaditems"
+ ng-click="state.filterAttr= theaditem.attr;state.reverseColumn=theaditem.attr;state.filterReverse=!state.filterReverse;">
+ {{theaditem.name}}
+ <i ng-if="state.reverseColumn!= theaditem.attr"
+ class="fa fa-unsorted"></i>
+ <i ng-if="state.reverseColumn== theaditem.attr && !state.filterReverse"
+ class="fa fa-sort-asc"></i>
+ <i ng-if="state.reverseColumn== theaditem.attr && state.filterReverse"
+ class="fa fa-sort-desc"></i>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="snapshot in tableSnapshots | filter: columnName | orderObjectBy:state.filterAttr:state.filterReverse">
+ <td style="{{(snapshot.snapshotID == snapshot.snapshotID)? 'background-color:#EBF9FE':''}}">
+ {{snapshot.snapshotID}}
+ </td>
+ <td style="{{(snapshot.snapshotID == snapshot.snapshotID)? 'background-color:#EBF9FE':''}}">
+ {{snapshot.storageType}}
+ </td>
+ <td style="{{(snapshot.snapshotID == snapshot.snapshotID)? 'background-color:#EBF9FE':''}}">
+ {{snapshot.lastBuildTime | utcToConfigTimeZone}}
+ </td>
+ <td style="{{(snapshot.snapshotID == snapshot.snapshotID)? 'background-color:#EBF9FE':''}}">
+ {{snapshot.sourceTableLastModifyTime | utcToConfigTimeZone}}
+ </td>
+ <td style="{{(snapshot.snapshotID == snapshot.snapshotID)? 'background-color:#EBF9FE':''}}">
+ {{snapshot.sourceTableSize | bytes}}
+ </td>
+ <td style="{{(snapshot.snapshotID == snapshot.snapshotID)? 'background-color:#EBF9FE':''}}" class="snapshot-usage">
+ <i class="fa fa-list text-aqua" style="cursor: pointer;" aria-hidden="true" tooltip-placement="left" tooltip-html-unsafe="<div style='text-align:left'>{{snapshot.usageInfo}}</div>"></i>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div ng-if="tableSnapshots.length == 0">
+ <div no-result text="No Snapshot Info."></div>
+ </div>
</div>
</div>
--
To stop receiving notification emails like this one, please contact
shaofengshi@apache.org.