瀏覽代碼

HUE-1838 [oozie] Add SLA tab on single workflow and coordinator dashboard page

Added chart and table to the tabs
Externalized SLA js functions
Enrico Berti 12 年之前
父節點
當前提交
094205e

+ 98 - 0
apps/oozie/src/oozie/templates/dashboard/list_oozie_coordinator.mako

@@ -275,6 +275,36 @@ ${ layout.menubar(section='coordinators', dashboard=True) }
 
       % if oozie_coordinator.has_sla:
       <div class="tab-pane" id="sla">
+        <div id="yAxisLabel" class="hide">${_('Time since Nominal Time')}</div>
+        <div id="slaChart"></div>
+        <table id="slaTable" class="table table-striped table-condensed hide">
+          <thead>
+            <th>${_('Status')}</th>
+            <th>${_('Nominal Time')}</th>
+            <th>${_('Expected Start')}</th>
+            <th>${_('Actual Start')}</th>
+            <th>${_('Expected End')}</th>
+            <th>${_('Actual End')}</th>
+            <th>${_('Expected Duration')}</th>
+            <th>${_('Actual Duration')}</th>
+            <th>${_('SLA')}</th>
+          </thead>
+          <tbody>
+          %for sla in oozie_slas:
+            <tr>
+              <td class="slaStatus">${sla['slaStatus']}</td>
+              <td><span class="nominalTime">${sla['nominalTime']}</span></td>
+              <td><span class="expectedStart">${sla['expectedStart']}</span></td>
+              <td><span class="actualStart">${sla['actualStart']}</span></td>
+              <td><span class="expectedEnd">${sla['expectedEnd']}</span></td>
+              <td><span class="actualEnd">${sla['actualEnd']}</span></td>
+              <td>${sla['expectedDuration']}</td>
+              <td>${sla['actualDuration']}</td>
+              <td><a href="${ url('oozie:list_oozie_sla') }#${ sla['id'] }"></a></td>
+            </tr>
+          %endfor
+          </tbody>
+        </table>
       </div>
       % endif
     </div>
@@ -309,10 +339,38 @@ ${ layout.menubar(section='coordinators', dashboard=True) }
 <link rel="stylesheet" href="/static/ext/css/codemirror.css">
 <script src="/static/ext/js/codemirror-xml.js"></script>
 
+% if oozie_coordinator.has_sla:
+<script src="/static/ext/js/moment.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/oozie/static/js/sla.utils.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/jquery/plugins/jquery.flot.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/jquery/plugins/jquery.flot.selection.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/jquery/plugins/jquery.flot.time.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/jquery.blueprint.js"></script>
+% endif
+
+
 <style type="text/css">
   .CodeMirror.cm-s-default {
     height:500px;
   }
+
+  #sla th {
+    vertical-align: middle !important;
+  }
+
+  #yAxisLabel {
+    -webkit-transform: rotate(270deg);
+    -moz-transform: rotate(270deg);
+    -o-transform: rotate(270deg);
+    writing-mode: lr-tb;
+    margin-left: -82px;
+    margin-top: 120px;
+    position: absolute;
+  }
+
+  #slaTable {
+    margin-top: 20px;
+  }
 </style>
 
 <script>
@@ -348,6 +406,16 @@ ${ layout.menubar(section='coordinators', dashboard=True) }
   var viewModel = new RunningCoordinatorActionsModel([]);
   ko.applyBindings(viewModel);
 
+  var CHART_LABELS = {
+    NOMINAL_TIME: "${_('Nominal Time')}",
+    EXPECTED_START: "${_('Expected Start')}",
+    ACTUAL_START: "${_('Actual Start')}",
+    EXPECTED_END: "${_('Expected End')}",
+    ACTUAL_END: "${_('Actual End')}",
+    TOOLTIP_ADDON: "${_('click for the SLA dashboard')}"
+  }
+  var slaTable;
+
   $(document).ready(function(){
     $("a[data-row-selector='true']").jHueRowSelector();
 
@@ -360,6 +428,25 @@ ${ layout.menubar(section='coordinators', dashboard=True) }
       $(this).removeClass("hide");
     });
 
+    % if oozie_coordinator.has_sla:
+    slaTable = $("#slaTable").dataTable({
+      "bPaginate": false,
+      "bLengthChange": false,
+      "bInfo": false,
+      "bAutoWidth": false,
+      "oLanguage": {
+        "sEmptyTable": "${_('No data available')}",
+        "sZeroRecords": "${_('No matching records')}"
+      }
+    });
+
+    $(".slaStatus").each(function () {
+      $(this).html(getSLAStatusLabel($(this).text()));
+    });
+
+    $(".dataTables_wrapper").css("min-height", "0");
+    $(".dataTables_filter").hide();
+    % endif
 
     var definitionEditor = $("#definitionEditor")[0];
 
@@ -376,6 +463,13 @@ ${ layout.menubar(section='coordinators', dashboard=True) }
       if ($(e.target).attr("href") == "#definition") {
         codeMirror.refresh();
       }
+      % if oozie_coordinator.has_sla:
+      if ($(e.target).attr("href") == "#sla") {
+        window.setTimeout(function () {
+          updateSLAChart(slaTable, CHART_LABELS, 30); // limits to 30 results
+        }, 100)
+      }
+      % endif
     });
 
     $(".confirmationModal").click(function(){
@@ -498,6 +592,10 @@ ${ layout.menubar(section='coordinators', dashboard=True) }
     function resizeLogs() {
       $("#log pre").css("overflow", "auto").height($(window).height() - $("#log pre").position().top - 80);
     }
+
+    if (window.location.hash == "#showSla") {
+      $("a[href='#sla']").click();
+    }
   });
 </script>
 

+ 17 - 100
apps/oozie/src/oozie/templates/dashboard/list_oozie_sla.mako

@@ -146,16 +146,17 @@ ${ layout.menubar(section='sla', dashboard=True) }
     </div>
   </div>
 </div>
-
+<script src="/oozie/static/js/bundles.utils.js" type="text/javascript" charset="utf-8"></script>
+<script src="/oozie/static/js/sla.utils.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/ext/js/jquery/plugins/jquery.flot.min.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/ext/js/jquery/plugins/jquery.flot.selection.min.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/ext/js/jquery/plugins/jquery.flot.time.min.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/js/jquery.blueprint.js"></script>
 
 <script type="text/javascript" charset="utf-8">
-  var slaTable;
   function performSearch(id) {
     if ((id != null || $("input[name='job_name']").val().trim()) != "" && slaTable) {
+      window.location.hash = (id != null ? id : $("input[name='job_name']").val().trim());
       $(".results").addClass("hide");
       $(".loader").removeClass("hide");
       $(".search-something").addClass("hide");
@@ -193,46 +194,16 @@ ${ layout.menubar(section='sla', dashboard=True) }
     }
   }
 
-  function getSLArow(row) {
-    return [
-      getStatusClass(row.slaStatus),
-      emptyStringIfNull(row.appName),
-      emptyStringIfNull(row.appType),
-      '<a href="' + row.appUrl + '" data-row-selector="true">' + row.id + '</a>',
-      emptyStringIfNull(row.nominalTime),
-      emptyStringIfNull(row.expectedStart),
-      emptyStringIfNull(row.actualStart),
-      emptyStringIfNull(row.expectedEnd),
-      emptyStringIfNull(row.actualEnd),
-      emptyStringIfNull(row.expectedDuration),
-      emptyStringIfNull(row.actualDuration),
-      emptyStringIfNull(row.jobStatus),
-      emptyStringIfNull(row.user),
-      emptyStringIfNull(row.lastModified)
-    ];
-  }
-
-  function getStatusClass(status) {
-    var klass = "";
-    if (['MET'].indexOf(status.toUpperCase()) > -1) {
-      klass = "label-success";
-    }
-    else if (['NOT_STARTED', 'IN_PROCESS'].indexOf(status.toUpperCase()) > -1) {
-      klass = "label-warning";
-    }
-    else {
-      klass = "label-important";
-    }
-    return '<span class="label ' + klass + '">' + status + '</span>'
-  }
-
-  function emptyStringIfNull(obj) {
-    if (obj != null && obj != undefined) {
-      return obj;
-    }
-    return "";
+  var CHART_LABELS = {
+    NOMINAL_TIME: "${_('Nominal Time')}",
+    EXPECTED_START: "${_('Expected Start')}",
+    ACTUAL_START: "${_('Actual Start')}",
+    EXPECTED_END: "${_('Expected End')}",
+    ACTUAL_END: "${_('Actual End')}",
+    TOOLTIP_ADDON: "${_('click for more details')}"
   }
 
+  var slaTable;
   $(document).ready(function () {
     $("a[data-row-selector='true']").jHueRowSelector();
 
@@ -281,72 +252,18 @@ ${ layout.menubar(section='sla', dashboard=True) }
     $(".dataTables_wrapper").css("min-height", "0");
     $(".dataTables_filter").hide();
 
-    function msToTime(millis) {
-      var s = Math.abs(millis);
-      var ms = s % 1000;
-      s = (s - ms) / 1000;
-      var secs = s % 60;
-      s = (s - secs) / 60;
-      var mins = s % 60;
-      var hrs = (s - mins) / 60;
-      return (millis < 0 ? "-" : "") + (hrs < 10 ? "0" : "") + hrs + ":" + (mins < 10 ? "0" : "") + mins + ":" + (secs < 10 ? "0" : "") + secs;
-    }
-
     $("a[data-toggle='tab']").on("shown", function (e) {
       if ($(e.target).attr("href") == "#chartTab") {
         window.setTimeout(function () {
-          $("#slaChart").jHueBlueprint("reset");
-          var rows = slaTable.fnGetNodes();
-          for (var i = 0; i < rows.length; i++) {
-            function getOptions(differenceCellValue, label, color) {
-              return {
-                data: [
-                  [moment($(rows[i]).find("td:eq(4)").html()).valueOf(), (moment(differenceCellValue).valueOf() - moment($(rows[i]).find("td:eq(4)").html()).valueOf())]
-                ],
-                label: label,
-                yAxisFormatter: function (val, axis) {
-                  return msToTime(val);
-                },
-                type: $.jHueBlueprint.TYPES.POINTCHART,
-                color: color,
-                isDateTime: true,
-                timeFormat: "<span style='color:" + ($(rows[i]).find("td:eq(4)").text().toUpperCase() == "MET" ? $.jHueBlueprint.COLORS.GREEN : $.jHueBlueprint.COLORS.RED) + "'>%Y-%m-%d %H:%M:%S</span>",
-                fill: false,
-                height: 300,
-                yTooltipFormatter: function (val) {
-                  return msToTime(val);
-                },
-                onItemClick: function (pos, item) {
-                  if (item) {
-                    location.href = $(slaTable.fnGetNodes()[item.dataIndex]).find("a").attr("href");
-                  }
-                }
-              }
-            }
-
-            if ($("#slaChart").data('plugin_jHueBlueprint') == null) {
-              $("#slaChart").jHueBlueprint(getOptions($(rows[i]).find("td:eq(4)").html(), (i == 0 ? "${_('Nominal Time')}" : null), $.jHueBlueprint.COLORS.ORANGE));
-            }
-            else {
-              $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("td:eq(4)").html(), (i == 0 ? "${_('Nominal Time')}" : null), $.jHueBlueprint.COLORS.ORANGE));
-            }
-            if ($(rows[i]).find("td:eq(5)").html().trim() != "") {
-              $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("td:eq(5)").html(), (i == 0 ? "${_('Expected Start')}" : null), $.jHueBlueprint.COLORS.BLUE));
-            }
-            if ($(rows[i]).find("td:eq(6)").html().trim() != "") {
-              $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("td:eq(6)").html(), (i == 0 ? "${_('Actual Start')}" : null), $.jHueBlueprint.COLORS.GREEN));
-            }
-            if ($(rows[i]).find("td:eq(7)").html().trim() != "") {
-              $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("td:eq(7)").html(), (i == 0 ? "${_('Expected End')}" : null), $.jHueBlueprint.COLORS.FALAFEL));
-            }
-            if ($(rows[i]).find("td:eq(8)").html().trim() != "") {
-              $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("td:eq(8)").html(), (i == 0 ? "${_('Actual End')}" : null), $.jHueBlueprint.COLORS.TURQUOISE));
-            }
-            $("#yAxisLabel").removeClass("hide");
-          }
+          updateSLAChart(slaTable, CHART_LABELS);
         }, 300)
       }
     });
+
+    if (window.location.hash != "") {
+      $("input[name='job_name']").val(window.location.hash.substr(1));
+      performSearch(window.location.hash.substr(1));
+    }
   });
 </script>
 

+ 101 - 3
apps/oozie/src/oozie/templates/dashboard/list_oozie_workflow.mako

@@ -288,6 +288,36 @@ ${ layout.menubar(section='workflows', dashboard=True) }
 
         % if oozie_workflow.has_sla:
         <div class="tab-pane" id="sla">
+          <div id="yAxisLabel" class="hide">${_('Time since Nominal Time')}</div>
+          <div id="slaChart"></div>
+          <table id="slaTable" class="table table-striped table-condensed hide">
+            <thead>
+              <th>${_('Status')}</th>
+              <th>${_('Nominal Time')}</th>
+              <th>${_('Expected Start')}</th>
+              <th>${_('Actual Start')}</th>
+              <th>${_('Expected End')}</th>
+              <th>${_('Actual End')}</th>
+              <th>${_('Expected Duration')}</th>
+              <th>${_('Actual Duration')}</th>
+              <th>${_('SLA')}</th>
+            </thead>
+            <tbody>
+            %for sla in oozie_slas:
+              <tr>
+                <td class="slaStatus">${sla['slaStatus']}</td>
+                <td><span class="nominalTime">${sla['nominalTime']}</span></td>
+                <td><span class="expectedStart">${sla['expectedStart']}</span></td>
+                <td><span class="actualStart">${sla['actualStart']}</span></td>
+                <td><span class="expectedEnd">${sla['expectedEnd']}</span></td>
+                <td><span class="actualEnd">${sla['actualEnd']}</span></td>
+                <td>${sla['expectedDuration']}</td>
+                <td>${sla['actualDuration']}</td>
+                <td><a href="${ url('oozie:list_oozie_sla') }#${ sla['id'] }"></a></td>
+              </tr>
+            %endfor
+            </tbody>
+          </table>
         </div>
         % endif
       </div>
@@ -324,10 +354,37 @@ ${ layout.menubar(section='workflows', dashboard=True) }
 <link rel="stylesheet" href="/static/ext/css/codemirror.css">
 <script src="/static/ext/js/codemirror-xml.js"></script>
 
+% if oozie_workflow.has_sla:
+<script src="/static/ext/js/moment.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/oozie/static/js/sla.utils.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/jquery/plugins/jquery.flot.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/jquery/plugins/jquery.flot.selection.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/jquery/plugins/jquery.flot.time.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/jquery.blueprint.js"></script>
+%endif
+
 <style type="text/css">
-.CodeMirror.cm-s-default {
-   height:700px;
-}
+  .CodeMirror.cm-s-default {
+    height: 700px;
+  }
+
+  #sla th {
+    vertical-align: middle !important;
+  }
+
+  #yAxisLabel {
+    -webkit-transform: rotate(270deg);
+    -moz-transform: rotate(270deg);
+    -o-transform: rotate(270deg);
+    writing-mode: lr-tb;
+    margin-left: -82px;
+    margin-top: 120px;
+    position: absolute;
+  }
+
+  #slaTable {
+    margin-top: 20px;
+  }
 </style>
 
 <script type="text/javascript">
@@ -365,6 +422,16 @@ ${ layout.menubar(section='workflows', dashboard=True) }
   var viewModel = new RunningWorkflowActionsModel([]);
   ko.applyBindings(viewModel);
 
+  var CHART_LABELS = {
+    NOMINAL_TIME: "${_('Nominal Time')}",
+    EXPECTED_START: "${_('Expected Start')}",
+    ACTUAL_START: "${_('Actual Start')}",
+    EXPECTED_END: "${_('Expected End')}",
+    ACTUAL_END: "${_('Actual End')}",
+    TOOLTIP_ADDON: "${_('click for the SLA dashboard')}"
+  }
+  var slaTable;
+
   $(document).ready(function() {
     var CURRENT_ZOOM = 1;
     $(document).keydown(function(e) {
@@ -391,6 +458,26 @@ ${ layout.menubar(section='workflows', dashboard=True) }
 
     $("*[rel=tooltip]").tooltip();
 
+    % if oozie_workflow.has_sla:
+    slaTable = $("#slaTable").dataTable({
+      "bPaginate": false,
+      "bLengthChange": false,
+      "bInfo": false,
+      "bAutoWidth": false,
+      "oLanguage": {
+        "sEmptyTable": "${_('No data available')}",
+        "sZeroRecords": "${_('No matching records')}"
+      }
+    });
+
+    $(".slaStatus").each(function () {
+      $(this).html(getSLAStatusLabel($(this).text()));
+    });
+
+    $(".dataTables_wrapper").css("min-height", "0");
+    $(".dataTables_filter").hide();
+    % endif
+
     $(".variable").each(function () {
       if ($(this).text().length > 25) {
         $(this).html($(this).text().substr(0, 24) + "&hellip;");
@@ -413,6 +500,13 @@ ${ layout.menubar(section='workflows', dashboard=True) }
       if ($(e.target).attr("href") == "#definition") {
         codeMirror.refresh();
       }
+      % if oozie_workflow.has_sla:
+      if ($(e.target).attr("href") == "#sla") {
+        window.setTimeout(function () {
+          updateSLAChart(slaTable, CHART_LABELS, 30); // limits to 30 results
+        }, 100)
+      }
+      % endif
     });
 
     $(".action-link").click(function(){
@@ -558,6 +652,10 @@ ${ layout.menubar(section='workflows', dashboard=True) }
       $("#log pre").css("overflow", "auto").height($(window).height() - $("#log pre").position().top - 80);
     }
 
+    if (window.location.hash == "#showSla") {
+      $("a[href='#sla']").click();
+    }
+
   });
 </script>
 

+ 18 - 0
apps/oozie/src/oozie/views/dashboard.py

@@ -208,11 +208,20 @@ def list_oozie_workflow(request, job_id, coordinator_job_id=None, bundle_job_id=
     }
     return HttpResponse(encode_json_for_js(return_obj), mimetype="application/json")
 
+  oozie_slas = []
+  if oozie_workflow.has_sla:
+    api = get_oozie(request.user, api_version="v2")
+    params = {
+      'id': oozie_workflow.id
+    }
+    oozie_slas = api.get_oozie_slas(**params)
+
   return render('dashboard/list_oozie_workflow.mako', request, {
     'history': history,
     'oozie_workflow': oozie_workflow,
     'oozie_coordinator': oozie_coordinator,
     'oozie_bundle': oozie_bundle,
+    'oozie_slas': oozie_slas,
     'hue_workflow': hue_workflow,
     'hue_coord': hue_coord,
     'parameters': parameters,
@@ -248,8 +257,17 @@ def list_oozie_coordinator(request, job_id, bundle_job_id=None):
     }
     return HttpResponse(encode_json_for_js(return_obj), mimetype="application/json")
 
+  oozie_slas = []
+  if oozie_coordinator.has_sla:
+    api = get_oozie(request.user, api_version="v2")
+    params = {
+      'id': oozie_coordinator.id
+    }
+    oozie_slas = api.get_oozie_slas(**params)
+
   return render('dashboard/list_oozie_coordinator.mako', request, {
     'oozie_coordinator': oozie_coordinator,
+    'oozie_slas': oozie_slas,
     'coordinator': coordinator,
     'oozie_bundle': oozie_bundle,
     'has_job_edition_permission': has_job_edition_permission,

+ 113 - 0
apps/oozie/static/js/sla.utils.js

@@ -0,0 +1,113 @@
+// 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 getSLAStatusLabel(status) {
+  var klass = "";
+  if (['MET'].indexOf(status.toUpperCase()) > -1) {
+    klass = "label-success";
+  }
+  else if (['NOT_STARTED', 'IN_PROCESS'].indexOf(status.toUpperCase()) > -1) {
+    klass = "label-warning";
+  }
+  else {
+    klass = "label-important";
+  }
+  return '<span class="label ' + klass + '">' + status + '</span>'
+}
+
+function getSLArow(row) {
+  return [
+    getSLAStatusLabel(row.slaStatus),
+    emptyStringIfNull(row.appName),
+    emptyStringIfNull(row.appType),
+    '<a href="' + row.appUrl + '#showSla" data-row-selector="true">' + row.id + '</a>',
+    '<span class="nominalTime">' + emptyStringIfNull(row.nominalTime) + '</span>',
+    '<span class="expectedStart">' + emptyStringIfNull(row.expectedStart) + '</span>',
+    '<span class="actualStart">' + emptyStringIfNull(row.actualStart) + '</span>',
+    '<span class="expectedEnd">' + emptyStringIfNull(row.expectedEnd) + '</span>',
+    '<span class="actualEnd">' + emptyStringIfNull(row.actualEnd) + '</span>',
+    emptyStringIfNull(row.expectedDuration),
+    emptyStringIfNull(row.actualDuration),
+    emptyStringIfNull(row.jobStatus),
+    emptyStringIfNull(row.user),
+    emptyStringIfNull(row.lastModified)
+  ];
+}
+
+function msToTime(millis) {
+  var s = Math.abs(millis);
+  var ms = s % 1000;
+  s = (s - ms) / 1000;
+  var secs = s % 60;
+  s = (s - secs) / 60;
+  var mins = s % 60;
+  var hrs = (s - mins) / 60;
+  return (millis < 0 ? "-" : "") + (hrs < 10 ? "0" : "") + hrs + ":" + (mins < 10 ? "0" : "") + mins + ":" + (secs < 10 ? "0" : "") + secs;
+}
+
+
+function updateSLAChart(slaTable, labels, limit) {
+  $("#slaChart").jHueBlueprint("reset");
+  var rows = slaTable.fnGetNodes();
+  for (var i = 0; i < (limit != null && limit < rows.length ? limit : rows.length); i++) {
+    function getOptions(differenceCellValue, label, color) {
+      return {
+        data: [
+          [moment($(rows[i]).find("span.nominalTime").html()).valueOf(), (moment(differenceCellValue).valueOf() - moment($(rows[i]).find("span.nominalTime").html()).valueOf())]
+        ],
+        label: label,
+        yAxisFormatter: function (val, axis) {
+          return msToTime(val);
+        },
+        type: $.jHueBlueprint.TYPES.POINTCHART,
+        color: color,
+        isDateTime: true,
+        timeFormat: "<span style='color:" + ($(rows[i]).find("td:eq(4)").text().toUpperCase() == "MET" ? $.jHueBlueprint.COLORS.GREEN : $.jHueBlueprint.COLORS.RED) + "'>%Y-%m-%d %H:%M:%S</span>",
+        fill: false,
+        height: 300,
+        tooltipAddon: labels.TOOLTIP_ADDON,
+        yTooltipFormatter: function (val) {
+          return msToTime(val);
+        },
+        onItemClick: function (pos, item) {
+          if (item && $(slaTable.fnGetNodes()[item.dataIndex]).find("a").length > 0) {
+            location.href = $(slaTable.fnGetNodes()[item.dataIndex]).find("a").attr("href");
+          }
+        }
+      }
+    }
+
+    if ($("#slaChart").data('plugin_jHueBlueprint') == null) {
+      $("#slaChart").jHueBlueprint(getOptions($(rows[i]).find("span.nominalTime").html(), (i == 0 ? labels.NOMINAL_TIME : null), $.jHueBlueprint.COLORS.ORANGE));
+    }
+    else {
+      $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("span.nominalTime").html(), (i == 0 ? labels.NOMINAL_TIME : null), $.jHueBlueprint.COLORS.ORANGE));
+    }
+    if ($(rows[i]).find("span.expectedStart").html().trim() != "") {
+      $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("span.expectedStart").html(), (i == 0 ? labels.EXPECTED_START : null), $.jHueBlueprint.COLORS.BLUE));
+    }
+    if ($(rows[i]).find("span.actualStart").html().trim() != "") {
+      $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("span.actualStart").html(), (i == 0 ? labels.ACTUAL_START : null), $.jHueBlueprint.COLORS.GREEN));
+    }
+    if ($(rows[i]).find("span.expectedEnd").html().trim() != "") {
+      $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("span.expectedEnd").html(), (i == 0 ? labels.EXPECTED_END : null), $.jHueBlueprint.COLORS.FALAFEL));
+    }
+    if ($(rows[i]).find("span.actualEnd").html().trim() != "") {
+      $("#slaChart").jHueBlueprint("add", getOptions($(rows[i]).find("span.actualEnd").html(), (i == 0 ? labels.ACTUAL_END : null), $.jHueBlueprint.COLORS.TURQUOISE));
+    }
+    $("#yAxisLabel").removeClass("hide");
+  }
+}

+ 2 - 1
desktop/core/static/js/jquery.blueprint.js

@@ -44,6 +44,7 @@
         height: 200,
         borderWidth: 0,
         tooltips: true,
+        tooltipAddon: "",
         enableSelection: false,
         isDateTime: false,
         timeFormat: null,
@@ -181,7 +182,7 @@
               x = item.series.data[item.dataIndex][0];
             }
 
-            showTooltip(item.pageX, item.pageY, "X: "+(_this.options.xTooltipFormatter?_this.options.xTooltipFormatter(x):x)+", Y: "+(_this.options.yTooltipFormatter?_this.options.yTooltipFormatter(y):y));
+            showTooltip(item.pageX, item.pageY, "X: " + (_this.options.xTooltipFormatter ? _this.options.xTooltipFormatter(x) : x) + ", Y: " + (_this.options.yTooltipFormatter ? _this.options.yTooltipFormatter(y) : y) + (_this.options.tooltipAddon != "" ? ", " + _this.options.tooltipAddon : ""));
           }
         } else {
           $("#jHueBlueprintTooltip").remove();