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:46 UTC

[17/52] [abbrv] incubator-kylin git commit: model wizard sperate from cube wizard

model wizard sperate from cube wizard


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

Branch: refs/heads/0.8.0
Commit: fb405ebd0941fc7be5ebd7de5db60b641eaad858
Parents: 7b52624
Author: jiazhong <ji...@ebay.com>
Authored: Fri Mar 13 18:22:37 2015 +0800
Committer: honma <ho...@ebay.com>
Committed: Fri May 15 11:35:49 2015 +0800

----------------------------------------------------------------------
 webapp/app/index.html                           |   1 +
 webapp/app/js/controllers/cubeEdit.js           |   4 +-
 webapp/app/js/controllers/cubeModel.js          |  30 +--
 webapp/app/js/controllers/modelEdit.js          | 228 +++++++++++++++++++
 webapp/app/js/controllers/modelSchema.js        |   5 +-
 webapp/app/js/model/metaModel.js                |  61 +++--
 webapp/app/js/services/model.js                 |   2 +-
 webapp/app/partials/cubes/cube_detail.html      |   2 -
 .../modelDesigner/model_dimensions.html         |   4 +-
 .../app/partials/modelDesigner/model_info.html  |  84 +++++++
 webapp/app/partials/models/model_detail.html    |  13 +-
 webapp/app/partials/models/model_edit.html      |   4 +-
 webapp/app/routes.json                          |   8 +
 13 files changed, 372 insertions(+), 74 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/index.html
----------------------------------------------------------------------
diff --git a/webapp/app/index.html b/webapp/app/index.html
index 9e7317f..c8698ac 100644
--- a/webapp/app/index.html
+++ b/webapp/app/index.html
@@ -170,6 +170,7 @@
 <script src="js/controllers/modelSchema.js"></script>
 <script src="js/controllers/modelDimensions.js"></script>
 <script src="js/controllers/modelRefresh.js"></script>
+<script src="js/controllers/modelEdit.js"></script>
 
 <!--New GUI-->
 <script src="js/controllers/models.js"></script>

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/js/controllers/cubeEdit.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/cubeEdit.js b/webapp/app/js/controllers/cubeEdit.js
index 356a5b5..fe099fd 100755
--- a/webapp/app/js/controllers/cubeEdit.js
+++ b/webapp/app/js/controllers/cubeEdit.js
@@ -357,7 +357,7 @@ KylinApp.controller('CubeEditCtrl', function ($scope, $q, $routeParams, $locatio
         //! here get the latest rowkey_columns
         $scope.cubeMetaFrame.rowkey.rowkey_columns = newRowKeyColumns;
 
-        if($scope.cubeMode==="editExistCube") {
+        if($scope.modelMode==="editExistCube") {
             var aggregationGroups = $scope.cubeMetaFrame.rowkey.aggregation_groups;
             // rm unused item from group,will only rm when [edit] dimension
             angular.forEach(aggregationGroups, function (group, index) {
@@ -387,7 +387,7 @@ KylinApp.controller('CubeEditCtrl', function ($scope, $q, $routeParams, $locatio
             });
         }
 
-        if($scope.cubeMode==="addNewCube"){
+        if($scope.modelMode==="addNewCube"){
 
           if(!tmpAggregationItems.length) {
               $scope.cubeMetaFrame.rowkey.aggregation_groups=[];

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/js/controllers/cubeModel.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/cubeModel.js b/webapp/app/js/controllers/cubeModel.js
index 038f7be..fc2cca8 100644
--- a/webapp/app/js/controllers/cubeModel.js
+++ b/webapp/app/js/controllers/cubeModel.js
@@ -20,25 +20,11 @@
 
 KylinApp.controller('CubeModelCtrl', function ($scope, $modal,cubeConfig,ModelService,MetaModel,SweetAlert,CubeGraphService,$log) {
 
+    //copy model for show Json in model tab list
+    $scope.modelJson = angular.copy($scope.model);
 
-    $scope.buildGraph = function (cube) {
-        CubeGraphService.buildTree(cube);
-    };
-    $scope.cleanStatus = function(model){
-
-        if (!model)
-        {
-            return;
-        }
-        var newModel = jQuery.extend(true, {}, model);
-        delete newModel.visiblePage;
-        delete newModel.showDetail;
-
-        angular.forEach(newModel.dimensions, function(dimension, index){
-            delete dimension.status;
-        });
-
-        return newModel;
+    $scope.buildGraph = function (model) {
+        CubeGraphService.buildTree(model);
     };
 
     $scope.cubeConfig = cubeConfig;
@@ -132,7 +118,7 @@ KylinApp.controller('CubeModelCtrl', function ($scope, $modal,cubeConfig,ModelSe
     };
 
         $scope.removeLookup = function (lookup) {
-            var dimExist = _.some($scope.cubeMetaFrame.dimensions,function(item,index){
+            var dimExist = _.some($scope.model.dimensions,function(item,index){
                 return item.table===lookup.table;
             });
             if(dimExist) {
@@ -146,9 +132,9 @@ KylinApp.controller('CubeModelCtrl', function ($scope, $modal,cubeConfig,ModelSe
                     closeOnConfirm: true
                 }, function (isConfirm) {
                     if (isConfirm) {
-                        for (var i = $scope.cubeMetaFrame.dimensions.length - 1; i >= 0; i--) {
-                            if ($scope.cubeMetaFrame.dimensions[i].table === lookup.table) {
-                                $scope.cubeMetaFrame.dimensions.splice(i, 1);
+                        for (var i = $scope.model.dimensions.length - 1; i >= 0; i--) {
+                            if ($scope.model.dimensions[i].table === lookup.table) {
+                                $scope.model.dimensions.splice(i, 1);
                             }
                         }
                         lookupList.splice(lookupList.indexOf(lookup), 1);

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/js/controllers/modelEdit.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/modelEdit.js b/webapp/app/js/controllers/modelEdit.js
new file mode 100644
index 0000000..2741d2c
--- /dev/null
+++ b/webapp/app/js/controllers/modelEdit.js
@@ -0,0 +1,228 @@
+/*
+ * 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.
+*/
+
+'use strict';
+
+
+KylinApp.controller('ModelEditCtrl', function ($scope, $q, $routeParams, $location, $templateCache, $interpolate, MessageService, TableService, CubeDescService, CubeService, loadingRequest, SweetAlert,$log,cubeConfig,CubeDescModel,ModelDescService,MetaModel,TableModel) {
+    $scope.cubeConfig = cubeConfig;
+    //add or edit ?
+    var absUrl = $location.absUrl();
+    $scope.modelMode = absUrl.indexOf("/models/add")!=-1?'addNewModel':absUrl.indexOf("/models/edit")!=-1?'editExistModel':'default';
+
+
+    $scope.getPartitonColumns = function(tableName){
+        var columns = _.filter($scope.getColumnsByTable(tableName),function(column){
+            return column.datatype==="date"||column.datatype==="string";
+        });
+        return columns;
+    };
+
+    $scope.getColumnsByTable = function (tableName) {
+        var temp = [];
+        angular.forEach(TableModel.selectProjectTables, function (table) {
+            if (table.name == tableName) {
+                temp = table.columns;
+            }
+        });
+        return temp;
+    };
+
+    $scope.getColumnType = function (_column,table){
+        var columns = $scope.getColumnsByTable(table);
+        var type;
+        angular.forEach(columns,function(column){
+            if(_column === column.name){
+                type = column.datatype;
+                return;
+            }
+        });
+        return type;
+    };
+
+    // ~ Define data
+    $scope.state = {
+        "modelSchema": ""
+    };
+
+    // ~ init
+    if ($scope.isEdit = !!$routeParams.modelName) {
+        ModelDescService.get({model_name: $routeParams.modelName}, function (model) {
+                    if (model) {
+                        $scope.model = model;
+                        //use
+                        //convert GMT mills ,to make sure partition date show GMT Date
+                        //should run only one time
+                        if(model.partition_desc&&model.partition_desc.partition_date_start)
+                        {
+                            MetaModel.converDateToGMT();
+                        }
+                    }
+                });
+
+    } else {
+        $scope.model = MetaModel.createNew();;
+    }
+
+    // ~ public methods
+    $scope.aceChanged = function () {
+    };
+
+    $scope.aceLoaded = function(){
+    };
+
+    $scope.prepareCube = function () {
+        // generate column family
+
+        if ($scope.model.partition_desc.partition_date_column&&($scope.model.partition_desc.partition_date_start|$scope.model.partition_desc.partition_date_start==0)) {
+            var dateStart = new Date($scope.model.partition_desc.partition_date_start);
+            dateStart = (dateStart.getFullYear() + "-" + (dateStart.getMonth() + 1) + "-" + dateStart.getDate());
+            //switch selected time to utc timestamp
+            $scope.model.partition_desc.partition_date_start = new Date(moment.utc(dateStart, "YYYY-MM-DD").format()).getTime();
+
+
+            if($scope.model.partition_desc.partition_date_column.indexOf(".")==-1){
+            $scope.model.partition_desc.partition_date_column=$scope.model.fact_table+"."+$scope.model.partition_desc.partition_date_column;
+            }
+
+        }
+        $scope.state.modelSchema = angular.toJson($scope.model, true);
+        $scope.state.project = $scope.projectModel.selectedProject;
+
+    };
+
+    $scope.cubeResultTmpl = function (notification) {
+        // Get the static notification template.
+        var tmpl = notification.type == 'success' ? 'cubeResultSuccess.html' : 'cubeResultError.html';
+        return $interpolate($templateCache.get(tmpl))(notification);
+    };
+
+    $scope.saveModel = function (design_form) {
+
+        try {
+            angular.fromJson($scope.state.cubeSchema);
+        } catch (e) {
+            SweetAlert.swal('Oops...', 'Invalid cube json format..', 'error');
+            return;
+        }
+
+        SweetAlert.swal({
+            title: '',
+            text: 'Are you sure to save the Model ?',
+            type: '',
+            showCancelButton: true,
+            confirmButtonColor: '#DD6B55',
+            confirmButtonText: "Yes",
+            closeOnConfirm: true
+        }, function(isConfirm) {
+            if(isConfirm){
+                loadingRequest.show();
+
+                if ($scope.isEdit) {
+                    CubeService.update({}, {modelDescData:$scope.state.modelSchema, modelName: $routeParams.modelName, project: $scope.state.project}, function (request) {
+                        if (request.successful) {
+                            $scope.state.modelSchema = request.modelSchema;
+                            MessageService.sendMsg($scope.cubeResultTmpl({'text':'Updated the cube successfully.',type:'success'}), 'success', {}, true, 'top_center');
+
+                            if (design_form) {
+                                design_form.$invalid = true;
+                            }
+                        } else {
+                            $scope.saveModelRollBack();
+                                var message =request.message;
+                                var msg = !!(message) ? message : 'Failed to take action.';
+                                MessageService.sendMsg($scope.cubeResultTmpl({'text':msg,'schema':$scope.state.modelSchema}), 'error', {}, true, 'top_center');
+                        }
+                        //end loading
+                        loadingRequest.hide();
+                    }, function (e) {
+                        $scope.saveModelRollBack();
+
+                        if(e.data&& e.data.exception){
+                            var message =e.data.exception;
+                            var msg = !!(message) ? message : 'Failed to take action.';
+                            MessageService.sendMsg($scope.cubeResultTmpl({'text':msg,'schema':$scope.state.modelSchema}), 'error', {}, true, 'top_center');
+                        } else {
+                            MessageService.sendMsg($scope.cubeResultTmpl({'text':'Failed to take action.','schema':$scope.state.modelSchema}), 'error', {}, true, 'top_center');
+                        }
+                        loadingRequest.hide();
+                    });
+                } else {
+                    CubeService.save({}, {cubeDescData: $scope.state.cubeSchema,modelDescData:$scope.state.modelSchema, project: $scope.state.project}, function (request) {
+                        if(request.successful) {
+                            $scope.state.modelSchema = request.modelSchema;
+
+                            MessageService.sendMsg($scope.cubeResultTmpl({'text':'Created the cube successfully.',type:'success'}), 'success', {}, true, 'top_center');
+                        } else {
+                            $scope.saveModelRollBack();
+                            var message =request.message;
+                            var msg = !!(message) ? message : 'Failed to take action.';
+                            MessageService.sendMsg($scope.cubeResultTmpl({'text':msg,'schema':$scope.state.modelSchema}), 'error', {}, true, 'top_center');
+                        }
+
+                        //end loading
+                        loadingRequest.hide();
+                    }, function (e) {
+                        $scope.saveModelRollBack();
+
+                        if (e.data && e.data.exception) {
+                            var message =e.data.exception;
+                            var msg = !!(message) ? message : 'Failed to take action.';
+                            MessageService.sendMsg($scope.cubeResultTmpl({'text':msg,'schema':$scope.state.modelSchema}), 'error', {}, true, 'top_center');
+                        } else {
+                            MessageService.sendMsg($scope.cubeResultTmpl({'text':"Failed to take action.",'schema':$scope.state.modelSchema}), 'error', {}, true, 'top_center');
+                        }
+                        //end loading
+                        loadingRequest.hide();
+
+                    });
+                }
+            }
+            else{
+                $scope.saveModelRollBack();
+            }
+        });
+    };
+
+//    reverse the date
+    $scope.saveModelRollBack = function (){
+        if($scope.model&&($scope.model.partition_desc.partition_date_start||$scope.model.partition_desc.partition_date_start==0))
+        {
+            $scope.model.partition_desc.partition_date_start+=new Date().getTimezoneOffset()*60000;
+        }
+    };
+
+    $scope.$watch('projectModel.selectedProject', function (newValue, oldValue) {
+        if(!newValue){
+            return;
+        }
+        var param = {
+            ext: true,
+            project:newValue
+        };
+        if(newValue){
+            TableModel.initTables();
+            TableService.list(param, function (tables) {
+                angular.forEach(tables, function (table) {
+                    table.name = table.database+"."+table.name;
+                    TableModel.addTable(table);
+                });
+            });
+        }
+    });
+});

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/js/controllers/modelSchema.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/modelSchema.js b/webapp/app/js/controllers/modelSchema.js
index 638e119..112507e 100644
--- a/webapp/app/js/controllers/modelSchema.js
+++ b/webapp/app/js/controllers/modelSchema.js
@@ -28,6 +28,7 @@ KylinApp.controller('ModelSchemaCtrl', function ($scope, QueryService, UserServi
 
 
     $scope.wizardSteps = [
+        {title: 'Model Info', src: 'partials/modelDesigner/model_info.html', isComplete: false},
         {title: 'Data Model', src: 'partials/modelDesigner/data_model.html', isComplete: false},
         {title: 'Dimensions', src: 'partials/modelDesigner/model_dimensions.html', isComplete: false},
         {title: 'Measures', src: 'partials/modelDesigner/model_measures.html', isComplete: false},
@@ -42,11 +43,11 @@ KylinApp.controller('ModelSchemaCtrl', function ($scope, QueryService, UserServi
         $scope.state = {mode: "view"};
     }
 
-    $scope.$watch('cubeMetaFrame', function (newValue, oldValue) {
+    $scope.$watch('model', function (newValue, oldValue) {
         if(!newValue){
             return;
         }
-        if ($scope.cubeMode=="editExistCube"&&newValue && !newValue.project) {
+        if ($scope.modelMode=="editExistModel"&&newValue && !newValue.project) {
             initProject();
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/js/model/metaModel.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/model/metaModel.js b/webapp/app/js/model/metaModel.js
index 021dda7..74714d9 100644
--- a/webapp/app/js/model/metaModel.js
+++ b/webapp/app/js/model/metaModel.js
@@ -21,49 +21,38 @@
  */
 KylinApp.service('MetaModel',function(){
 
+    var _this = this;
     //data model when edit model
-    this.model={
-        name: null,
-        fact_table: null,
-        lookups: [],
-        filter_condition:null,
-        capacity:null,
-        "partition_desc" : {
-            "partition_date_column" : '',
-            "partition_date_start" : 0,
-            "partition_type" : 'APPEND'
-        },
-        last_modified:0
+    this.name=null;
+    this.fact_table=null;
+    this.lookups=[];
+    this.filter_condition=null;
+    this.capacity=null;
+    this.dimensions=[];
+    this.metrics=[];
+    this.partition_desc = {
+        "partition_date_column" : '',
+        "partition_date_start" : 0,
+        "partition_type" : 'APPEND'
     };
+    this.last_modified=0;
 
     this.setMetaModel =function(model){
-        var _model = {};
-        _model.name = model.name;
-        _model.fact_table = model.fact_table;
-        _model.lookups =model.lookups;
-        _model.filter_condition = model.filter_condition;
-        _model.capacity = model.capacity;
-        _model.partition_desc = model.partition_desc;
-        _model.last_modified = model.last_modified;
-        this.model = _model;
-    };
-
-    this.initModel = function(){
-        this.model = this.createNew();
-    }
-
-    this.getMetaModel = function(){
-        return this.model;
-    };
-
-    this.setFactTable = function(fact_table) {
-        this.model.fact_table =fact_table;
+        _this.name = model.name;
+        _this.fact_table = model.fact_table;
+        _this.lookups =model.lookups;
+        _this.filter_condition = model.filter_condition;
+        _this.capacity = model.capacity;
+        _this.partition_desc = model.partition_desc;
+        _this.last_modified = model.last_modified;
+        _this.metrics  = model.metrics;
+        _this.dimensions = model.dimensions;
     };
 
 
     this.converDateToGMT = function(){
-        if(this.model.partition_desc&&this.model.partition_desc.partition_date_start){
-            this.model.partition_desc.partition_date_start+=new Date().getTimezoneOffset()*60000;
+        if(this.partition_desc&&this.partition_desc.partition_date_start){
+            this.partition_desc.partition_date_start+=new Date().getTimezoneOffset()*60000;
         }
     };
     //
@@ -74,6 +63,8 @@ KylinApp.service('MetaModel',function(){
                 lookups: [],
                 filter_condition:'',
                 capacity:'MEDIUM',
+                dimensions:[],
+                metrics:[],
                 "partition_desc" : {
                     "partition_date_column" : '',
                     "partition_date_start" : 0,

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/js/services/model.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/services/model.js b/webapp/app/js/services/model.js
index 681ce75..ae9bbb0 100644
--- a/webapp/app/js/services/model.js
+++ b/webapp/app/js/services/model.js
@@ -16,7 +16,7 @@
  * limitations under the License.
 */
 
-KylinApp.factory('ModelService', ['$resource', function ($resource, config) {
+KylinApp.factory('ModelDescService', ['$resource', function ($resource, config) {
     return $resource(Config.service.url + 'model/:model_name/:propName/:propValue/:action', {}, {
         get: {method: 'GET', params: {}, isArray: false}
     });

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/partials/cubes/cube_detail.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/cubes/cube_detail.html b/webapp/app/partials/cubes/cube_detail.html
index 51196cf..59f2d69 100755
--- a/webapp/app/partials/cubes/cube_detail.html
+++ b/webapp/app/partials/cubes/cube_detail.html
@@ -107,8 +107,6 @@
                 <ul>
                     <li>Region Count: <span class="red">{{table.regionCount}}</span></li>
                     <li>Size: <span class="red">{{table.tableSize | bytes:2}}</span></li>
-                    <li>Start Date (Include): <span class="red">{{table.dateRangeStart | reverseToGMT0}}</span></li>
-                    <li>End Date (Exclude): <span class="red">{{table.dateRangeStart | reverseToGMT0}}</span></li>
                 </ul>
             </div>
             <div ng-if="cube.hbase">

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/partials/modelDesigner/model_dimensions.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/modelDesigner/model_dimensions.html b/webapp/app/partials/modelDesigner/model_dimensions.html
index 9067807..8be67d1 100644
--- a/webapp/app/partials/modelDesigner/model_dimensions.html
+++ b/webapp/app/partials/modelDesigner/model_dimensions.html
@@ -77,7 +77,7 @@
                     <span class="label label-primary" ng-repeat="t in getDimType(dimension)">{{t}}</span>
                 </td>
                 <!--Columns-->
-                <td>
+                <td  style="word-wrap:break-word;word-break:break-all;">
                     <div ng-repeat="t in getDimType(dimension)">
                         <div ng-switch="t">
                             <dl class="dl-horizontal" ng-switch-when="hierarchy">
@@ -90,7 +90,7 @@
                             </dl>
                             <dl class="dl-horizontal" ng-switch-when="normal">
                                 <dt>Column</dt>
-                                <dd>{{dimension.column}}</dd>
+                                <dd>{{dimension.columns}}</dd>
                             </dl>
                         </div>
                     </div>

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/partials/modelDesigner/model_info.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/modelDesigner/model_info.html b/webapp/app/partials/modelDesigner/model_info.html
new file mode 100644
index 0000000..d91a0f0
--- /dev/null
+++ b/webapp/app/partials/modelDesigner/model_info.html
@@ -0,0 +1,84 @@
+<!--
+* 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="row">
+    <div class="col-xs-8">
+        <!--Project-->
+        <div class="form-group">
+            <div class="row">
+                <label class="col-xs-12 col-sm-3 control-label no-padding-right">
+                    <a ng-if="state.mode=='edit'" href="projects">
+                        <b>Project</b>
+                    </a>
+                    <b ng-if="state.mode=='view'" class="font-color-default">Project</b>
+                </label>
+                <div class="col-xs-12 col-sm-6">
+                    <input ng-if="state.mode=='edit'" name="project_name" type="text" class="form-control"
+                           ng-model="model.project" disabled/>
+                    <span ng-if="state.mode=='view'">
+                        {{model.project}}
+                    </span>
+                </div>
+            </div>
+        </div>
+
+        <!--Cube Name-->
+        <div class="form-group">
+            <div class="row">
+                <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default">
+                    <b>Cube Name</b>
+                </label>
+                <div class="col-xs-12 col-sm-6">
+                    <input ng-if="state.mode=='edit'" name="model_name" type="text" class="form-control"
+                           ng-model="model.name" required
+                           placeholder="You can use letters, numbers, and '_'"
+                           ng-maxlength=100 ng-pattern="/^\w+$/" />
+                    <span ng-if="state.mode=='view'">{{model.name}}</span>
+                </div>
+                <div class="col-xs-12 col-sm-3">
+                    <div class="text-warning" ng-if="design_form.model_name.$error.required  && design_form.model_name.$dirty">
+                        Model name is required.
+                    </div>
+                    <div class="text-warning" ng-if="design_form.model_name.$invalid && design_form.model_name.$dirty && !design_form.model_name.$error.required">
+                        Model name is invalid.
+                    </div>
+                </div>
+            </div>
+        </div>
+
+    </div>
+
+    <!--Tips-->
+    <div class="col-xs-4">
+        <div class="box box-solid">
+            <div class="box-header widget-header-flat">
+                <h4 class="box-title">Tips</h4>
+            </div>
+            <div class="box-body">
+                <div class="row">
+                    <div class="col-xs-12">
+                        <ol class="text-info">
+                            <li>Model must belong to project which you have privilege to create</li>
+                            <li>Model name is unique name of entire system</li>
+                        </ol>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/partials/models/model_detail.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/models/model_detail.html b/webapp/app/partials/models/model_detail.html
index 96a64c0..a4ae287 100644
--- a/webapp/app/partials/models/model_detail.html
+++ b/webapp/app/partials/models/model_detail.html
@@ -22,7 +22,7 @@
             <a href="" ng-click="model.visiblePage='metadata'">Grid</a>
         </li>
         <li class="{{model.visiblePage=='graph'? 'active':''}}">
-            <a href="" ng-click="model.visiblePage='graph';buildGraph(model);">Visualization</a>
+            <a href="" ng-click="model.visiblePage='graph';buildGraph(model);$event.stopPropagation();">Visualization</a>
         </li>
         <li class="{{model.visiblePage=='json_model'? 'active':''}}"
             ng-if="userService.hasRole('ROLE_ADMIN') || hasPermission(model, 16) && !newAccess">
@@ -32,12 +32,13 @@
 
     <div class="model-detail" ng-if="!model.visiblePage || model.visiblePage=='metadata'">
         <div ng-include="'partials/models/model_schema.html'" ng-controller="ModelSchemaCtrl"
-             ng-init="state={mode:'view', modelName:model.name}"></div>
+             ng-init="state={mode:'view', modelName:model.name}">
+        </div>
     </div>
-    <div ng-show="model.visiblePage=='json_model'" class="model-detail">
-          <pre
-               style="background-color: white;border: 0px">{{angular.toJson(cleanStatus(model), true)}}</pre>
+    <div ng-show="model.visiblePage=='graph'" id="model_graph_{{model.name}}" class="model-detail model_graph">
     </div>
-    <div ng-show="model.visiblePage=='graph'" id="model_graph_{{model.name}}" class="cube-detail cube_graph">
+    <div ng-show="model.visiblePage=='json_model'" class="model-detail">
+          <pre ng-if="!state.jsonEdit"
+               style="background-color: white;border: 0px">{{angular.toJson(modelJson, true)}}</pre>
     </div>
 </div>

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/partials/models/model_edit.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/models/model_edit.html b/webapp/app/partials/models/model_edit.html
index 07c3913..82d0348 100644
--- a/webapp/app/partials/models/model_edit.html
+++ b/webapp/app/partials/models/model_edit.html
@@ -24,8 +24,8 @@
         <form role="form" name="cube_form" novalidate>
             <!-- This margin in order to align with table tree in left part -->
             <div style="margin-top: 20px;">
-                <div ng-include="'partials/cubes/cube_schema.html'" ng-controller="CubeSchemaCtrl"
-                     ng-init="state={mode:'edit', cubeName: routeParams.cubeName}">
+                <div ng-include="'partials/models/model_schema.html'" ng-controller="ModelSchemaCtrl"
+                     ng-init="state={mode:'edit', modelName: routeParams.modelName}">
                 </div>
             </div>
         </form>

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/fb405ebd/webapp/app/routes.json
----------------------------------------------------------------------
diff --git a/webapp/app/routes.json b/webapp/app/routes.json
index 0b4af3f..31071c5 100644
--- a/webapp/app/routes.json
+++ b/webapp/app/routes.json
@@ -111,5 +111,13 @@
             "tab": "models",
             "controller": "ModelsCtrl"
         }
+    },
+    {
+        "url": "/models/edit/:modelName",
+        "params": {
+            "templateUrl": "partials/models/model_edit.html",
+            "tab": "models",
+            "controller": "ModelEditCtrl"
+        }
     }
 ]
\ No newline at end of file