[MERGE]0k/web_fullcalendar branch from github
bzr revid: jke@openerp.com-20131106164700-hiwisdcygno4sj17
This commit is contained in:
parent
08abc4345a
commit
e6b54fa60b
|
@ -38,17 +38,41 @@ months = {
|
||||||
10: "October", 11: "November", 12: "December"
|
10: "October", 11: "November", 12: "December"
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
|
|
||||||
"""
|
def get_recurrent_dates(rrulestring, startdate, exdate=None, tz=None, exrule=None, context=None):
|
||||||
Get recurrent dates based on Rule string considering exdate and start date.
|
"""Get recurrent dates based on Rule string considering exdate and start date.
|
||||||
@param rrulestring: rulestring
|
|
||||||
@param exdate: list of exception dates for rrule
|
All input dates and output dates are in UTC. Dates are infered
|
||||||
@param startdate: startdate for computing recurrent dates
|
thanks to rules in the ``tz`` timezone if given, else it'll be in
|
||||||
|
the current local timezone as specified in the context.
|
||||||
|
|
||||||
|
@param rrulestring: rulestring (ie: 'FREQ=DAILY;INTERVAL=1;COUNT=3')
|
||||||
|
@param exdate: string of dates separated by commas (ie: '20130506220000Z,20130507220000Z')
|
||||||
|
@param startdate: string start date for computing recurrent dates
|
||||||
|
@param tz: pytz timezone for computing recurrent dates
|
||||||
|
@param exrule: string exrule
|
||||||
|
@param context: current openerp context (for local timezone if ``tz`` is not provided)
|
||||||
@return: list of Recurrent dates
|
@return: list of Recurrent dates
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
exdate = exdate.split(',') if exdate else []
|
||||||
|
startdate = pytz.UTC.localize(
|
||||||
|
datetime.strptime(startdate, "%Y-%m-%d %H:%M:%S"))
|
||||||
|
|
||||||
def todate(date):
|
def todate(date):
|
||||||
val = parser.parse(''.join((re.compile('\d')).findall(date)))
|
val = parser.parse(''.join((re.compile('\d')).findall(date)))
|
||||||
return val
|
## Dates are localized to saved timezone if any, else defaulted to
|
||||||
|
## current timezone. WARNING: these last event dates are considered as
|
||||||
|
## "floating" dates.
|
||||||
|
if not val.tzinfo:
|
||||||
|
val = pytz.UTC.localize(val)
|
||||||
|
return val.astimezone(timezone)
|
||||||
|
|
||||||
|
## Note that we haven't any context tz info when called by the server, so
|
||||||
|
## we'll default to UTC which could induce one-day errors in date
|
||||||
|
## calculation.
|
||||||
|
timezone = pytz.timezone(tz or context.get('tz') or 'UTC')
|
||||||
|
|
||||||
if not startdate:
|
if not startdate:
|
||||||
startdate = datetime.now()
|
startdate = datetime.now()
|
||||||
|
@ -56,6 +80,9 @@ def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
|
||||||
if not exdate:
|
if not exdate:
|
||||||
exdate = []
|
exdate = []
|
||||||
|
|
||||||
|
## Convert the start date to saved timezone (or context tz) as it'll
|
||||||
|
## define the correct hour/day asked by the user to repeat for recurrence.
|
||||||
|
startdate = startdate.astimezone(timezone)
|
||||||
rset1 = rrule.rrulestr(str(rrulestring), dtstart=startdate, forceset=True)
|
rset1 = rrule.rrulestr(str(rrulestring), dtstart=startdate, forceset=True)
|
||||||
for date in exdate:
|
for date in exdate:
|
||||||
datetime_obj = todate(date)
|
datetime_obj = todate(date)
|
||||||
|
@ -64,7 +91,9 @@ def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
|
||||||
if exrule:
|
if exrule:
|
||||||
rset1.exrule(rrule.rrulestr(str(exrule), dtstart=startdate))
|
rset1.exrule(rrule.rrulestr(str(exrule), dtstart=startdate))
|
||||||
|
|
||||||
return list(rset1)
|
return [d.astimezone(pytz.UTC) for d in rset1]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def base_calendar_id2real_id(base_calendar_id=None, with_date=False):
|
def base_calendar_id2real_id(base_calendar_id=None, with_date=False):
|
||||||
"""
|
"""
|
||||||
|
@ -821,10 +850,7 @@ class calendar_alarm(osv.osv):
|
||||||
re_dates = []
|
re_dates = []
|
||||||
|
|
||||||
if hasattr(res_obj, 'rrule') and res_obj.rrule:
|
if hasattr(res_obj, 'rrule') and res_obj.rrule:
|
||||||
event_date = datetime.strptime(res_obj.date, '%Y-%m-%d %H:%M:%S')
|
recurrent_dates = get_recurrent_dates(res_obj.rrule, res_obj.date, res_obj.exdate, res_obj.vtimezone, res_obj.exrule, context=context)
|
||||||
#exdate is a string and we need a list
|
|
||||||
exdate = res_obj.exdate and res_obj.exdate.split(',') or []
|
|
||||||
recurrent_dates = get_recurrent_dates(res_obj.rrule, exdate, event_date, res_obj.exrule)
|
|
||||||
|
|
||||||
trigger_interval = alarm.trigger_interval
|
trigger_interval = alarm.trigger_interval
|
||||||
if trigger_interval == 'days':
|
if trigger_interval == 'days':
|
||||||
|
@ -986,6 +1012,47 @@ class calendar_event(osv.osv):
|
||||||
result[event] = ""
|
result[event] = ""
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _get_recurrence_end_date(self, cr, uid, ids, name, arg, context=None):
|
||||||
|
"""Get a good estimate of the end of the timespan concerned by an event.
|
||||||
|
|
||||||
|
This means we need to concider the last event of a recurrency, and that we
|
||||||
|
add its duration. For simple events (no rrule), the date_deadline is sufficient.
|
||||||
|
|
||||||
|
This value is stored in database and will help select events that should be
|
||||||
|
concidered candidate for display when filters are made upon dates (typically
|
||||||
|
the agenda filter will make one-month, one-week, one-day timespan searches).
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not context:
|
||||||
|
context = {}
|
||||||
|
events = super(calendar_event, self).read(
|
||||||
|
cr, uid, ids, ['rrule', 'exdate', 'exrule', 'duration', 'date_deadline', 'date', 'vtimezone'], context=context)
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for event in events:
|
||||||
|
|
||||||
|
duration = timedelta(hours=event['duration'])
|
||||||
|
|
||||||
|
if event['rrule']:
|
||||||
|
all_dates = get_recurrent_dates(
|
||||||
|
event['rrule'], event['date'], event['exdate'], event['vtimezone'],
|
||||||
|
event['exrule'], context=context)
|
||||||
|
if not event['vtimezone'] and not context.get('tz'):
|
||||||
|
## We are called by the server probably at update time (no
|
||||||
|
## context), and no vtimezone was recorded, so we have no
|
||||||
|
## idea of possible client timezone so we have a possible
|
||||||
|
## one-day-of error when applying RRULEs on floating dates.
|
||||||
|
## Let's add a day.
|
||||||
|
duration += timedelta(days=1)
|
||||||
|
result[event['id']] = (all_dates[-1] + duration).astimezone(pytz.UTC).strftime("%Y-%m-%d %H:%M:%S") \
|
||||||
|
if all_dates else None
|
||||||
|
else:
|
||||||
|
result[event['id']] = event['date_deadline']
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def _rrule_write(self, obj, cr, uid, ids, field_name, field_value, args, context=None):
|
def _rrule_write(self, obj, cr, uid, ids, field_name, field_value, args, context=None):
|
||||||
data = self._get_empty_rrule_data()
|
data = self._get_empty_rrule_data()
|
||||||
if field_value:
|
if field_value:
|
||||||
|
@ -1035,6 +1102,10 @@ rule or repeating pattern of time to exclude from the recurring rule."),
|
||||||
'base_calendar_alarm_id': fields.many2one('calendar.alarm', 'Alarm'),
|
'base_calendar_alarm_id': fields.many2one('calendar.alarm', 'Alarm'),
|
||||||
'recurrent_id': fields.integer('Recurrent ID'),
|
'recurrent_id': fields.integer('Recurrent ID'),
|
||||||
'recurrent_id_date': fields.datetime('Recurrent ID date'),
|
'recurrent_id_date': fields.datetime('Recurrent ID date'),
|
||||||
|
'recurrence_end_date': fields.function(_get_recurrence_end_date,
|
||||||
|
type='datetime',
|
||||||
|
store=True, string='Recurrence end date',
|
||||||
|
priority=30),
|
||||||
'vtimezone': fields.selection(_tz_get, size=64, string='Timezone'),
|
'vtimezone': fields.selection(_tz_get, size=64, string='Timezone'),
|
||||||
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
|
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
|
||||||
'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with organizer attribute of VEvent.
|
'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with organizer attribute of VEvent.
|
||||||
|
@ -1154,39 +1225,43 @@ rule or repeating pattern of time to exclude from the recurring rule."),
|
||||||
context = {}
|
context = {}
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for data in super(calendar_event, self).read(cr, uid, select, ['rrule', 'recurrency', 'exdate', 'exrule', 'date'], context=context):
|
# for data in super(calendar_event, self).read(cr, uid, select, ['rrule', 'recurrency', 'exdate', 'exrule', 'date'], context=context):
|
||||||
|
for data in super(calendar_event, self).read(cr, uid, select, ['rrule', 'recurrency', 'exdate', 'exrule', 'date', 'vtimezone'], context=context):
|
||||||
if not data['recurrency'] or not data['rrule']:
|
if not data['recurrency'] or not data['rrule']:
|
||||||
result.append(data['id'])
|
result.append(data['id'])
|
||||||
continue
|
continue
|
||||||
event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
|
# event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
|
||||||
|
# event_date = pytz.UTC.localize(event_date)
|
||||||
# TOCHECK: the start date should be replaced by event date; the event date will be changed by that of calendar code
|
# TOCHECK: the start date should be replaced by event date; the event date will be changed by that of calendar code
|
||||||
|
|
||||||
if not data['rrule']:
|
# if not data['rrule']:
|
||||||
continue
|
# continue
|
||||||
|
#
|
||||||
exdate = data['exdate'] and data['exdate'].split(',') or []
|
# exdate = data['exdate'] and data['exdate'].split(',') or []
|
||||||
rrule_str = data['rrule']
|
# rrule_str = data['rrule']
|
||||||
new_rrule_str = []
|
# new_rrule_str = []
|
||||||
rrule_until_date = False
|
# rrule_until_date = False
|
||||||
is_until = False
|
# is_until = False
|
||||||
for rule in rrule_str.split(';'):
|
# for rule in rrule_str.split(';'):
|
||||||
name, value = rule.split('=')
|
# name, value = rule.split('=')
|
||||||
if name == "UNTIL":
|
# if name == "UNTIL":
|
||||||
is_until = True
|
# is_until = True
|
||||||
value = parser.parse(value)
|
# value = parser.parse(value)
|
||||||
rrule_until_date = parser.parse(value.strftime("%Y-%m-%d %H:%M:%S"))
|
# rrule_until_date = parser.parse(value.strftime("%Y-%m-%d %H:%M:%S"))
|
||||||
value = value.strftime("%Y%m%d%H%M%S")
|
# value = value.strftime("%Y%m%d%H%M%S")
|
||||||
new_rule = '%s=%s' % (name, value)
|
# new_rule = '%s=%s' % (name, value)
|
||||||
new_rrule_str.append(new_rule)
|
# new_rrule_str.append(new_rule)
|
||||||
new_rrule_str = ';'.join(new_rrule_str)
|
# new_rrule_str = ';'.join(new_rrule_str)
|
||||||
rdates = get_recurrent_dates(str(new_rrule_str), exdate, event_date, data['exrule'])
|
# rdates = get_recurrent_dates(str(new_rrule_str), exdate, event_date, data['exrule'])
|
||||||
|
# rdates = get_recurrent_dates(data['rrule'], exdate, event_date, data['exrule'])
|
||||||
|
rdates = get_recurrent_dates(data['rrule'], data['date'], data['exdate'], data['vtimezone'], data['exrule'], context=context)
|
||||||
for r_date in rdates:
|
for r_date in rdates:
|
||||||
# fix domain evaluation
|
# fix domain evaluation
|
||||||
# step 1: check date and replace expression by True or False, replace other expressions by True
|
# step 1: check date and replace expression by True or False, replace other expressions by True
|
||||||
# step 2: evaluation of & and |
|
# step 2: evaluation of & and |
|
||||||
# check if there are one False
|
# check if there are one False
|
||||||
pile = []
|
pile = []
|
||||||
|
ok = True
|
||||||
for arg in domain:
|
for arg in domain:
|
||||||
if str(arg[0]) in (str('date'), str('date_deadline')):
|
if str(arg[0]) in (str('date'), str('date_deadline')):
|
||||||
if (arg[1] == '='):
|
if (arg[1] == '='):
|
||||||
|
@ -1346,7 +1421,8 @@ rule or repeating pattern of time to exclude from the recurring rule."),
|
||||||
new_arg = arg
|
new_arg = arg
|
||||||
if arg[0] in ('date_deadline', unicode('date_deadline')):
|
if arg[0] in ('date_deadline', unicode('date_deadline')):
|
||||||
if context.get('virtual_id', True):
|
if context.get('virtual_id', True):
|
||||||
new_args += ['|','&',('recurrency','=',1),('end_date', arg[1], arg[2])]
|
# new_args += ['|','&',('recurrency','=',1),('end_date', arg[1], arg[2])]
|
||||||
|
new_args += ['|','&',('recurrency','=',1),('recurrence_end_date', arg[1], arg[2])]
|
||||||
elif arg[0] == "id":
|
elif arg[0] == "id":
|
||||||
new_id = get_real_ids(arg[2])
|
new_id = get_real_ids(arg[2])
|
||||||
new_arg = (arg[0], arg[1], new_id)
|
new_arg = (arg[0], arg[1], new_id)
|
||||||
|
@ -1421,7 +1497,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
|
||||||
new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
|
new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
|
||||||
|
|
||||||
date_new = event_id.split('-')[1]
|
date_new = event_id.split('-')[1]
|
||||||
date_new = time.strftime("%Y%m%dT%H%M%S", \
|
date_new = time.strftime("%Y%m%dT%H%M%SZ", \
|
||||||
time.strptime(date_new, "%Y%m%d%H%M%S"))
|
time.strptime(date_new, "%Y%m%d%H%M%S"))
|
||||||
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
|
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
|
||||||
res = self.write(cr, uid, [real_event_id], {'exdate': exdate})
|
res = self.write(cr, uid, [real_event_id], {'exdate': exdate})
|
||||||
|
@ -1472,7 +1548,8 @@ rule or repeating pattern of time to exclude from the recurring rule."),
|
||||||
context = {}
|
context = {}
|
||||||
fields2 = fields and fields[:] or None
|
fields2 = fields and fields[:] or None
|
||||||
|
|
||||||
EXTRAFIELDS = ('class','user_id','duration')
|
EXTRAFIELDS = ('class','user_id','duration', 'date',
|
||||||
|
'rrule', 'vtimezone', 'exrule', 'exdate')
|
||||||
for f in EXTRAFIELDS:
|
for f in EXTRAFIELDS:
|
||||||
if fields and (f not in fields):
|
if fields and (f not in fields):
|
||||||
fields2.append(f)
|
fields2.append(f)
|
||||||
|
@ -1495,6 +1572,15 @@ rule or repeating pattern of time to exclude from the recurring rule."),
|
||||||
res = real_data[real_id].copy()
|
res = real_data[real_id].copy()
|
||||||
ls = base_calendar_id2real_id(base_calendar_id, with_date=res and res.get('duration', 0) or 0)
|
ls = base_calendar_id2real_id(base_calendar_id, with_date=res and res.get('duration', 0) or 0)
|
||||||
if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
|
if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
|
||||||
|
recurrent_dates = [
|
||||||
|
d.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
for d in get_recurrent_dates(
|
||||||
|
res['rrule'], res['date'], res['exdate'],
|
||||||
|
res['vtimezone'], res['exrule'], context=context)]
|
||||||
|
if ls[1] not in recurrent_dates:
|
||||||
|
raise KeyError(
|
||||||
|
'Virtual id %r is not valid, event %r can '
|
||||||
|
'not produce it.' % (base_calendar_id, real_id))
|
||||||
res['date'] = ls[1]
|
res['date'] = ls[1]
|
||||||
res['date_deadline'] = ls[2]
|
res['date_deadline'] = ls[2]
|
||||||
res['id'] = base_calendar_id
|
res['id'] = base_calendar_id
|
||||||
|
@ -1548,7 +1634,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
|
||||||
date_new = time.strftime("%Y%m%dT%H%M%S", \
|
date_new = time.strftime("%Y%m%dT%H%M%S", \
|
||||||
time.strptime(date_new, "%Y%m%d%H%M%S"))
|
time.strptime(date_new, "%Y%m%d%H%M%S"))
|
||||||
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
|
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
|
||||||
self.write(cr, uid, [real_event_id], {'exdate': exdate})
|
self.write(cr, uid, [real_event_id], {'exdate': exdate}, context=context)
|
||||||
ids.remove(event_id)
|
ids.remove(event_id)
|
||||||
for event in self.browse(cr, uid, ids, context=context):
|
for event in self.browse(cr, uid, ids, context=context):
|
||||||
if event.attendee_ids:
|
if event.attendee_ids:
|
||||||
|
|
|
@ -239,7 +239,7 @@
|
||||||
<field name="model">calendar.event</field>
|
<field name="model">calendar.event</field>
|
||||||
<field name="priority" eval="2"/>
|
<field name="priority" eval="2"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<calendar string="Events" date_start="date" color="show_as" date_delay="duration">
|
<calendar string="Events" date_start="date" color="show_as" date_delay="duration" all_day="allday">
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="class"/>
|
<field name="class"/>
|
||||||
<field name="show_as"/>
|
<field name="show_as"/>
|
||||||
|
|
Loading…
Reference in New Issue