niv-openerp 2011-05-13 17:46:28 +02:00
commit 6eb51adbac
5 changed files with 302 additions and 356 deletions

View File

@ -3,8 +3,8 @@
"version": "2.0",
"depends": ['base'],
"js": [
'static/lib/dhtmlxGantt/codebase/dhtmlxcommon.js',
'static/lib/dhtmlxGantt/codebase/dhtmlxgantt.js',
'static/lib/dhtmlxGantt/sources/dhtmlxcommon.js',
'static/lib/dhtmlxGantt/sources/dhtmlxgantt.js',
'static/src/js/gantt.js'
],
"css": ['static/lib/dhtmlxGantt/codebase/dhtmlxgantt.css'],

View File

@ -1,184 +1,11 @@
import glob, os
from xml.etree import ElementTree
import math
import simplejson
import openerpweb
import time
import datetime
from base.controllers.main import Xml2Json
from base.controllers.main import View
COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
'#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
'#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
'#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900', ]
_colorline = ['#%02x%02x%02x' % (25 + ((r + 10) % 11) * 23, 5 + ((g + 1) % 11) * 20, 25 + ((b + 4) % 11) * 23) for r in range(11) for g in range(11) for b in range(11) ]
def choice_colors(n):
if n > len(COLOR_PALETTE):
return _colorline[0:-1:len(_colorline) / (n + 1)]
elif n:
return COLOR_PALETTE[:n]
return []
class GanttView(openerpweb.Controller):
class GanttView(View):
_cp_path = "/base_gantt/ganttview"
date_start = None
date_delay = None
date_stop = None
color_field = None
day_length = 8
fields = {}
events = []
calendar_fields = {}
ids = []
model = ''
domain = []
context = {}
event_res = []
colors = {}
color_values = []
@openerpweb.jsonrequest
def load(self, req, model, view_id):
m = req.session.model(model)
r = m.fields_view_get(view_id, 'gantt')
r["arch"] = Xml2Json.convert_to_structure(r["arch"])
return {'fields_view':r}
@openerpweb.jsonrequest
def get_events(self, req, **kw):
fields_view = self.fields_view_get(req, model, view_id, 'gantt')
return {'fields_view':fields_view}
self.model = kw['model']
self.fields = kw['fields']
self.day_length = kw['day_length']
self.calendar_fields = kw['calendar_fields']
self.color_field = kw.get('color_field') or self.color_field or None
self.fields[self.color_field] = ""
self.colors = kw.get('colors') or {}
self.text = self.calendar_fields['text']['name']
self.date_start = self.calendar_fields['date_start']['name']
self.fields[self.date_start] = ""
if self.calendar_fields.get('date_stop'):
self.date_stop = self.calendar_fields['date_stop']['name']
self.fields[self.date_stop] = ""
if self.calendar_fields.get('date_delay'):
self.date_delay = self.calendar_fields['date_delay']['name']
self.fields[self.date_delay] = ""
if self.calendar_fields.get('parent'):
self.parent = self.calendar_fields['parent']['name']
self.fields[self.parent] = ""
model = req.session.model(self.model)
event_ids = model.search([])
return self.create_event(event_ids, model)
def create_event(self, event_ids, model):
self.events = model.read(event_ids, self.fields.keys())
result = []
for evt in self.events:
event_res = {}
key = evt[self.color_field]
name = key
value = key
if isinstance(key, list): # M2O, XMLRPC returns List instead of Tuple
name = key[0]
value = key[-1]
evt[self.color_field] = key = key[-1]
if isinstance(key, tuple): # M2O
value, name = key
self.colors[key] = (name, value, None)
st_date = evt.get(self.date_start)
if st_date:
self.set_format(st_date)
if self.date_delay:
duration = evt.get(self.date_delay)
else:
en_date = evt.get(self.date_stop)
duration = (time.mktime(time.strptime(en_date, self.format))-\
time.mktime(time.strptime(st_date, self.format)))/ (60 * 60)
if duration > self.day_length :
d = math.floor(duration / 24)
h = duration % 24
duration = d * self.day_length + h
event_res = {}
event_res['start_date'] = st_date
event_res['duration'] = duration
event_res['text'] = evt.get(self.text)
event_res['id'] = evt['id']
event_res['parent'] = evt.get(self.parent)
result.append(event_res)
colors = choice_colors(len(self.colors))
for i, (key, value) in enumerate(self.colors.items()):
self.colors[key] = [value[0], value[1], colors[i]]
return {'result': result,'sidebar': self.colors}
def set_format(self, st_date):
if len(st_date) == 10 :
self.format = "%Y-%m-%d"
else :
self.format = "%Y-%m-%d %H:%M:%S"
return
def check_format(self, date):
if self.format == "%Y-%m-%d %H:%M:%S":
date = date + " 00:00:00"
return date
@openerpweb.jsonrequest
def on_event_resize(self, req, **kw):
if self.date_delay:
key = self.date_delay
value = kw['duration']
else:
key = self.date_stop
value = self.check_format(kw['end_date'])
try:
model = req.session.model(self.model)
res = model.write(kw['id'], {key : value})
except Exception, e:
print "eeeeeeeeeeeeeeeeeeeeeeeeeee",e
return True
@openerpweb.jsonrequest
def on_event_drag(self, req, **kw):
start_date = self.check_format(kw['start_date'])
if self.date_delay:
key = self.date_delay
value = kw['duration']
else:
key = self.date_stop
value = self.check_format(kw['end_date'])
try:
model = req.session.model(self.model)
res = model.write(kw['id'], {self.date_start : start_date, key : value})
except Exception, e:
print "eeeeeeeeeeeeeeeeeeeeeeeeeee",e
return True
@openerpweb.jsonrequest
def reload_gantt(self, req, **kw):
model = req.session.model(kw['model'])
if (kw['domain']):
domain = (kw['color_field'],'in', kw['domain'])
event_ids = model.search([domain])
else:
event_ids = model.search([])
return self.create_event(event_ids, model)

View File

@ -173,8 +173,9 @@ GanttProjectInfo.prototype.getTaskByIdInTree = function(parentTask, id)
* @type: public
* @topic: 0
*/
function GanttTaskInfo(id, name, est, duration, percentCompleted, predecessorTaskId)
function GanttTaskInfo(id, name, est, duration, percentCompleted, predecessorTaskId, color)
{
this.color = color || "white";
this.Id = id;
this.Name = name;
this.EST = est;
@ -348,7 +349,7 @@ GanttProject.prototype.create = function()
* @type: public
* @topic: 0
*/
function GanttChart()
function GanttChart(day_length)
{
this.Error = new GanttError();
this.dhtmlXMLSenderObject = new dhtmlXMLSenderObject(this);
@ -356,7 +357,7 @@ function GanttChart()
//settings
this.heightTaskItem = 12;
this.dayInPixels = 24;
this.hoursInDay = 8;
this.hoursInDay = day_length;
this._showTreePanel = true;
this._showTooltip = true;
this.isShowDescTask = false;
@ -4869,11 +4870,13 @@ GanttTask.prototype.createTaskItem = function()
cellTblTask.height = this.Chart.heightTaskItem + "px";
cellTblTask.width = this.TaskInfo.PercentCompleted + "%";
cellTblTask.style.lineHeight = "1px";
cellTblTask.style.backgroundColor = self.TaskInfo.color;
var imgPr = document.createElement("img");
imgPr.style.width = (this.TaskInfo.PercentCompleted * this.TaskInfo.Duration * this.Chart.hourInPixelsWork) / 100 + "px";
imgPr.style.height = this.Chart.heightTaskItem + "px";
cellTblTask.appendChild(imgPr);
imgPr.src = this.Chart.imgs + "progress_filled.png";
//imgPr.src = this.Chart.imgs + "progress_filled.png";
}
if (this.TaskInfo.PercentCompleted != 100)
@ -4943,9 +4946,9 @@ GanttTask.prototype.createTaskItem = function()
}
var taskClick = function() {
self.Chart.callEvent("onTaskClick", [self]);
self.Chart.callEvent("onTaskDblClick", [self]);
};
this.addEvent(divMove, 'click', taskClick, false);
this.addEvent(divMove, 'dblclick', taskClick, false);
if (this.Chart.isEditable)
{

View File

@ -7,7 +7,8 @@ QWeb.add_template('/base_gantt/static/src/xml/base_gantt.xml');
openerp.base.views.add('gantt', 'openerp.base_gantt.GanttView');
openerp.base_gantt.GanttView = openerp.base.Controller.extend({
init: function(view_manager, session, element_id, dataset, view_id) {
init: function(view_manager, session, element_id, dataset, view_id) {
this._super(session, element_id);
this.view_manager = view_manager;
this.dataset = dataset;
@ -16,207 +17,326 @@ openerp.base_gantt.GanttView = openerp.base.Controller.extend({
this.fields_views = {};
this.widgets = {};
this.widgets_counter = 0;
this.fields = {};
this.datarecord = {};
this.fields = this.dataset.fields ? this.dataset.fields: {};
this.ids = this.dataset.ids;
this.name = "";
this.date_start = "";
this.date_delay = "";
this.date_stop = "";
this.color_field = "";
this.day_lenth = 8;
this.colors = [];
this.color_values = [];
this.calendar_fields = {};
this.info_fields = [];
this.domain = this.dataset._domain ? this.dataset._domain: [];
this.context = {};
},
do_show: function () {
// TODO: re-trigger search
this.$element.show();
},
do_hide: function () {
this.$element.hide();
},
start: function() {
this.rpc("/base_gantt/ganttview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
this.rpc("/base_gantt/ganttview/load",
{"model": this.model, "view_id": this.view_id}, this.on_loaded);
},
on_loaded: function(data) {
this.fields_view = data.fields_view;
var self = this;
this.fields_view = data.fields_view;
this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
this.view_id = this.fields_view.view_id;
this.date_start = this.fields_view.arch.attrs.date_start;
this.date_delay = this.fields_view.arch.attrs.date_delay;
this.date_stop = this.fields_view.arch.attrs.date_stop;
this.color_field = this.fields_view.arch.attrs.color;
this.color_field = this.fields_view.arch.attrs.color;
this.day_length = this.fields_view.arch.attrs.day_length || 8;
this.colors = this.fields_view.arch.attrs.colors;
this.fields = this.fields_view.fields;
this.text = this.fields_view.arch.children[0].children[0].attrs.name;
this.parent = this.fields_view.arch.children[0].attrs.link;
this.calendar_fields['parent'] = {'name': this.parent};
this.calendar_fields['date_start'] = {'name': this.date_start};
this.calendar_fields['text'] = {'name': this.text};
if(this.date_delay)
this.calendar_fields['date_delay'] = {'name': this.date_delay};
if(this.date_stop)
this.calendar_fields['date_stop'] = {'name': this.date_stop};
this.format = "yyyy-MM-dd";
self.create_gantt();
self.get_events();
this.calendar_fields['day_length'] = this.day_length;
this.rpc('/base_gantt/ganttview/get_events',
{'model': this.model,
'fields': this.fields,
'color_field': this.color_field,
'day_length': this.day_length,
'calendar_fields': this.calendar_fields,
'colors': this.colors,
'info_fields': this.info_fields
},
function(res) {
self.create_gantt();
self.load_event(res);
})
this.$element.html(QWeb.render("GanttView", {"view": this, "fields_view": this.fields_view}));
},
convert_date_format: function(date) {
date=date+"";
if(typeof (date)!="string"||date.length===0){
return null;
}
var iso=date.split("-");
if(iso.length===0){
return null;
}
var day = iso[2];
var iso_hours = day.split(' ');
if (iso_hours.length > 1) {
day = iso_hours[0];
var iso_date_hours = iso_hours[1].split(':')
var new_date = new Date(iso[0], iso[1] - 1, day);
new_date.setHours(iso_date_hours[0]);
new_date.setMinutes(iso_date_hours[1]);
new_date.setSeconds(iso_date_hours[2]);
}
else {
var new_date = new Date(iso[0], iso[1] - 1, day);
}
new_date.setFullYear(iso[0]);
new_date.setMonth(iso[1]-1);
new_date.setDate(day);
return new_date;
},
create_gantt: function() {
ganttChartControl = new GanttChart();
ganttChartControl = new GanttChart(this.day_length);
ganttChartControl.setImagePath("/base_gantt/static/lib/dhtmlxGantt/codebase/imgs/");
ganttChartControl.setEditable(true);
ganttChartControl.showTreePanel(true);
ganttChartControl.showContextMenu(true);
ganttChartControl.showDescTask(true,'d,s-f');
ganttChartControl.showDescProject(true,'n,d');
},
load_event: function(res) {
var self = this
var result = res.result;
var sidebar = res.sidebar;
var project_id = new Array();
var project = new Array();
var j = -1;
var self = this;
for (i in result) {
var parent_id = result[i]['parent'][0];
var parent_name = result[i]['parent'][1];
if (jQuery.inArray(parent_id, project_id) == -1){
if (parent_id == undefined){
parent_name = "";
}
j = j + 1;
project[j] = new GanttProjectInfo(parent_id, parent_name, new Date(2011, 1, 1));
project_id[j] = parent_id;
}
var id = result[i]['id'];
var text = result[i]['text'];
var start_date = this.convert_date_format(result[i]['start_date']);
var duration = result[i]['duration'];
var task = new GanttTaskInfo(id, text, start_date, duration, 100, "");
k = project_id.indexOf(parent_id);
project[k].addTask(task);
}
for (i in project_id){
ganttChartControl.addProject(project[i]);
}
ganttChartControl.create("GanttDiv");
ganttChartControl.attachEvent("onTaskEndResize", function(task) {self.on_task_end_resize(task);})
ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_task_end_drag(task);})
//Create Sidebar
if (jQuery('#cal-sidebar-option').length == 0){
jQuery('#gantt-sidebar').append(
jQuery('<table>',{'width':'100%','cellspacing': 0, 'cellpadding': 0, 'id':'cal-sidebar-option'})
)
for(s in sidebar) {
jQuery('#cal-sidebar-option').append(
jQuery('<tr>').append(
jQuery('<td>').append(
jQuery('<div>')
.append(
jQuery('<input>',
{
'type': 'checkbox',
'id':sidebar[s][0],
'value':sidebar[s][0]
}).bind('click',function(){
self.reload_gantt(self.color_field,self.model)
}),
sidebar[s][1]
)
.css('background-color',sidebar[s][sidebar[s].length-1])
)
)
)
},
get_events: function() {
var self = this;
this.dataset.read_ids(this.dataset.ids, {}, function(result) {
self.load_event(result);
});
},
load_event: function(events) {
var self = this;
var result = events;
var project = {};
var project_smalldate = {};
var proj_id = [];
var proj_id_text = [];
COLOR_PALETTE = ['#ccccff', '#cc99ff', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
'#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
'#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
'#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900']
//Smallest date of child is parent start date
for (i in result){
var res = result[i];
if (res[this.date_start] != false){
var parent_id = res[this.parent][0] || res[this.parent];
var parent_name = res[this.parent][1];
var start_date = this.convert_str_date(res[this.date_start]);
if (project_smalldate[parent_id] == undefined){
project_smalldate[parent_id] = start_date;
proj_id.push(parent_id);
if (parent_name != undefined){
proj_id_text.push(res[this.parent]);
}
}
else{
if (start_date < project_smalldate[parent_id]){
project_smalldate[parent_id] = start_date;
}
}
}
}
//get parent text
if (parent_name == undefined){
var ajax = {
url: '/base/dataset/call',
async: false
};
this.rpc(ajax, {
model: this.dataset.model,
method: "name_get",
ids: proj_id,
args: ""
}, function(response) {
proj_id_text = response.result;
});
}
//create parents
for (i in proj_id){
var id = proj_id_text[i][0];
var text = proj_id_text[i][1];
project[id] = new GanttProjectInfo(id, text, project_smalldate[id]);
}
//create childs
var k = 0;
var color_box = {};
for (i in result) {
var res = result[i];
if (res[this.date_start] != false){
var parent_id = res[this.parent][0] || res[this.parent];
var id = res['id'];
var text = res[this.text];
var start_date = this.convert_str_date(res[this.date_start]);
var color = res[this.color_field][0] || res[this.color_field];
if (color_box[color] == undefined){
color_box[color] = COLOR_PALETTE[k];
k = k + 1;
}
if (this.date_stop != undefined){
if (res[this.date_stop] != false){
var stop_date = this.convert_str_date(res[this.date_stop]);
var duration= self.hours_between(start_date, stop_date);
}
else{
var duration = 0;
}
}
else{
var duration = res[this.date_delay];
}
if (duration == false)
duration = 0
task = new GanttTaskInfo(id, text, start_date, duration, 100, "", color_box[color]);
project[parent_id].addTask(task);
}
}
//Add parent
for (i in proj_id){
ganttChartControl.addProject(project[proj_id[i]]);
}
ganttChartControl.create("GanttDiv");
ganttChartControl.attachEvent("onTaskStartDrag", function(task) {self.on_drag_start(task);});
ganttChartControl.attachEvent("onTaskEndResize", function(task) {self.on_resize_drag_end(task, "resize");});
ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_resize_drag_end(task, "drag");});
ganttChartControl.attachEvent("onTaskDblClick", function(task) {self.open_popup(task);});
},
reload_gantt: function(color_field, model) {
var domain = [];
hours_between: function(date1, date2) {
var ONE_DAY = 1000 * 60 * 60 * 24;
var date1_ms = date1.getTime();
var date2_ms = date2.getTime();
var difference_ms = Math.abs(date1_ms - date2_ms);
d = Math.round(difference_ms / ONE_DAY);
h = Math.round((difference_ms % ONE_DAY)/(1000 * 60 * 60));
return (d * this.day_length) + h;
},
open_popup : function(task) {
var event_id = task.getId();
if (event_id) {
event_id = parseInt(event_id, 10);
var dataset_event_index = jQuery.inArray(event_id, this.ids);
} else {
var dataset_event_index = null;
}
this.dataset.index = dataset_event_index;
var element_id = _.uniqueId("act_window_dialog");
var dialog = jQuery('<div>',
{'id': element_id
}).dialog({
title: 'Gantt Chart',
modal: true,
minWidth: 800,
position: 'top'
});
var event_form = new openerp.base.FormView(this.view_manager, this.session, element_id, this.dataset, false);
event_form.start();
},
on_drag_start : function(task){
st_date = task.getEST();
if(st_date.getHours()){
self.hh = st_date.getHours();
self.mm = st_date.getMinutes();
}
},
on_resize_drag_end : function(task, evt){
var event_id = task.getId();
var data = {};
if (evt == "drag"){
full_date = task.getEST().set({hour: self.hh, minute : self.mm, second:0});
data[this.date_start] = this.convert_date_str(full_date);
}
if (this.date_stop != undefined){
tm = (task.getDuration() % this.day_length);
stp = task.getFinishDate().add(tm).hour();
data[this.date_stop] = this.convert_date_str(stp);
}else{
data[this.date_delay] = task.getDuration();
}
this.dataset.write(event_id, data, function(result) {});
},
do_show: function () {
this.$element.show();
},
do_hide: function () {
this.$element.hide();
},
convert_str_date: function (str){
if (str.length == 19){
this.format = "yyyy-MM-dd HH:mm:ss";
return openerp.base.parse_datetime(str);
} else if (str.length == 10){
this.format = "yyyy-MM-dd";
return openerp.base.parse_date(str);
} else if (str.length == 8){
this.format = "HH:mm:ss";
return openerp.base.parse_time(str);
}
throw "Unrecognized date/time format";
},
convert_date_str: function(full_date) {
if (this.format == "yyyy-MM-dd HH:mm:ss"){
return openerp.base.format_datetime(full_date);
} else if (this.format == "yyyy-MM-dd"){
return openerp.base.format_date(full_date);
} else if (this.format == "HH:mm:ss"){
return openerp.base.format_time(full_date);
}
throw "Unrecognized date/time format";
},
reload_gantt: function(domain) {
var self = this;
jQuery('input[type=checkbox]:checked','#cal-sidebar-option').each(function() {
domain.push(parseInt(jQuery(this).attr('id')))
var ajax = {
url: '/base/dataset/search_read',
async: false
};
this.rpc(ajax, {
model: this.dataset.model,
domain: self.dataset.domain,
context :self.dataset.context
}, function(response) {
ganttChartControl.clearAll();
jQuery("#GanttDiv").children().remove();
self.load_event(response);
});
},
do_search: function (domains, contexts, groupbys) {
var self = this;
return this.rpc('/base/session/eval_domain_and_context', {
domains: domains,
contexts: contexts,
group_by_seq: groupbys
}, function (results) {
self.dataset.context = results.context;
self.dataset.domain = results.domain;
return self.reload_gantt(self.dataset.domain);
});
this.rpc('/base_gantt/ganttview/reload_gantt',{
'domain':domain,
'color_field':color_field,
'model': model
},function(res) {
ganttChartControl.clearAll();
jQuery("#GanttDiv").children().remove();
self.load_event(res);
});
},
reverse_convert_date_format: function(date) {
return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate();
},
on_task_end_resize : function(task) {
this.rpc('/base_gantt/ganttview/on_event_resize',
{'id' : task.getId(),
'end_date' : this.reverse_convert_date_format(task.getFinishDate()),
'duration' : task.getDuration()
},
function(result) {
})
},
on_task_end_drag : function(task) {
this.rpc('/base_gantt/ganttview/on_event_drag',
{'id' : task.getId(),
'start_date' : this.reverse_convert_date_format(task.getEST()),
'end_date' : this.reverse_convert_date_format(task.getFinishDate()),
'duration' : task.getDuration()
},
function(result) {
})
}
});
@ -224,5 +344,4 @@ openerp.base_gantt.GanttView = openerp.base.Controller.extend({
// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them
};
// vim:et fdc=0 fdl=0:

View File

@ -3,12 +3,9 @@
<h3 class="title"><t t-esc="view.fields_view.arch.attrs.string"/></h3>
<table class="gantt-view" width="100%" height="100%" cellspacing="0" cellpadding="0">
<tr>
<td style="width:85%">
<td>
<div style="width:100%;height:300px;position:relative" id="GanttDiv"/>
</td>
<td valign = "top">
<div id="gantt-sidebar"/>
</td>
</tr>
</table>
</t>