Эх сурвалжийг харах

[oozie] Skeleton of new workflow editor

Can drag and drop a Pig action
Romain Rigaux 11 жил өмнө
parent
commit
6523907bc7

+ 148 - 0
apps/oozie/src/oozie/templates/editor/workflow_editor.mako

@@ -0,0 +1,148 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. 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.
+<%!
+from desktop.views import commonheader, commonfooter
+from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="dashboard" file="/common_dashboard.mako" />
+
+${ commonheader(_("Workflow Editor"), "Oozie", user) | n,unicode }
+
+
+<%dashboard:layout_toolbar>
+  <%def name="widgets()">
+    <div data-bind="css: { 'draggable-widget': true },
+                    draggable: {data: draggableHiveAction(), isEnabled: true,
+                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
+                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+         title="${_('Hive Script')}" rel="tooltip" data-placement="top">
+         <a class="draggable-icon"><img src="/oozie/static/art/icon_beeswax_48.png" class="app-icon"></a>
+    </div>
+
+    <div data-bind="css: { 'draggable-widget': true},
+                    draggable: {data: draggablePigAction(), isEnabled: true,
+                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
+                        'stop': function(event, ui){$('.card-body').slideDown('fast'); }}}"
+         title="${_('Pig Script')}" rel="tooltip" data-placement="top">
+         <a class="draggable-icon"><img src="/oozie/static/art/icon_pig_48.png" class="app-icon"></a>
+    </div>
+
+    <div data-bind="css: { 'draggable-widget': true },
+                    draggable: {data: draggableJavaAction(), isEnabled: true,
+                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
+                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+         title="${_('Java program')}" rel="tooltip" data-placement="top">
+         <a class="draggable-icon"><i class="fa fa-file-code-o"></i></a>
+    </div>
+
+    <div data-bind="css: { 'draggable-widget': true },
+                    draggable: {data: draggableMapReduceAction(), isEnabled: true,
+                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
+                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+         title="${_('MapReduce job')}" rel="tooltip" data-placement="top">
+         <a class="draggable-icon"><i class="fa fa-file-code-o"></i></a>
+    </div>
+</%def>
+</%dashboard:layout_toolbar>
+
+
+<div class="search-bar">
+  <div class="pull-right" style="padding-right:50px">
+    % if user.is_superuser:
+      <a title="${ _('Edit') }" rel="tooltip" data-placement="bottom" data-bind="click: toggleEditing, css: {'btn': true, 'btn-inverse': isEditing}"><i class="fa fa-pencil"></i></a>
+      &nbsp;&nbsp;&nbsp;
+      <button type="button" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }" data-bind="click: $root.save, css: {'btn': true}"><i class="fa fa-save"></i></button>
+      <a class="btn" href="${ url('impala:new_search') }" title="${ _('New') }" rel="tooltip" data-placement="bottom" data-bind="css: {'btn': true}"><i class="fa fa-file-o"></i></a>
+    % endif
+  </div>
+</div>
+
+
+${ dashboard.layout_skeleton() }
+
+
+<script type="text/html" id="pig-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())">
+    <div data-bind="visible: $root.isEditing" style="margin-bottom: 20px">
+      <input type="text" data-bind="value: id" />
+      <input type="text" data-bind="value: name" />
+    </div>
+
+    <div>
+      <ul class="nav nav-tabs">
+        <li class="active"><a href="#action" data-toggle="tab">${ _('Pig') }</a></li>
+        <li><a href="#files" data-toggle="tab">${ _('Files') }</a></li>
+        <li><a href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a href="#credentials" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a href="#transitions" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" id="action">
+          <img src="/oozie/static/art/icon_pig_48.png" class="app-icon">
+        </div>
+        <div class="tab-pane" id="files">
+        </div>
+        <div class="tab-pane" id="sla">
+        </div>
+        <div class="tab-pane" id="credentials">
+        </div>
+        <div class="tab-pane" id="transitions">
+          OK --> []
+          KO --> []
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<link rel="stylesheet" href="/oozie/static/css/workflow-editor.css">
+<link rel="stylesheet" href="/static/ext/css/hue-filetypes.css">
+<link rel="stylesheet" href="/static/ext/css/hue-charts.css">
+<link rel="stylesheet" href="/static/ext/chosen/chosen.min.css">
+
+
+${ dashboard.import_layout() }
+
+<script src="/static/ext/js/bootstrap-editable.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/hue.utils.js"></script>
+<script src="/static/js/ko.editable.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/chosen/chosen.jquery.min.js" type="text/javascript" charset="utf-8"></script>
+
+${ dashboard.import_bindings() }
+
+<script src="/oozie/static/js/workflow-editor.ko.js" type="text/javascript" charset="utf-8"></script>
+
+
+<script type="text/javascript">
+  var viewModel = new WorkflowEditorViewModel(${ layout_json | n,unicode }, ${ workflow_json | n,unicode });
+  ko.applyBindings(viewModel);
+
+  viewModel.init();
+
+  function columnDropAdditionalHandler(widget) {
+    widgetDraggedAdditionalHandler(widget);
+  }
+
+  function widgetDraggedAdditionalHandler(widget) {
+    viewModel.workflow.addNode(widget);
+  }
+</script>
+
+${ commonfooter(messages) | n,unicode }

+ 7 - 0
apps/oozie/src/oozie/urls.py

@@ -67,6 +67,13 @@ urlpatterns = patterns(
 )
 
 
+urlpatterns += patterns(
+  'oozie.views.editor2',
+
+  url(r'^editor/workflow$', 'workflow_editor', name='workflow_editor'),
+)
+
+
 urlpatterns += patterns(
   'oozie.views.api',
 

BIN
apps/oozie/static/art/icon_beeswax_48.png


BIN
apps/oozie/static/art/icon_pig_48.png


+ 295 - 0
apps/oozie/static/css/workflow-editor.css

@@ -0,0 +1,295 @@
+/*
+ Licensed to Cloudera, Inc. under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  Cloudera, Inc. 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.
+*/
+
+.dropdown-menu li {
+  font-size: 14px;
+}
+
+.dropdown a.dropdown-toggle {
+  padding-left: 10px;
+  padding-right: 10px;
+}
+
+.search-bar {
+  background-color: #F5F5F5;
+  border-bottom: 1px solid #e3e3e3;
+  -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+  -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+  margin: 0;
+  padding: 10px;
+  padding-left: 20px;
+  padding-right: 20px;
+  text-align: left;
+  width: 100%;
+  position: fixed;
+  top: 28px;
+  z-index: 1000;
+}
+
+.search-bar input {
+  margin-left: 10px;
+  padding-right: 20px;
+}
+
+.search-bar ul li {
+  text-align: right;
+}
+
+.sort-by {
+  font-size: 11px;
+  height: 24px;
+  width: 120px;
+  margin-top: -2px;
+}
+
+.nav-header {
+  padding-left: 0 !important;
+  padding-top: 8px;
+}
+
+.facet-list {
+  list-style: none outside none;
+  padding: 0;
+  margin: 0;
+}
+
+.facet-list li {
+  padding-left: 8px;
+  padding-right: 3px;
+}
+
+.facet-list li a {
+  word-wrap: break-word;
+}
+
+.counter {
+  color: #999999;
+  font-size: 13px;
+}
+
+.pagination {
+  border-radius: 4px 4px 4px 4px;
+  list-style: none outside none;
+  margin: 0 0 20px;
+  padding: 8px 15px;
+}
+
+.results.container {
+  width: 1180px;
+}
+
+.results.span2 {
+  width: 180px;
+}
+
+.results.span10 {
+  width: 980px;
+}
+
+.results.span12 {
+  width: 1180px;
+}
+
+.errorlist {
+  display: none;
+}
+
+.breadcrumb {
+  background-color: #FFFFFF;
+  padding-left: 0;
+  margin-bottom: 0;
+}
+
+#result-container {
+  margin-top: 20px;
+}
+
+.facetItem {
+  display: none;
+}
+
+.selectMask {
+  background-color: #F9F9F9;
+  background-image: linear-gradient(to bottom, #FFFFFF, #F1F1F1);
+  background-repeat: repeat-x;
+  border: 1px solid #C0C0C0;
+  border-right-color: rgba(0, 0, 0, 0.1);
+  border-radius: 2px;
+  border-bottom-right-radius: 0;
+  border-top-right-radius: 0;
+  box-shadow: none;
+  color: #444444;
+  display: inline-block;
+  font-size: 13px;
+  height: 29px;
+  line-height: 29px;
+  margin: 0;
+  margin-left: 5px;
+  min-height: 29px;
+  min-width: 120px;
+  padding: 0 8px;
+  text-shadow: none;
+  vertical-align: middle;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.download-btn-group {
+  min-width: auto;
+}
+
+.download-btn-group > ul > li {
+  text-align: left;
+}
+
+em {
+  font-weight: bold;
+  background-color: yellow;
+}
+
+.fields-chooser li {
+  cursor: pointer;
+  margin-bottom: 10px;
+}
+
+.fields-chooser li .badge {
+  font-weight: normal;
+  font-size: 12px;
+}
+
+.badge-left {
+  border-radius: 9px 0px 0px 9px;
+  padding-right: 5px;
+}
+
+.badge-right {
+  border-radius: 0px 9px 9px 0px;
+  padding-left: 5px;
+}
+
+.trash-filter {
+  cursor: pointer;
+}
+
+.editable-click {
+  cursor: pointer;
+}
+
+.CodeMirror {
+  border: 1px dotted #DDDDDD;
+}
+
+[contenteditable=true] {
+  border: 1px dotted #DDDDDD;
+  outline: 0;
+  margin-top: 20px;
+  margin-bottom: 20px;
+  min-height: 150px;
+}
+
+[contenteditable=true] [class*="span"], .tmpl [class*="span"] {
+  background-color: #eee;
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+  min-height: 40px;
+  line-height: 40px;
+  background-color: #F3F3F3;
+  border: 2px dashed #DDD;
+}
+
+.tmpl {
+  margin: 10px;
+  height: 60px;
+}
+
+.tmpl [class*="span"] {
+  color: #999;
+  font-size: 12px;
+  text-align: center;
+  font-weight: bold;
+}
+
+.editor-title {
+  font-weight: bold;
+  color: #262626;
+  border-bottom: 1px solid #338bb8;
+}
+
+.document-details {
+  background-color: #F6F6F6;
+  padding: 10px;
+  border: 1px solid #e5e5e5;
+}
+
+.result-row:nth-child(even) {
+  background-color: #F6F6F6;
+}
+
+.result-row td {
+  white-space: nowrap;
+  padding: 3px;
+}
+
+#result-container thead tr {
+  border-bottom: 1px solid #e5e5e5;
+}
+
+.demi-modal {
+  min-height: 80px;
+}
+
+.filter-box {
+  float: left;
+  margin-right: 10px;
+  margin-bottom: 10px;
+  background-color: #F6F6F6;
+  padding: 5px;
+  border: 1px solid #d8d8d8;
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+}
+
+.field-selector {
+  display: inline-block;
+  margin-left: 3px;
+  min-height: 20px;
+  line-height: 20px
+}
+
+.field-selector.hoverable {
+  cursor: pointer;
+}
+
+.field-selector.hoverable:hover {
+  background-color: #DDD;
+}
+
+.draggable-icon {
+  cursor: move;
+}
+
+.chosen-container .chosen-results li {
+  text-align: left;
+}
+
+#emptyDashboard {
+  right: 70px!important;
+}

+ 150 - 0
apps/oozie/static/js/workflow-editor.ko.js

@@ -0,0 +1,150 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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.
+
+function magicLayout(vm) {
+  loadLayout(vm, vm.initial.layout);
+  $(document).trigger("magicLayout");
+}
+
+function loadLayout(viewModel, json_layout) {
+  var _columns = [];
+
+  $(json_layout).each(function (cnt, json_col) {
+    var _rows = [];
+    $(json_col.rows).each(function (rcnt, json_row) {
+      var row = new Row([], viewModel);
+      $(json_row.widgets).each(function (wcnt, widget) {
+        row.addWidget(new Widget({
+          size:widget.size,
+          id: widget.id,
+          name: widget.name,
+          widgetType: widget.widgetType,
+          properties: widget.properties,
+          offset: widget.offset,
+          loading: true,
+          vm: viewModel
+        }));
+      });
+      _rows.push(row);
+    });
+    var column = new Column(json_col.size, _rows);
+    _columns = _columns.concat(column);
+  });
+
+  viewModel.columns(_columns);
+}
+
+// End dashboard lib
+
+var Node = function (node_json) {
+  var self = this;
+
+  self.id = ko.observable(typeof node_json.id != "undefined" && node_json.id != null ? node_json.id : UUID());
+  self.name = ko.observable(typeof node_json.name != "undefined" && node_json.name != null ? node_json.name : "");
+}
+
+var Workflow = function (vm, workflow) {
+  var self = this;
+
+  self.id = ko.observable(typeof workflow.id != "undefined" && workflow.id != null ? workflow.id : UUID());
+  self.name = ko.observable(typeof workflow.name != "undefined" && workflow.name != null ? workflow.name : "");
+  self.nodes = ko.observableArray([]);
+
+  self.addNode = function(widget) {
+    if (self.nodes().length == 0) {
+      var node = new Node(ko.mapping.toJS(widget));
+      self.nodes.push(node);
+    }
+  }
+
+  self.getNodeById = function (node_id) {
+    var _node = null;
+    $.each(self.nodes(), function (index, node) {
+      if (node.id() == node_id) {
+        _node = node;
+        return false;
+      }
+    });
+    return _node;
+  }
+
+}
+
+var WorkflowEditorViewModel = function (layout_json, workflow_json) {
+  var self = this;
+
+  self.isEditing = ko.observable(true);
+  self.toggleEditing = function () {
+    self.isEditing(! self.isEditing());
+  };
+
+  self.columns = ko.observable([]);
+  self.previewColumns = ko.observable("");
+  self.workflow = new Workflow(self, workflow_json);
+
+  self.inited = ko.observable(self.columns().length > 0);
+  self.init = function(callback) {
+    loadLayout(self, layout_json);
+  }
+
+  self.getWidgetById = function (widget_id) {
+    var _widget = null;
+    $.each(self.columns(), function (i, col) {
+      $.each(col.rows(), function (j, row) {
+        $.each(row.widgets(), function (z, widget) {
+          if (widget.id() == widget_id){
+            _widget = widget;
+          }
+        });
+      });
+    });
+    return _widget;
+  }
+
+  self.save = function () {
+    $.post("/impala/dashboard/save", {
+        "dashboard": ko.mapping.toJSON(self.dashboard),
+        "layout": ko.mapping.toJSON(self.columns)
+    }, function (data) {
+      if (data.status == 0) {
+        self.dashboard.id(data.id);
+        $(document).trigger("info", data.message);
+        if (window.location.search.indexOf("dashboard") == -1) {
+          window.location.hash = '#dashboard=' + data.id;
+        }
+      }
+      else {
+        $(document).trigger("error", data.message);
+     }
+   }).fail(function (xhr, textStatus, errorThrown) {
+      $(document).trigger("error", xhr.responseText);
+    });
+  };
+
+  function bareWidgetBuilder(name, type){
+    return new Widget({
+      size: 12,
+      id: UUID(),
+      name: name,
+      widgetType: type
+    });
+  }
+
+  self.draggableHiveAction = ko.observable(bareWidgetBuilder("Hive Script", "hive-widget"));
+  self.draggablePigAction = ko.observable(bareWidgetBuilder("Pig Script", "pig-widget"));
+  self.draggableJavaAction = ko.observable(bareWidgetBuilder("Java program", "java-widget"));
+  self.draggableMapReduceAction = ko.observable(bareWidgetBuilder("MapReduce job", "mapreduce-widget"));
+};

+ 12 - 0
desktop/core/static/js/hue.utils.js

@@ -157,3 +157,15 @@ if (!Object.keys) {
     };
   }());
 }
+
+
+function s4() {
+  return Math.floor((1 + Math.random()) * 0x10000)
+      .toString(16)
+      .substring(1);
+}
+
+
+function UUID() {
+  return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+}