Commit 2e13a9e9 authored by Romain Courteaud's avatar Romain Courteaud Committed by Jérome Perrin

Migrate job schedule widget.

parent 193da8aa
...@@ -227,6 +227,12 @@ module.exports = function (grunt) { ...@@ -227,6 +227,12 @@ module.exports = function (grunt) {
src: 'http://dhtmlx.com/x/download/regular/dhtmlxGantt.zip', src: 'http://dhtmlx.com/x/download/regular/dhtmlxGantt.zip',
dest: '<%= global_config.tmp %>/dhtmlxGantt.zip' dest: '<%= global_config.tmp %>/dhtmlxGantt.zip'
}, },
momentjs: {
src: 'https://raw.githubusercontent.com/moment/moment/2.5.1/min/' +
'moment-with-langs.min.js',
relative_dest: 'lib/moment.js',
dest: '<%= global_config.dest %>/<%= curl.momentjs.relative_dest %>'
},
handsontablejs: { handsontablejs: {
src: 'https://raw.githubusercontent.com/warpech/' + src: 'https://raw.githubusercontent.com/warpech/' +
'jquery-handsontable/v0.10.5/dist/jquery.handsontable.full.js', 'jquery-handsontable/v0.10.5/dist/jquery.handsontable.full.js',
......
...@@ -74,7 +74,6 @@ ...@@ -74,7 +74,6 @@
<li><a href="#station_utilisation_graph">Stations Utilization</a></li> <li><a href="#station_utilisation_graph">Stations Utilization</a></li>
<li><a href="#capacity_utilisation_graph">Capacity Utilization</a></li> <li><a href="#capacity_utilisation_graph">Capacity Utilization</a></li>
<li><a href="#job_gantt">Job Gantt</a></li> <li><a href="#job_gantt">Job Gantt</a></li>
<li><a href="#job_schedule_spreadsheet">Job Schedule</a></li>
</ul> </ul>
<div id="capacity_utilisation_graph"> <div id="capacity_utilisation_graph">
...@@ -82,8 +81,6 @@ ...@@ -82,8 +81,6 @@
<div id="capacity_graphs"></div> <div id="capacity_graphs"></div>
</div> </div>
<div id="job_schedule_spreadsheet" style="overflow: scroll;"></div>
</div> </div>
<pre id="error"> <pre id="error">
</pre> </pre>
...@@ -97,13 +94,6 @@ ...@@ -97,13 +94,6 @@
<script type="text/javascript" src="lib/jquery.jsPlumb-1.5.4-min.js"></script> <script type="text/javascript" src="lib/jquery.jsPlumb-1.5.4-min.js"></script>
<!-- /DEP --> <!-- /DEP -->
<!-- flot -->
<script type="text/javascript" src="lib/jquery.flot.js"></script>
<script type="text/javascript" src="lib/jquery.flot.stack.js"></script>
<!-- nice display of dates -->
<script type="text/javascript" src="lib/moment-with-langs.min.js"></script>
<div id="dialog-form" title="Configure"> <div id="dialog-form" title="Configure">
<form> <form>
<fieldset id="dialog-fieldset"> <fieldset id="dialog-fieldset">
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
(function (scope, $, jsPlumb, console) { (function (scope, $, jsPlumb, console) {
"use strict"; "use strict";
<<<<<<< HEAD
function capacity_utilisation_graph_widget(input_data, output_data) { function capacity_utilisation_graph_widget(input_data, output_data) {
var available_capacity_by_station = {}, var available_capacity_by_station = {},
capacity_usage_by_station = {}; capacity_usage_by_station = {};
...@@ -90,201 +91,6 @@ ...@@ -90,201 +91,6 @@
} }
}; };
function exit_stat_widget(input_data, output_data) {
var exit_stat = $("#exit_stat").find('div').empty();
$.each(output_data.elementList, function(idx, el){
if (el._class == 'Dream.Exit'){
var text = exit_stat.html() + "<table><tr><th colspan='2'>" + (
el.name || el.id) + "</th></tr>";
$.each(el.results, function(metric, value){
if (metric == 'intervalThroughputList') {
var attainment_list = [],
general = input_data.general,
throughputTarget = parseFloat(general.throughputTarget);
text += "<tr><td>Daily Attainment</td><td>"
$.each(value, function(i, intervalValue) {
var icon = "fa-frown-o";
attainment_list.push((intervalValue/throughputTarget));
if (intervalValue > throughputTarget) {
icon = "fa-smile-o"
}
text += intervalValue + ' <i class="fa ' + icon + '"/><br/>';
})
text += "</td></tr>";
text += "<tr><td>Average Daily Line Attainment</td><td>" + (
(attainment_list.reduce(function(a, b){return a+b}) / attainment_list.length ) * 100).toFixed(2) + "%</td></tr>";
} else {
if (typeof value == "object") {
if (value.ub == value.lb) {
value = value.ub;
} else {
var ci_text = "<table width='100%'><tbody>"
ci_text += "<tr><td>Average</td><td>" + value.avg.toFixed(2) + "</td></tr>"
ci_text += "<tr><td>Lower Bound</td><td>" + value.lb.toFixed(2) + "</td></tr>"
ci_text += "<tr><td>Upper Bound</td><td>" + value.ub.toFixed(2) + "</td></tr>"
ci_text += "</tbody></table>"
value = ci_text;
}
}
if (typeof value == "number") {
value = value.toFixed(2)
}
// Rename some metric to something more meaningful
if (metric == "lifespan" ){
metric = "Cycle Time"
}
if (metric == "takt_time" ){
metric = "Average Departure Rate"
}
text += "<tr><td>" + metric + "</td><td>" + value + "</td></tr>";
}
})
exit_stat.html(text + "</table>");
}
})
}
function job_schedule_spreadsheet_widget(input_data, output_data) {
var now = new Date();
// XXX why ?
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
var spreadsheet_data = [],
spreadsheet_header = [
[
"Jobs",
"ID",
"Project Manager",
"Due Date",
"Priority",
"Entrance Time",
"Processing Time",
"Station ID",
"Step No."
]
],
simulation_start_date = new Date(input_data.general.currentDate || now.getTime());
// TODO: time unit for later
// or an utility function to map sim time to real time & vice
// versa.
$.each(output_data.elementList, function(idx, obj) {
if (obj._class === 'Dream.Job' || obj._class === 'Dream.CapacityProject') {
var input_job = null, input_order = null;
// find the input order and order component for this job
// XXX this has no real meaning with capacity project
for (var node_id in input_data.nodes) {
var node = input_data.nodes[node_id];
if (node.wip) {
for (var i=0; i<node.wip.length; i++) {
var order = node.wip[i];
if (order.id == obj.id) {
input_job = input_order = order;
}
if (input_job === null && order.componentsList) {
for (var j=0; j<order.componentsList.length; j++){
var component = order.componentsList[j];
if (component.id == obj.id){
input_order = order;
input_job = component;
}
}
}
}
}
}
// XXX does not make sense in the case of capacity project
var due_date = new Date(simulation_start_date.getTime() +
input_order.dueDate * 1000 * 3600);
$.each(obj['results']['schedule'], function (i, schedule) {
var entrance_date = new Date(simulation_start_date.getTime() +
// TODO: time unit
schedule['entranceTime'] * 1000 * 3600);
var duration = 0;
if (schedule['exitTime']) {
duration = (schedule['exitTime'] - schedule['entranceTime']) * 24;
} else {
// When duration is not returned by ManPy, it is calculated by
// difference of entranceTime of this step and entranceTime of the
// next step, or completionTime when this is the last step
if (i+1 == obj['results']['schedule'].length) {
duration = obj['results']['completionTime'] - schedule['entranceTime'];
} else {
duration = obj['results']['schedule'][i+1]['entranceTime'] - schedule['entranceTime'];
}
}
var name = "";
if (obj._class == 'Dream.CapacityProject') {
name = input_order.name + '-' + schedule['stationId'];
} else {
name = input_order.name + "-" + input_job.name;
}
spreadsheet_data.push([
// XXX this label is incorrect for design step, during design
// phase we still have an order and not an order component.
name,
obj['id'],
input_order.manager,
moment(due_date).format("YYYY/MM/DD"),
input_order.priority,
moment(entrance_date).format("MMM/DD HH:mm"),
duration,
schedule['stationId'],
i
]);
});
}
});
if (spreadsheet_data.length > 1) {
var job_schedule_spreadsheet = $('#job_schedule_spreadsheet');
// Sort the spreadsheet data to an order convenient for end users
// TODO: search for a default cmp in javascript
spreadsheet_data.sort( function(a, b) {
var result = 0,
order_id_a, order_id_b, entrance_a, entrance_b;
order_id_a = a[0].split('-')[0];
order_id_b = b[0].split('-')[0];
if (order_id_a !== order_id_b) {
if (order_id_a > order_id_b) {
result = 1;
} else {
result = -1;
}
} else {
entrance_a = a[4];
entrance_b = b[4];
if (entrance_a > entrance_b) {
result = 1;
} else if (entrance_a < entrance_b) {
result = -1;
} else {
result = 0;
}
}
return result;
});
job_schedule_spreadsheet.show();
job_schedule_spreadsheet.handsontable({
data: spreadsheet_header.concat(spreadsheet_data),
width: function () {
return $(window).width() -
job_schedule_spreadsheet.offset().left +
$(window).scrollLeft();
},
readOnly: true
});
job_schedule_spreadsheet.find('.htCore').width(job_schedule_spreadsheet.width());
}
}
scope.Dream = function (configuration) { scope.Dream = function (configuration) {
var that = jsonPlumb(), var that = jsonPlumb(),
priv = {}; priv = {};
......
...@@ -27,6 +27,10 @@ ...@@ -27,6 +27,10 @@
), ),
gadget.whoWantToDisplayThisDocumentPage("exit_stat", key), gadget.whoWantToDisplayThisDocumentPage("exit_stat", key),
gadget.whoWantToDisplayThisDocumentPage("job_gantt", key), gadget.whoWantToDisplayThisDocumentPage("job_gantt", key),
gadget.whoWantToDisplayThisDocumentPage(
"job_schedule_spreadsheet",
key
),
gadget.whoWantToDisplayThisDocumentPage("debug_json", key) gadget.whoWantToDisplayThisDocumentPage("debug_json", key)
]); ]);
}) })
...@@ -39,7 +43,8 @@ ...@@ -39,7 +43,8 @@
{link: result_list[4], title: "Queues Statistics"}, {link: result_list[4], title: "Queues Statistics"},
{link: result_list[5], title: "Exit Statistics"}, {link: result_list[5], title: "Exit Statistics"},
{link: result_list[6], title: "Job Gantt"}, {link: result_list[6], title: "Job Gantt"},
{link: result_list[7], title: "Debug JSON"} {link: result_list[7], title: "Job Schedule"},
{link: result_list[8], title: "Debug JSON"}
]; ];
}); });
}); });
......
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Job Schedule</title>
<script src="../<%= copy.rsvp.relative_dest %>" type="text/javascript"></script>
<script src="../<%= copy.renderjs.relative_dest %>" type="text/javascript"></script>
<script src="../<%= curl.momentjs.relative_dest %>" type="text/javascript"></script>
<script src="document_page_mixin.js" type="text/javascript"></script>
<script src="job_schedule_spreadsheet.js" type="text/javascript"></script>
</head>
<body>
<div data-gadget-url="../handsontable/index.html"
data-gadget-scope="tableeditor"></div>
</body>
</html>
/*global console, rJS, RSVP, initDocumentPageMixin, moment */
/*jslint nomen: true */
(function (window, rJS, RSVP, initDocumentPageMixin, moment) {
"use strict";
function job_schedule_spreadsheet_widget(all_data) {
var now = new Date(),
input_data = all_data.input,
output_data = all_data.result,
spreadsheet_data = [],
spreadsheet_header = [[
"Jobs",
"ID",
"Project Manager",
"Due Date",
"Priority",
"Entrance Time",
"Processing Time",
"Station ID",
"Step No."
]],
simulation_start_date = new Date(
input_data.general.currentDate || now.getTime()
),
i,
j,
k,
obj,
node,
component,
order,
node_id,
due_date,
entrance_date,
duration,
schedule,
input_job = null,
input_order = null;
// XXX why ?
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
// XXX: time unit for later
// or an utility function to map sim time to real time & vice
// versa.
for (i = 0; i < output_data.elementList.length; i += 1) {
obj = output_data.elementList[i];
if (obj._class === 'Dream.Job') {
input_job = null;
input_order = null;
// find the input order and order component for this job
for (node_id in input_data.nodes) {
if (input_data.nodes.hasOwnProperty(node_id)) {
node = input_data.nodes[node_id];
if (node.wip) {
for (j = 0; j < node.wip.length; j += 1) {
order = node.wip[j];
if (order.id === obj.id) {
input_job = input_order = order;
}
if (input_job === null) {
for (k = 0; k < order.componentsList.length; k += 1) {
component = order.componentsList[k];
if (component.id === obj.id) {
input_order = order;
input_job = component;
}
}
}
}
}
}
}
due_date = new Date(simulation_start_date.getTime() +
input_order.dueDate * 1000 * 3600);
for (j = 0; j < obj.results.schedule.length; j += 1) {
schedule = obj.results.schedule[j];
entrance_date = new Date(simulation_start_date.getTime() +
// XXX: time unit
schedule.entranceTime * 1000 * 3600);
duration = 0;
// Duration is calculated by difference of entranceTime of this
// step and entranceTime of the next step, or completionTime when
// this is the last step
if (j + 1 === obj.results.schedule.length) {
duration = obj.results.completionTime - schedule.entranceTime;
} else {
duration = obj.results.schedule[j + 1].entranceTime -
schedule.entranceTime;
}
spreadsheet_data.push([
// XXX this label is incorrect for design step, during design
// phase we still have an order and not an order component.
input_order.name + "-" + input_job.name,
obj.id,
input_order.manager,
moment(due_date).format("YYYY/MM/DD"),
input_order.priority,
moment(entrance_date).format("MMM/DD HH:mm"),
duration,
schedule.stationId,
j
]);
}
}
}
if (spreadsheet_data.length > 1) {
// Sort the spreadsheet data to an order convenient for end users
// XXX: search for a default cmp in javascript
spreadsheet_data.sort(function (a, b) {
var result = 0,
order_id_a,
order_id_b,
entrance_a,
entrance_b;
order_id_a = a[0].split('-')[0];
order_id_b = b[0].split('-')[0];
if (order_id_a !== order_id_b) {
if (order_id_a > order_id_b) {
result = 1;
} else {
result = -1;
}
} else {
entrance_a = a[4];
entrance_b = b[4];
if (entrance_a > entrance_b) {
result = 1;
} else if (entrance_a < entrance_b) {
result = -1;
} else {
result = 0;
}
}
return result;
});
}
return spreadsheet_header.concat(spreadsheet_data);
}
var gadget_klass = rJS(window);
initDocumentPageMixin(gadget_klass);
gadget_klass
/////////////////////////////////////////////////////////////////
// ready
/////////////////////////////////////////////////////////////////
// Init local properties
.ready(function (g) {
g.props = {};
})
// Assign the element to a variable
.ready(function (g) {
return g.getElement()
.push(function (element) {
g.props.element = element;
});
})
/////////////////////////////////////////////////////////////////
// Acquired methods
/////////////////////////////////////////////////////////////////
.declareAcquiredMethod("aq_getAttachment", "jio_getAttachment")
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod("render", function (options) {
var jio_key = options.id,
gadget = this;
gadget.props.jio_key = jio_key;
return new RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.aq_getAttachment({
"_id": jio_key,
"_attachment": "simulation.json"
}),
gadget.getDeclaredGadget("tableeditor")
]);
})
.push(function (result_list) {
return result_list[1].render(
JSON.stringify(
job_schedule_spreadsheet_widget(
// XXX Hardcoded result
JSON.parse(result_list[0])[0]
)
)
);
// job_schedule_spreadsheet.show();
// job_schedule_spreadsheet.handsontable({
// data: spreadsheet_header.concat(spreadsheet_data),
// width: function () {
// return $(window).width() -
// job_schedule_spreadsheet.offset().left +
// $(window).scrollLeft();
// },
// readOnly: true
// });
// job_schedule_spreadsheet.find('.htCore')
// .width(job_schedule_spreadsheet.width());
});
})
.declareMethod("startService", function () {
return this.getDeclaredGadget("tableeditor")
.push(function (tableeditor) {
return tableeditor.startService();
});
});
}(window, rJS, RSVP, initDocumentPageMixin, moment));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment