You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by ma...@apache.org on 2015/05/15 07:21:56 UTC

[27/52] [abbrv] incubator-kylin git commit: new model page

new model page


Project: http://git-wip-us.apache.org/repos/asf/incubator-kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-kylin/commit/c3394d6e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-kylin/tree/c3394d6e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-kylin/diff/c3394d6e

Branch: refs/heads/0.8.0
Commit: c3394d6e157f9dc4127224b19401e16d38ea34bd
Parents: 4c456a2
Author: jiazhong <ji...@ebay.com>
Authored: Wed Mar 18 20:46:34 2015 +0800
Committer: honma <ho...@ebay.com>
Committed: Fri May 15 11:36:51 2015 +0800

----------------------------------------------------------------------
 .../kylin/rest/controller/CubeController.java   |   4 +-
 .../kylin/rest/controller/ModelController.java  |   2 +-
 .../apache/kylin/rest/service/CubeService.java  |  25 ++--
 .../rest/controller/CubeControllerTest.java     |   4 +-
 .../kylin/rest/service/CubeServiceTest.java     |   4 +-
 webapp/app/index.html                           |   6 +-
 webapp/app/js/app.js                            |   2 +-
 webapp/app/js/controllers/job.js                |   2 +-
 webapp/app/js/controllers/models.js             |  41 +++++--
 webapp/app/js/model/modelList.js                |   9 +-
 webapp/app/less/app.less                        |   2 +-
 webapp/app/less/component.less                  |  51 ++++++++
 webapp/app/partials/models/_models.html         | 117 +++++++++++++++++++
 webapp/app/partials/models/models.html          | 102 +---------------
 webapp/app/partials/models/models_info.html     |  46 ++++++++
 webapp/app/partials/models/models_tree.html     |  47 ++++++++
 webapp/bower.json                               |   5 +-
 17 files changed, 338 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java b/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java
index d4cef18..bc8f09a 100644
--- a/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java
+++ b/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java
@@ -89,8 +89,8 @@ public class CubeController extends BasicController {
     @RequestMapping(value = "", method = {RequestMethod.GET})
     @ResponseBody
     @Metered(name = "listCubes")
-    public List<CubeInstance> getCubes(@RequestParam(value = "cubeName", required = false) String cubeName, @RequestParam(value = "projectName", required = false) String projectName, @RequestParam("limit") Integer limit, @RequestParam("offset") Integer offset) {
-        return cubeService.getCubes(cubeName, projectName, (null == limit) ? 20 : limit, offset);
+    public List<CubeInstance> getCubes(@RequestParam(value = "cubeName", required = false) String cubeName,@RequestParam(value = "modelName", required = false) String modelName, @RequestParam(value = "projectName", required = false) String projectName, @RequestParam("limit") Integer limit, @RequestParam("offset") Integer offset) {
+        return cubeService.getCubes(cubeName, projectName, modelName,(null == limit) ? 20 : limit, offset);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java b/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java
index ce53474..97a5d32 100644
--- a/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java
+++ b/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java
@@ -123,7 +123,7 @@ public class ModelController extends BasicController {
 
         String descData = JsonUtil.writeValueAsIndentString(modelDesc);
         modelRequest.setModelDescData(descData);
-        modelRequest.setSuccessful(true);
+
         return modelRequest;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/server/src/main/java/org/apache/kylin/rest/service/CubeService.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/kylin/rest/service/CubeService.java b/server/src/main/java/org/apache/kylin/rest/service/CubeService.java
index d786b1e..8dc0345 100644
--- a/server/src/main/java/org/apache/kylin/rest/service/CubeService.java
+++ b/server/src/main/java/org/apache/kylin/rest/service/CubeService.java
@@ -81,7 +81,7 @@ public class CubeService extends BasicService {
     private AccessService accessService;
 
     @PostFilter(Constant.ACCESS_POST_FILTER_READ)
-    public List<CubeInstance> listAllCubes(final String cubeName, final String projectName) {
+    public List<CubeInstance> listAllCubes(final String cubeName, final String projectName,final String modelName) {
         List<CubeInstance> cubeInstances = null;
         ProjectInstance project = (null != projectName) ? getProjectManager().getProject(projectName) : null;
 
@@ -91,8 +91,21 @@ public class CubeService extends BasicService {
             cubeInstances = listAllCubes(projectName);
         }
 
+        List<CubeInstance> filterModelCubes = new ArrayList<CubeInstance>();
+
+        if(modelName!=null){
+            for (CubeInstance cubeInstance : cubeInstances) {
+                boolean isCubeMatch = cubeInstance.getDescriptor().getModelName().toLowerCase().equals(modelName.toLowerCase());
+                if (isCubeMatch) {
+                    filterModelCubes.add(cubeInstance);
+                }
+            }
+        }else{
+            filterModelCubes = cubeInstances;
+        }
+
         List<CubeInstance> filterCubes = new ArrayList<CubeInstance>();
-        for (CubeInstance cubeInstance : cubeInstances) {
+        for (CubeInstance cubeInstance : filterModelCubes) {
             boolean isCubeMatch = (null == cubeName) || cubeInstance.getName().toLowerCase().contains(cubeName.toLowerCase());
 
             if (isCubeMatch) {
@@ -103,12 +116,12 @@ public class CubeService extends BasicService {
         return filterCubes;
     }
 
-    public List<CubeInstance> getCubes(final String cubeName, final String projectName, final Integer limit, final Integer offset) {
+    public List<CubeInstance> getCubes(final String cubeName, final String projectName,final String modelName ,final Integer limit, final Integer offset) {
         int climit = (null == limit) ? 30 : limit;
         int coffset = (null == offset) ? 0 : offset;
 
         List<CubeInstance> cubes;
-        cubes = listAllCubes(cubeName, projectName);
+        cubes = listAllCubes(cubeName, projectName,modelName);
 
         if (cubes.size() <= coffset) {
             return Collections.emptyList();
@@ -302,7 +315,6 @@ public class CubeService extends BasicService {
      * @param cube
      * @return
      * @throws IOException
-     * @throws CubeIntegrityException
      * @throws JobException
      */
     @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN + " or hasPermission(#cube, 'ADMINISTRATION') or hasPermission(#cube, 'OPERATION') or hasPermission(#cube, 'MANAGEMENT')")
@@ -328,7 +340,6 @@ public class CubeService extends BasicService {
      * Update a cube status from ready to disabled.
      *
      * @return
-     * @throws CubeIntegrityException
      * @throws IOException
      * @throws JobException
      */
@@ -356,7 +367,6 @@ public class CubeService extends BasicService {
      * Update a cube status from disable to ready.
      *
      * @return
-     * @throws CubeIntegrityException
      * @throws IOException
      * @throws JobException
      */
@@ -518,7 +528,6 @@ public class CubeService extends BasicService {
      *
      * @throws IOException
      * @throws JobException
-     * @throws CubeIntegrityException
      */
     private void releaseAllSegments(CubeInstance cube) throws IOException, JobException {
         final List<CubingJob> cubingJobs = listAllCubingJobs(cube.getName(), null);

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java
----------------------------------------------------------------------
diff --git a/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java b/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java
index 52c2d77..1ec27d5 100644
--- a/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java
+++ b/server/src/test/java/org/apache/kylin/rest/controller/CubeControllerTest.java
@@ -66,7 +66,7 @@ public class CubeControllerTest extends ServiceTestBase {
         CubeDesc[] cubes = (CubeDesc[]) cubeDescController.getCube("test_kylin_cube_with_slr_ready");
         Assert.assertNotNull(cubes);
         Assert.assertNotNull(cubeController.getSql("test_kylin_cube_with_slr_ready", "20130331080000_20131212080000"));
-        Assert.assertNotNull(cubeController.getCubes(null, null, 0, 5));
+        Assert.assertNotNull(cubeController.getCubes(null, null, null,0, 5));
 
         CubeDesc cube = cubes[0];
         CubeDesc newCube = new CubeDesc();
@@ -109,7 +109,7 @@ public class CubeControllerTest extends ServiceTestBase {
         cubeController.updateNotifyList(newCubeName, notifyList);
         cubeController.updateCubeCost(newCubeName, 80);
 
-        List<CubeInstance> cubeInstances = cubeController.getCubes(newCubeName, "default", 1, 0);
+        List<CubeInstance> cubeInstances = cubeController.getCubes(newCubeName, "default",null, 1, 0);
 
         CubeInstance cubeInstance = cubeInstances.get(0);
         Assert.assertTrue(cubeInstance.getDescriptor().getNotifyList().contains("john@example.com"));

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/server/src/test/java/org/apache/kylin/rest/service/CubeServiceTest.java
----------------------------------------------------------------------
diff --git a/server/src/test/java/org/apache/kylin/rest/service/CubeServiceTest.java b/server/src/test/java/org/apache/kylin/rest/service/CubeServiceTest.java
index 7bf2516..88e31f4 100644
--- a/server/src/test/java/org/apache/kylin/rest/service/CubeServiceTest.java
+++ b/server/src/test/java/org/apache/kylin/rest/service/CubeServiceTest.java
@@ -48,13 +48,13 @@ public class CubeServiceTest extends ServiceTestBase {
         Assert.assertTrue(CubeService.getCubeDescNameFromCube("testCube").equals("testCube_desc"));
         Assert.assertTrue(CubeService.getCubeNameFromDesc("testCube_desc").equals("testCube"));
 
-        List<CubeInstance> cubes = cubeService.getCubes(null, null, null, null);
+        List<CubeInstance> cubes = cubeService.getCubes(null, null, null, null,null);
         Assert.assertNotNull(cubes);
         CubeInstance cube = cubes.get(0);
         cubeService.isCubeDescEditable(cube.getDescriptor());
         cubeService.isCubeEditable(cube);
 
-        cubes = cubeService.getCubes(null, null, 1, 0);
+        cubes = cubeService.getCubes(null, null,null, 1, 0);
         Assert.assertTrue(cubes.size() == 1);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/index.html
----------------------------------------------------------------------
diff --git a/webapp/app/index.html b/webapp/app/index.html
index 8c6bb2d..ac5c7b7 100644
--- a/webapp/app/index.html
+++ b/webapp/app/index.html
@@ -29,7 +29,7 @@
 
     <!-- ref:css css/styles.min.<%= buildNumber %>.css -->
     <link rel="stylesheet" type="text/css" href="components/bootstrap/dist/css/bootstrap.min.css">
-    <link rel="stylesheet" type="text/css" href="components/font-awesome/css/font-awesome.css">
+    <link rel="stylesheet" type="text/css" href="components/components-font-awesome/css/font-awesome.css">
 
     <link rel="stylesheet" type="text/css" href="components/angular-tree-control/css/tree-control.css">
     <link rel="stylesheet" type="text/css" href="components/angular-tree-control/css/tree-control-attribute.css">
@@ -44,6 +44,7 @@
     <link rel="stylesheet" type="text/css" href="css/AdminLTE-fonts.css">
     <link rel="stylesheet" type="text/css" href="css/AdminLTE.css">
     <link rel="stylesheet" type="text/css" href="components/bootstrap-sweetalert/lib/sweet-alert.css">
+    <link rel="stylesheet" type="text/css" href="components/angular-bootstrap-nav-tree/dist/abn_tree.css">
 
     <link rel="stylesheet/less" href="less/build.less">
     <!-- endref -->
@@ -100,7 +101,8 @@
 <script src="components/underscore/underscore.js"></script>
 <script src="components/angular-underscore/angular-underscore.js"></script>
 <script src="components/jquery-ui/jquery-ui.min.js"></script>
-<script src="components/angular-ui-sortable/sortable.js"></script>
+<script src="components/angular-ui-sortable/sortable.min.js"></script>
+<script src="components/angular-bootstrap-nav-tree/dist/abn_tree_directive.js"></script>
 
 <script src="js/app.js"></script>
 <script src="js/config.js"></script>

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/js/app.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/app.js b/webapp/app/js/app.js
index ba24cb1..1e00bdc 100644
--- a/webapp/app/js/app.js
+++ b/webapp/app/js/app.js
@@ -17,4 +17,4 @@
 */
 
 //Kylin Application Module
-KylinApp = angular.module('kylin', ['ngRoute', 'ngResource', 'ngGrid', 'ui.bootstrap', 'ui.ace', 'base64', 'angularLocalStorage', 'localytics.directives', 'treeControl', 'nvd3ChartDirectives','ngLoadingRequest','oitozero.ngSweetAlert','ngCookies','angular-underscore', 'ngAnimate', 'ui.sortable']);
+KylinApp = angular.module('kylin', ['ngRoute', 'ngResource', 'ngGrid', 'ui.bootstrap', 'ui.ace', 'base64', 'angularLocalStorage', 'localytics.directives', 'treeControl', 'nvd3ChartDirectives','ngLoadingRequest','oitozero.ngSweetAlert','ngCookies','angular-underscore', 'ngAnimate', 'ui.sortable','angularBootstrapNavTree']);

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/js/controllers/job.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/job.js b/webapp/app/js/controllers/job.js
index 0054915..0e65578 100644
--- a/webapp/app/js/controllers/job.js
+++ b/webapp/app/js/controllers/job.js
@@ -78,7 +78,7 @@ KylinApp
             return JobList.list(jobRequest).then(function(resp){
                 $scope.state.loading = false;
                 defer.resolve(resp);
-                defer.promise;
+                return defer.promise;
             });
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/js/controllers/models.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/models.js b/webapp/app/js/controllers/models.js
index 1777723..fb6c964 100644
--- a/webapp/app/js/controllers/models.js
+++ b/webapp/app/js/controllers/models.js
@@ -19,24 +19,30 @@
 'use strict';
 
 KylinApp
-    .controller('ModelsCtrl', function ($scope, $q, $routeParams, $location, $modal, MessageService, CubeDescService, CubeService, JobService, UserService,  ProjectService,SweetAlert,loadingRequest,$log,modelConfig,ProjectModel,ModelService,MetaModel,ModelList) {
+    .controller('ModelsCtrl', function ($scope, $q, $routeParams, $location, $window,$modal, MessageService, CubeDescService, CubeService, JobService, UserService,  ProjectService,SweetAlert,loadingRequest,$log,modelConfig,ProjectModel,ModelService,MetaModel,ModelList) {
         $scope.modelList = ModelList;
         $scope.modelConfig = modelConfig;
         ModelList.removeAll();
         $scope.loading = false;
         $scope.action = {};
-
+        $scope.window = 0.68 * $window.innerHeight;
         $scope.listParams={
             cubeName: $routeParams.cubeName,
             projectName: $routeParams.projectName
         };
 
+
+        //tree data
+        $scope.models_treedata=[];
+
+
+        //  TODO offset&limit
         $scope.list = function (offset, limit) {
             if(!$scope.projectModel.projects.length){
                 return [];
             }
             offset = (!!offset) ? offset : 0;
-            limit = (!!limit) ? limit : 20;
+            limit = (!!limit) ? limit : 70;
 
             var queryParam = {offset: offset, limit: limit};
             if ($scope.listParams.modelName) {
@@ -47,27 +53,40 @@ KylinApp
             $scope.loading = true;
 
             var defer = $q.defer();
-            return ModelList.list(queryParam).then(function(resp){
+             ModelList.list(queryParam).then(function(resp){
                 $scope.loading = false;
                 defer.resolve(resp);
-                defer.promise;
             },function(resp){
                 $scope.loading = false;
                 defer.resolve([]);
-                defer.promise;
             });
+
+            return  defer.promise;
         };
 
+        $scope.init = function(){
+            $scope.list().then(function(resp){
+                $scope.models_treedata = [];
+                angular.forEach(ModelList.models,function(model){
+                    var _model = {label:model.name,noLeaf:true}
+                    var _children = []
+                    angular.forEach(model.cubes,function(cube){
+                        _children.push(cube.name);
+                    });
+                    if(_children.length){
+                         _model.children = _children;
+                    }
+//                    _model.children=[''];
+                    $scope.models_treedata.push(_model);
+                });
 
-        $scope.loadDetail = function (model) {
-            $log.info(model);
+            });
         };
 
-
         $scope.$watch('projectModel.selectedProject', function (newValue, oldValue) {
-            if(newValue!=oldValue||newValue==null){
+            if(newValue||newValue==null){
                 ModelList.removeAll();
-                $scope.reload();
+                $scope.init();
             }
 
         });

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/js/model/modelList.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/model/modelList.js b/webapp/app/js/model/modelList.js
index 9e92ed9..1161f47 100644
--- a/webapp/app/js/model/modelList.js
+++ b/webapp/app/js/model/modelList.js
@@ -16,7 +16,7 @@
  * limitations under the License.
 */
 
-KylinApp.service('ModelList',function(ModelService,$q,AccessService){
+KylinApp.service('ModelList',function(ModelService,CubeService,$q,AccessService){
     var models=[];
     var _this = this;
 
@@ -28,10 +28,14 @@ KylinApp.service('ModelList',function(ModelService,$q,AccessService){
                 AccessService.list({type: "DataModelDesc", uuid: model.uuid}, function (accessEntities) {
                     model.accessEntities = accessEntities;
                 });
+//                add cube info to model
+                CubeService.list({offset: 0, limit: 70,modelName:model.name}, function (_cubes) {
+                    model.cubes=_cubes;
+                });
             });
             _models = _.filter(_models,function(models){return models.name!=undefined});
             _this.models = _this.models.concat(_models);
-            defer.resolve(_this.models.length);
+            defer.resolve(_this.models);
         },function(){
             defer.reject("Failed to load models");
         });
@@ -39,6 +43,7 @@ KylinApp.service('ModelList',function(ModelService,$q,AccessService){
 
     };
 
+
     this.removemodels = function(models){
         var modelsIndex = _this.models.indexOf(models);
         if (modelsIndex > -1) {

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/less/app.less
----------------------------------------------------------------------
diff --git a/webapp/app/less/app.less b/webapp/app/less/app.less
index 7296eb9..c829ca1 100644
--- a/webapp/app/less/app.less
+++ b/webapp/app/less/app.less
@@ -375,7 +375,7 @@ input.ng-invalid-required:after {
 
 .nav-pills>li {
 cursor: pointer;
-background-color: #094868;
+//background-color: #094868;
 }
 
 .alert-info {

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/less/component.less
----------------------------------------------------------------------
diff --git a/webapp/app/less/component.less b/webapp/app/less/component.less
index 0289bc4..7da1b69 100644
--- a/webapp/app/less/component.less
+++ b/webapp/app/less/component.less
@@ -893,3 +893,54 @@ ul.messenger-theme-ice .messenger-message{
 .model-dimension-edit .chosen-container{
   width:100% !important;
 }
+
+
+//overwrite style for abn-tree
+.abn-tree-animate-enter,
+li.abn-tree-row.ng-enter {
+  transition: 200ms linear all;
+  position: relative;
+  display: block;
+  opacity: 0;
+  max-height:0px;
+}
+.abn-tree-animate-enter.abn-tree-animate-enter-active,
+li.abn-tree-row.ng-enter-active{
+  opacity: 1;
+  max-height:30px;
+}
+
+.abn-tree-animate-leave,
+li.abn-tree-row.ng-leave {
+  transition: 200ms linear all;
+  position: relative;
+  display: block;
+  height:30px;
+  max-height: 30px;
+  opacity: 1;
+}
+.abn-tree-animate-leave.abn-tree-animate-leave-active,
+li.abn-tree-row.ng-leave-active {
+  height: 0px;
+  max-height:0px;
+  opacity: 0;
+}
+
+
+/*
+------------------------------------------
+Angular 1.2.0 Animation
+*/
+
+
+.abn-tree-animate.ng-enter{
+
+}
+.abn-tree-animate.ng-enter{
+
+}
+
+.abn-tree .indented  {
+  font-size: 13px;
+  color: #3a87ad;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/partials/models/_models.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/models/_models.html b/webapp/app/partials/models/_models.html
new file mode 100644
index 0000000..ac433c1
--- /dev/null
+++ b/webapp/app/partials/models/_models.html
@@ -0,0 +1,117 @@
+<!--
+* 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 class="page-header row">
+    <!--Project-->
+    <div class="col-xs-3">
+        <form ng-if="userService.isAuthorized()">
+            <div class="form-group">
+                <!--Project-->
+                <a class="btn btn-xs btn-info" href="projects" tooltip="Manage Project"><i class="fa fa-gears"></i></a>
+                <a class="btn btn-xs btn-primary" style="width: 29px" ng-if="userService.hasRole('ROLE_ADMIN')||userService.hasRole('ROLE_MODELER')&&kylinConfig.getDeployEnv()!=='PROD'"  tooltip="Add Project" ng-click="toCreateProj()">
+                    <i class="fa fa-plus"></i>
+                </a>
+            </div>
+        </form>
+    </div>
+    <!--Model Name-->
+    <div class="col-xs-3">
+        <form ng-submit="" style="display: inline" >
+            <span class="input-icon input-icon-right nav-search" style="font-size:14px;"><b>Model Name:</b>
+                <input type="text" placeholder="Filter ..." class="nav-search-input"  ng-model="listParams.modelName"/>
+                <i class="ace-icon fa fa-search blue"  ng-click="modelList.removeAll();list()"></i>
+            </span>
+        </form>
+    </div>
+    <div class="pull-right">
+        <a class="btn btn-primary btn-sm" href="models/add"  ng-if="userService.hasRole('ROLE_MODELER')" id="addModelButton"><i class="fa fa-plus"></i> Model</a>
+    </div>
+</div>
+<div ng-if="!loading && modelList.models.length == 0">
+    <div no-result text="No Model."></div>
+</div>
+<loading ng-if="loading" text="Loading Models..."></loading>
+
+<div ng-if="modelList.models.length > 0" class="dataTables_wrapper no-footer">
+    <div class="row">
+        <div class="col-xs-12"><label class="table-header-text">Models</label></div>
+    </div>
+    <table class="table table-striped table-bordered table-hover dataTable no-footer">
+        <!--Header-->
+        <thead>
+        <tr style="cursor: pointer">
+            <th ng-repeat="theaditem in modelConfig.theaditems"
+                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>
+            <th>Actions</th>
+        </tr>
+        </thead>
+        <!--Body-->
+        <tbody ng-repeat="model in modelList.models | orderObjectBy:state.filterAttr:state.filterReverse">
+        <tr ng-class="{accordion:true}" style="cursor: pointer"  ng-click="model.showDetail=!model.showDetail;loadDetail(model)">
+            <td>
+                <i ng-show="!model.showDetail" class="fa fa-chevron-circle-right blue"></i>
+                <i ng-show="model.showDetail" class="fa fa-chevron-circle-down blue"></i>
+                {{ model.name}}
+            </td>
+            <td>
+                    {{ model.fact_table}}
+            </td>
+            <td>
+                    {{ model.last_modified | utcToConfigTimeZone}}
+            </td>
+            <td>
+                <div ng-click="$event.stopPropagation();" class="btn-group" ng-if="userService.hasRole('ROLE_ADMIN') || hasPermission(model, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask)">
+                    <button type="button" class="btn btn-default btn-xs dropdown-toggle"
+                            data-toggle="dropdown">
+                        Action <span class="ace-icon fa fa-caret-down icon-on-right"></span>
+                    </button>
+                    <ul class="dropdown-menu" role="menu">
+                        <li><a href="models/edit/{{model.name}}">Edit</a></li>
+                        <li ng-show="false" ng-if="userService.hasRole('ROLE_ADMIN')">
+                            <a ng-click="dropModel(model)" tooltip="Drop the model, related cubes and data permanently.">Drop</a></li>
+
+                    </ul>
+                </div>
+                <span ng-if="!(userService.hasRole('ROLE_ADMIN') || hasPermission(model, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask))">
+                    N/A
+                </span>
+            </td>
+        </tr>
+        <tr ng-show="model.showDetail">
+            <td colspan="9" style="padding: 10px 30px 10px 30px;">
+                <div ng-include src="'partials/models/model_detail.html'"></div>
+            </td>
+        </tr>
+        </tbody>
+    </table>
+</div>
+
+
+<div class="row">
+    <div class="col-xs-12">
+        <kylin-pagination data="modelList.models" load-func="list" action="action"/>
+    </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/partials/models/models.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/models/models.html b/webapp/app/partials/models/models.html
index ac433c1..49343aa 100644
--- a/webapp/app/partials/models/models.html
+++ b/webapp/app/partials/models/models.html
@@ -15,103 +15,13 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 -->
-
-<div class="page-header row">
-    <!--Project-->
-    <div class="col-xs-3">
-        <form ng-if="userService.isAuthorized()">
-            <div class="form-group">
-                <!--Project-->
-                <a class="btn btn-xs btn-info" href="projects" tooltip="Manage Project"><i class="fa fa-gears"></i></a>
-                <a class="btn btn-xs btn-primary" style="width: 29px" ng-if="userService.hasRole('ROLE_ADMIN')||userService.hasRole('ROLE_MODELER')&&kylinConfig.getDeployEnv()!=='PROD'"  tooltip="Add Project" ng-click="toCreateProj()">
-                    <i class="fa fa-plus"></i>
-                </a>
-            </div>
-        </form>
-    </div>
-    <!--Model Name-->
-    <div class="col-xs-3">
-        <form ng-submit="" style="display: inline" >
-            <span class="input-icon input-icon-right nav-search" style="font-size:14px;"><b>Model Name:</b>
-                <input type="text" placeholder="Filter ..." class="nav-search-input"  ng-model="listParams.modelName"/>
-                <i class="ace-icon fa fa-search blue"  ng-click="modelList.removeAll();list()"></i>
-            </span>
-        </form>
-    </div>
-    <div class="pull-right">
-        <a class="btn btn-primary btn-sm" href="models/add"  ng-if="userService.hasRole('ROLE_MODELER')" id="addModelButton"><i class="fa fa-plus"></i> Model</a>
+<div class="row">
+    <!--table_tree-->
+    <div class="col-xs-3 overwriteLTE">
+        <div ng-include src="'partials/models/models_tree.html'"></div>
     </div>
-</div>
-<div ng-if="!loading && modelList.models.length == 0">
-    <div no-result text="No Model."></div>
-</div>
-<loading ng-if="loading" text="Loading Models..."></loading>
 
-<div ng-if="modelList.models.length > 0" class="dataTables_wrapper no-footer">
-    <div class="row">
-        <div class="col-xs-12"><label class="table-header-text">Models</label></div>
+    <div class="col-xs-9">
+        <h3>Model Info</h3>
     </div>
-    <table class="table table-striped table-bordered table-hover dataTable no-footer">
-        <!--Header-->
-        <thead>
-        <tr style="cursor: pointer">
-            <th ng-repeat="theaditem in modelConfig.theaditems"
-                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>
-            <th>Actions</th>
-        </tr>
-        </thead>
-        <!--Body-->
-        <tbody ng-repeat="model in modelList.models | orderObjectBy:state.filterAttr:state.filterReverse">
-        <tr ng-class="{accordion:true}" style="cursor: pointer"  ng-click="model.showDetail=!model.showDetail;loadDetail(model)">
-            <td>
-                <i ng-show="!model.showDetail" class="fa fa-chevron-circle-right blue"></i>
-                <i ng-show="model.showDetail" class="fa fa-chevron-circle-down blue"></i>
-                {{ model.name}}
-            </td>
-            <td>
-                    {{ model.fact_table}}
-            </td>
-            <td>
-                    {{ model.last_modified | utcToConfigTimeZone}}
-            </td>
-            <td>
-                <div ng-click="$event.stopPropagation();" class="btn-group" ng-if="userService.hasRole('ROLE_ADMIN') || hasPermission(model, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask)">
-                    <button type="button" class="btn btn-default btn-xs dropdown-toggle"
-                            data-toggle="dropdown">
-                        Action <span class="ace-icon fa fa-caret-down icon-on-right"></span>
-                    </button>
-                    <ul class="dropdown-menu" role="menu">
-                        <li><a href="models/edit/{{model.name}}">Edit</a></li>
-                        <li ng-show="false" ng-if="userService.hasRole('ROLE_ADMIN')">
-                            <a ng-click="dropModel(model)" tooltip="Drop the model, related cubes and data permanently.">Drop</a></li>
-
-                    </ul>
-                </div>
-                <span ng-if="!(userService.hasRole('ROLE_ADMIN') || hasPermission(model, permissions.ADMINISTRATION.mask, permissions.MANAGEMENT.mask, permissions.OPERATION.mask))">
-                    N/A
-                </span>
-            </td>
-        </tr>
-        <tr ng-show="model.showDetail">
-            <td colspan="9" style="padding: 10px 30px 10px 30px;">
-                <div ng-include src="'partials/models/model_detail.html'"></div>
-            </td>
-        </tr>
-        </tbody>
-    </table>
 </div>
-
-
-<div class="row">
-    <div class="col-xs-12">
-        <kylin-pagination data="modelList.models" load-func="list" action="action"/>
-    </div>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/partials/models/models_info.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/models/models_info.html b/webapp/app/partials/models/models_info.html
new file mode 100644
index 0000000..f381077
--- /dev/null
+++ b/webapp/app/partials/models/models_info.html
@@ -0,0 +1,46 @@
+<!--
+* 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 class="tree-border">
+    <div class="row">
+        <div class="col-xs-7">
+            <h3 class="text-info">Tables</h3>
+        </div>
+
+        <!--button-->
+        <div class="col-xs-5" style="padding-left: 0px;margin-top: 20px;">
+            <div class="pull-right">
+                <a class="btn btn-xs btn-primary" tooltip="Load Hive Table"  ng-if="userService.hasRole('ROLE_ADMIN')"  ng-click="openModal()"><i class="fa fa-download"></i></a>
+                <a class="btn btn-xs btn-success" tooltip="Refresh Tables" ng-click="aceSrcTbChanged()"><i class="fa fa-refresh"></i></a>
+            </div>
+        </div>
+
+    </div>
+    <div class="space-4"></div>
+    <!--tree-->
+    <div style="width:100%; height:{{window}}px; overflow:auto;">
+        <treecontrol ng-if="tableModel.selectedSrcDb.length > 0" class="tree-light"
+                     dirSelection="true"
+                     tree-model="tableModel.selectedSrcDb"
+                     options="tableModel.treeOptions"
+                     on-selection="showSelected(node)"
+                     selected-node="tableModel.selectedSrcTable">
+            {{node.name}} {{!!(node.datatype)?'(' + trimType(node.datatype) + ')' : ''}}
+        </treecontrol>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/app/partials/models/models_tree.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/models/models_tree.html b/webapp/app/partials/models/models_tree.html
new file mode 100644
index 0000000..40b7c3a
--- /dev/null
+++ b/webapp/app/partials/models/models_tree.html
@@ -0,0 +1,47 @@
+<!--
+* 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 class="tree-border">
+    <div class="row">
+        <div class="col-xs-7">
+            <h3 class="text-info">Models</h3>
+        </div>
+
+        <!--button-->
+        <div class="col-xs-5" style="padding-left: 0px;margin-top: 20px;">
+            <div class="pull-right">
+                <a class="btn btn-xs btn-primary" href='models/add' tooltip="Create Model"  ng-if="userService.hasRole('ROLE_ADMIN')"><i class="fa fa-plus"></i></a>
+            </div>
+        </div>
+
+    </div>
+    </div>
+    <div class="space-4 box-header with-border"></div>
+    <!--tree-->
+    <div style="width:100%; height:{{window}}px; overflow:auto;">
+        <abn-tree
+                tree-data         = "models_treedata"
+                tree-control      = "my_tree"
+                icon-leaf         = "fa fa-cube"
+                icon-expand       = "fa fa-plus"
+                icon-collapse     = "fa fa-minus"
+                expand-level      = "2"
+                initial-selection = "Vegetable">
+            ></abn-tree>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/c3394d6e/webapp/bower.json
----------------------------------------------------------------------
diff --git a/webapp/bower.json b/webapp/bower.json
index be8eca3..b3f9a8d 100755
--- a/webapp/bower.json
+++ b/webapp/bower.json
@@ -27,8 +27,9 @@
     "underscore": "~1.7.0",
     "fuelux": "~3.5.1",
     "angular-animate": "1.2",
-    "angular-cookies":"1.2"
-
+    "angular-cookies": "1.2",
+    "angular-bootstrap-nav-tree": "*",
+    "components-font-awesome": "~4.3.0"
   },
   "devDependencies": {
     "less.js": "~1.4.0",