357 lines
12 KiB
Python
357 lines
12 KiB
Python
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 []
|
|
|
|
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.session, 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]}
|
|
|