[FIX] Calendar.

bzr revid: vda@tinyerp.com-20110531112545-laa8rp2xxx9ympgk
This commit is contained in:
vda (OpenERP) 2011-05-31 16:55:45 +05:30
parent 9a5058a6b4
commit 8cca171661
3 changed files with 233 additions and 497 deletions

View File

@ -1,356 +1,10 @@
from base.controllers.main import View
import openerpweb, time, math, re, datetime as DT, pytz
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) ]
DT_SERVER_FORMATS = {
'datetime' : '%Y-%m-%d %H:%M:%S',
'date' : '%Y-%m-%d',
'time' : '%H:%M:%S'
}
DT_FORMAT_INFO = {'datetime' : ('%Y-%m-%d %H:%M:%S', DT.datetime, 0, 6),
'date': ('%Y-%m-%d', DT.date, 0, 3),
'time': ('%H:%M:%S', DT.time, 3, 6)}
def choice_colors(n):
if n > len(COLOR_PALETTE):
return _colorline[0:-1:len(_colorline) / (n + 1)]
elif n:
return COLOR_PALETTE[:n]
return []
import openerpweb
class CalendarView(View):
_cp_path = "/base_calendar/calendarview"
mode = 'month'
date_start = None
date_delay = None
date_stop = None
color_field = None
day_length = 8
use_search = False
selected_day = None
date_format = '%Y-%m-%d'
info_fields = []
fields = {}
events = []
colors = {}
color_values = []
remote_timezone = 'utc'
client_timezone = False
calendar_fields = {}
concurrency_info = None
ids = []
model = ''
domain = []
context = {}
@openerpweb.jsonrequest
def load(self, req, model, view_id):
fields_view = self.fields_view_get(req, model, view_id, 'calendar')
return {'fields_view':fields_view}
def convert(self, event):
fields = [self.date_start]
if self.date_stop:
fields.append(self.date_stop)
for fld in fields:
fld_type = self.fields[fld]['type']
fmt = DT_SERVER_FORMATS[fld_type]
if event[fld] and fmt:
event[fld] = time.strptime(event[fld], fmt)
# default start/stop time is 9:00 AM / 5:00 PM
if fld_type == 'date' and event[fld]:
ds = list(event[fld])
if fld == self.date_start:
ds[3] = 9
elif fld == self.date_stop:
ds[3] = 17
event[fld] = tuple(ds)
@openerpweb.jsonrequest
def schedule_events(self, req, **kw):
self.model = kw['model']
self.mode = kw.get('mode') or self.mode or 'month'
self.fields = kw['fields']
self.color_field = kw.get('color_field') or self.color_field or None
self.colors = kw.get('colors') or {}
self.calendar_fields = kw['calendar_fields']
self.info_fields = kw['info_fields']
self.date_start = self.calendar_fields['date_start']['name']
self.domain = kw.get('domain') or []
self.remote_timezone = req.session.remote_timezone
self.client_timezone = req.session.client_timezone
if self.calendar_fields.get('date_stop'):
self.date_stop = self.calendar_fields['date_stop']['name']
if self.calendar_fields.get('date_delay'):
self.date_delay = self.calendar_fields['date_delay']['name']
model = req.session.model(self.model)
event_ids = model.search(self.domain)
self.events = model.read(event_ids, self.fields.keys())
result = []
self.date_format = req.session._lang and req.session._lang['date_format']
if self.color_field:
for evt in self.events:
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)
colors = choice_colors(len(self.colors))
for i, (key, value) in enumerate(self.colors.items()):
self.colors[key] = [value[0], value[1], colors[i]]
for evt in self.events:
self.convert(evt)
a = self.get_event_widget(evt)
result.append(a)
return {'result':result,'sidebar':self.colors}
def parsedatetime(self, string):
kind = 'datetime'
if '-' in string and ':' in string:
kind = 'datetime'
elif '-' in string:
kind = 'date'
elif ':' in string:
kind = 'time'
fmt, obj, i, j = DT_FORMAT_INFO[kind]
return obj(*time.strptime(string, fmt)[i:j])
def parse_datetime(self, value, kind="datetime", as_timetuple=False):
server_format = DT_SERVER_FORMATS[kind]
local_format = self.date_format
if not value:
return False
if isinstance(value, (time.struct_time, tuple)):
value = time.strftime(local_format, value)
try:
value = time.strptime(value, local_format)
except ValueError:
try:
# might be in server format already (e.g. filter domain)
value = time.strptime(value, server_format)
except ValueError:
try:
dt = list(time.localtime())
dt[2] = int(value)
value = tuple(dt)
except:
return False
if kind == "datetime":
try:
value = self.tz_convert(value, 'parse')
except Exception,e:
print "*******************Error in timezone parsing *********",e
if as_timetuple:
return value
return time.strftime(server_format, value)
@openerpweb.jsonrequest
def edit_events(self, req,**kw):
data = {}
ds = self.parsedatetime(kw['start_date'])
de = self.parsedatetime(kw['end_date'])
data[kw['calendar_fields']['date_start']['name']] = self.parse_datetime(ds.timetuple())
if 'date_stop' in kw['calendar_fields']:
data[kw['calendar_fields']['date_stop']['name']] = self.parse_datetime(de.timetuple())
elif 'date_delay' in kw['calendar_fields']:
day_length = kw['calendar_fields']['day_length']
tds = time.mktime(ds.timetuple())
tde = time.mktime(de.timetuple())
n = (tde - tds) / (60 * 60)
if n > day_length:
d = math.floor(n / 24)
h = n % 24
n = d * day_length + h
data[kw['calendar_fields']['date_delay']['name']] = n
error = None
try:
req.session.model(kw['model']).write([int(kw['id'])], data)
except Exception, e:
error = e
return error
def tz_convert(self, struct_time, action):
# if no client timezone is configured, consider the client is in the same
# timezone as the server
lzone = pytz.timezone(self.client_timezone or self.remote_timezone)
szone = pytz.timezone(self.remote_timezone)
dt = DT.datetime.fromtimestamp(time.mktime(struct_time))
if action == 'parse':
fromzone = lzone
tozone = szone
elif action == 'format':
fromzone = szone
tozone = lzone
else:
raise Exception("_tz_convert action should be 'parse' or 'format'. Not '%s'" % (action, ))
localized_original_datetime = fromzone.localize(dt, is_dst=True)
destination_datetime = localized_original_datetime.astimezone(tozone)
return destination_datetime.timetuple()
def format_datetime(self, value, kind="datetime", as_timetuple=False):
"""Convert date value to the local datetime considering timezone info.
@param value: the date value
@param kind: type of the date value (date, time or datetime)
@param as_timetuple: return timetuple
@type value: basestring or time.time_tuple)
@return: string or timetuple
"""
server_format = DT_SERVER_FORMATS[kind]
local_format = self.date_format
if not value:
return ''
if isinstance(value, (time.struct_time, tuple)):
value = time.strftime(server_format, value)
if isinstance(value, DT.datetime):
value = value
try:
value = DT.datetime.strptime(value[:10], server_format)
return value.strftime(local_format)
except:
return ''
value = value.strip()
# remove trailing miliseconds
value = re.sub("(.*?)(\s+\d{2}:\d{2}:\d{2})(\.\d+)?$", "\g<1>\g<2>", value)
# add time part in value if missing
if kind == 'datetime' and not re.search('\s+\d{2}:\d{2}:\d{2}?$', value):
value += ' 00:00:00'
# remove time part from value
elif kind == 'date':
value = re.sub('\s+\d{2}:\d{2}:\d{2}(\.\d+)?$', '', value)
value = time.strptime(value, server_format)
if kind == "datetime":
try:
value = self.tz_convert(value, 'format')
except Exception, e:
print "\n\n\n************ Error in timezone formatting", e
if as_timetuple:
return value
return time.strftime(local_format, value)
def get_event_widget(self, event):
title = '' # the title
description = [] # the description
if self.info_fields:
f = self.info_fields[0]
s = event[f]
if isinstance(s, (tuple, list)): s = s[-1]
title = s
for f in self.info_fields[1:]:
s = event[f]
if isinstance(s, (tuple, list)):
s = s[-1]
if s:
description.append(str(s))
starts = event.get(self.date_start)
ends = event.get(self.date_delay) or 1.0
span = 0
if starts and ends:
n = 0
h = ends
if ends == self.day_length:
span = 1
elif ends > self.day_length:
n = ends / self.day_length
h = ends % self.day_length
n = int(math.floor(n))
if h > 0:
span = n + 1
else:
span = n
ends = time.localtime(time.mktime(starts) + (h * 60 * 60) + (n * 24 * 60 * 60))
if starts and self.date_stop:
ends = event.get(self.date_stop)
if not ends:
ends = time.localtime(time.mktime(starts) + 60 * 60)
tds = time.mktime(starts)
tde = time.mktime(ends)
if tds >= tde:
tde = tds + 60 * 60
ends = time.localtime(tde)
n = (tde - tds) / (60 * 60)
if n >= self.day_length:
span = math.ceil(n / 24)
starts = self.format_datetime(starts, "datetime", True)
ends = self.format_datetime(ends, "datetime", True)
title = title.strip()
description = ', '.join(description).strip()
return {'id': event['id'], 'start_date': str(DT.datetime(*starts[:6])), 'end_date': str(DT.datetime(*ends[:6])), 'text': title, 'title': description, 'color': self.colors[event[self.color_field]][-1]}
return {'fields_view': fields_view}

View File

@ -11,14 +11,13 @@ openerp.base_calendar.CalendarView = openerp.base.Controller.extend({
this._super(session, element_id);
this.view_manager = view_manager;
this.dataset = dataset;
this.dataset_index = 0;
this.model = dataset.model;
this.view_id = view_id;
this.fields_view = {};
this.widgets = {};
this.widgets_counter = 0;
this.fields = this.dataset.fields ? this.dataset.fields: {};
this.datarecord = {};
this.ids = this.dataset.ids;
this.name = "";
this.date_start = "";
this.date_delay = "";
@ -35,23 +34,22 @@ openerp.base_calendar.CalendarView = openerp.base.Controller.extend({
start: function() {
this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
},
on_loaded: function(result) {
var self = this;
var params = {};
this.fields_view = result.fields_view;
this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
on_loaded: function(data) {
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_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.colors = this.fields_view.arch.attrs.colors;
this.colors = this.fields_view.arch.attrs.colors;
this.day_length = this.fields_view.arch.attrs.day_length || 8;
this.color_field = this.fields_view.arch.attrs.color;
this.fields = this.fields_view.fields;
//* Calendar Fields *
//* Calendar Fields *
this.calendar_fields['date_start'] = {'name': this.date_start, 'kind': this.fields[this.date_start]['type']};
if(this.date_delay)
@ -59,121 +57,181 @@ openerp.base_calendar.CalendarView = openerp.base.Controller.extend({
if(this.date_stop)
this.calendar_fields['date_stop'] = {'name': this.date_stop, 'kind': this.fields[this.date_stop]['type']};
this.calendar_fields['day_length'] = this.day_length;
//* ------- *
for(var fld=0;fld<this.fields_view.arch.children.length;fld++) {
//* ------- *
for(var fld=0;fld<this.fields_view.arch.children.length;fld++) {
this.info_fields.push(this.fields_view.arch.children[fld].attrs.name);
}
this.load_scheduler();
},
this.load_scheduler();
},
load_scheduler:function() {
var self = this;
var params = {};
params['model'] = this.model;
params['calendar_fields'] = this.calendar_fields;
params['info_fields'] = this.info_fields;
params['fields'] = this.fields;
params['color_field'] = this.color_field;
params['domain'] = this.domain;
params['colors'] = this.colors;
/*
* Start dhtmlx Schedular
load_scheduler: function() {
var self = this;
this.dataset.read_ids(
this.ids,
this.fields,
function(events){
if (self.session.locale_code) {
$LAB.setOptions({AlwaysPreserveOrder: true})
.script([
'/base_calendar/static/lib/dhtmlxScheduler/sources/locale_'+self.session.locale_code+'.js',
'/base_calendar/static/lib/dhtmlxScheduler/sources/locale_recurring_'+self.session.locale_code+'.js'
])
.wait(function() {
self.schedule_events(events);
});
} else {
self.schedule_events(events);
}
});
},
schedule_events: function(events) {
this.$element.html(QWeb.render("CalendarView", {"fields_view": this.fields_view}));
/*
* Initialize dhtmlx Schedular
*/
scheduler.clearAll();
scheduler.config.xml_date="%Y-%m-%d %H:%i";
scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
this.rpc(
'/base_calendar/calendarview/schedule_events',
params,
function(result) {
self.schedule_events(result);
}
)
},
schedule_events: function(result) {
var self = this;
var res = result.result;
var sidebar = result.sidebar;
this.$element.html(QWeb.render("CalendarView", {"view": this, "fields_view": this.fields_view, "sidebar":sidebar, "calendar":this}));
// Initialize Sceduler
scheduler.clearAll();
scheduler.config.api_date="%Y-%m-%d %H:%M:%S";
if(this.fields[this.date_start]['type'] == 'time') {
scheduler.config.xml_date="%H:%M:%S";
} else {
scheduler.config.xml_date="%Y-%m-%d %H:%M:%S";
}
scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
// Initialize Sceduler
scheduler.init('openerp_scheduler',null,"month");
scheduler.parse(res,"json");
jQuery('#dhx_minical_icon').bind('click', this.mini_calendar);
// To Change Event
scheduler.attachEvent(
'onEventChanged'
,function(event_id, event_object) {
self.edit_event(event_id, event_object)
});
/*
* Create Sidebar
*/
jQuery('#calendar-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_scheduler()
}),
sidebar[s][1]
)
.css('background-color',sidebar[s][sidebar[s].length-1])
)
)
)
}
},
convert_date_format: function(start_date, end_date) {
var params = {};
params['start_date'] = start_date.getFullYear() +'-' + start_date.getMonth()+'-' + start_date.getDate()+' '+start_date.getHours()+':'+start_date.getMinutes()+':'+start_date.getSeconds();
if(end_date) {
params['end_date'] = end_date.getFullYear() +'-' + end_date.getMonth()+'-' + end_date.getDate()+' '+end_date.getHours()+':'+end_date.getMinutes()+':'+end_date.getSeconds();
}
return params;
},
edit_event: function(evt_id, evt_object) {
var dates = this.convert_date_format(evt_object.start_date, evt_object.end_date);
this.rpc(
'/base_calendar/calendarview/edit_events',
{
'start_date': dates.start_date,
'end_date': dates.end_date,
'id': evt_id,
'model': this.model,
'info_fields': this.info_fields,
'fields': this.fields,
'calendar_fields': this.calendar_fields
}
);
},
mini_calendar: function() {
//To parse Events we have to convert date Format
var res_events = [];
for(var e=0;e<events.length;e++) {
var evt = events[e];
if(!evt[this.date_start]) {
this.notification.warn("Start date is not defined for event :", evt['id']);
break;
}
if (this.fields[this.date_start]['type'] == 'date') {
evt[this.date_start] = openerp.base.parse_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss')
}
if(this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') {
evt[this.date_stop] = openerp.base.parse_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss')
}
res_events.push(this.convert_event(evt))
}
scheduler.parse(res_events,"json");
jQuery('#dhx_minical_icon').bind('click', this.mini_calendar);
// Event Options Click,edit
var self = this;
scheduler.attachEvent(
"onDblClick",
function(event_id, event_object) {
self.popup_event(event_id);
}
);
scheduler.attachEvent(
"onEventCreated",
function(event_id, event_object) {
//Replace default Lightbox with Popup Form of new Event
scheduler.showLightbox = function(){
//Delete Newly created Event,Later we reload Scheduler
scheduler.deleteEvent(event_id)
self.popup_event();
}
}
);
},
convert_event: function(event) {
var res_text = '';
var res_description = [];
var start = event[this.date_start];
var end = event[this.date_delay] || 1;
var span = 0;
if(this.info_fields) {
var fld = event[this.info_fields[0]];
if(typeof fld == 'object') {
res_text = fld[fld.length -1];
} else {
res_text = fld
}
var sliced_info_fields = this.info_fields.slice(1);
for(sl_fld in sliced_info_fields) {
var slc_fld = event[sliced_info_fields[sl_fld]];
if(typeof slc_fld == 'object') {
res_description.push(slc_fld[slc_fld.length - 1])
} else {
if(slc_fld) {
res_description.push(slc_fld);
}
}
}
}
if(start && end){
var n = 0;
var h = end;
if (end == this.day_length) {
span = 1
} else if(end > this.day_length) {
n = end / this.day_length;
h = end % this.day_length;
n = parseInt(Math.floor(n));
if(h > 0)
span = n + 1
else
span = n
}
var end_date = openerp.base.parse_datetime(start);
end = end_date.add({hours: h, minutes: n})
}
if(start && this.date_stop) {
var tds = start = openerp.base.parse_datetime(start);
var tde = ends = openerp.base.parse_datetime(event[this.date_stop]);
if(event[this.date_stop] == undefined) {
if(tds) {
end = (tds.getOrdinalNumber() + 60 * 60)
}
}
if(tds && tde) {
//time.mktime equivalent
tds = (tds.getOrdinalNumber() / 1e3 >> 0) - (tds.getOrdinalNumber() < 0);
tde = (tde.getOrdinalNumber() / 1e3 >> 0) - (tde.getOrdinalNumber() < 0);
}
if(tds >= tde) {
tde = tds + 60 * 60;
}
n = (tde - tds) / (60 * 60);
if (n >= this.day_length) {
span = Math.ceil(n / 24);
}
}
return {
'start_date': start.toString('yyyy-MM-dd HH:mm:ss'),
'end_date': end.toString('yyyy-MM-dd HH:mm:ss'),
'text': res_text,
'id': event['id'],
'title': res_description.join()
}
},
mini_calendar: function() {
if(scheduler.isCalendarVisible()) {
scheduler.destroyCalendar();
@ -189,11 +247,22 @@ openerp.base_calendar.CalendarView = openerp.base.Controller.extend({
});
}
},
reload_scheduler: function() {
// self.color_field
console.log('Reload Scheduler>>>')
},
do_search: function(domains, contexts, groupbys) {
var self = this;
this.rpc('/base/session/eval_domain_and_context', {
domains: domains,
contexts: contexts,
group_by_seq: groupbys
}, function (results) {
// TODO: handle non-empty results.group_by with read_group
self.dataset.context = self.context = results.context;
self.dataset.domain = self.domain = results.domain;
self.dataset.read_slice(self.fields, 0, self.limit,function(events){
self.schedule_events(events)
});
});
},
do_show: function () {
this.$element.show();
@ -201,23 +270,42 @@ openerp.base_calendar.CalendarView = openerp.base.Controller.extend({
do_hide: function () {
this.$element.hide();
},
popup_event: function(event_id) {
var action_manager = this.view_manager.session.action_manager;
var action = {
"res_model": this.dataset.model,
"res_id": event_id,
"views":[[false,"form"]],
"type":"ir.actions.act_window",
"view_type":"form",
"view_mode":"form"
}
action.flags = {
search_view: false,
sidebar : false,
views_switcher : false,
action_buttons : false,
pager: false
}
var element_id = _.uniqueId("act_window_dialog");
var dialog = jQuery('<div>', {
'id': element_id
}).dialog({
modal: true,
width: 'auto',
height: 'auto'
});
var action_manager = new openerp.base.ActionManager(this.session, element_id);
action_manager.start();
action_manager.do_action(action);
}
});
//openerp.base.Action = openerp.base.Action.extend({
// do_action_window: function(action) {
// this._super.apply(this,arguments);
// for(var i = 0; i < action.views.length; i++) {
// if(action.views[i][1] == "calendar") {
// this.calendar_id = action.views[i][0];
// break;
// }
// }
// // IF there is a view calender
// // if(this.calendar_id
// },
//});
};
// DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner'))

View File

@ -1,15 +1,9 @@
<template>
<t t-name="CalendarView">
<h3 class="title"><t t-esc="view.fields_view.arch.attrs.string"/></h3>
<h3 class="title"><t t-esc="fields_view.arch.attrs.string"/></h3>
<table class="calendar-view" width="100%" height="100%" cellspacing="0" cellpadding="0">
<tr>
<td>
<div style="height: 1000px;width: 100%;">
<div id="calendar-sidebar">
</div>
</div>
</td>
<td style="width:85%;" align="left">
<td style="width:100%;" align="left">
<div id="openerp_scheduler" class="dhx_cal_container" style="height: 1000px;width: 100%;">
<div class="dhx_cal_navline">
<div class="dhx_cal_prev_button"/>