1.1 --- a/imip_agent.py Thu Oct 09 22:50:12 2014 +0200
1.2 +++ b/imip_agent.py Thu Oct 09 22:50:41 2014 +0200
1.3 @@ -1,13 +1,14 @@
1.4 #!/usr/bin/env python
1.5
1.6 from bisect import bisect_left, insort_left
1.7 -from datetime import date, datetime
1.8 +from datetime import date, datetime, timedelta
1.9 from email import message_from_file
1.10 from email.mime.multipart import MIMEMultipart
1.11 from email.mime.text import MIMEText
1.12 from pytz import timezone, UnknownTimeZoneError
1.13 from smtplib import SMTP
1.14 from vCalendar import parse, ParseError, to_dict, to_node
1.15 +from vRecurrence import get_parameters, get_rule, to_tuple
1.16 import imip_store
1.17 import re
1.18 import sys
1.19 @@ -86,10 +87,21 @@
1.20 def get_utc_datetime(d, name):
1.21 value, attr = get_item(d, name)
1.22 dt = get_datetime(value, attr)
1.23 + return to_utc_datetime(dt)
1.24 +
1.25 +def to_utc_datetime(dt):
1.26 if not dt:
1.27 return None
1.28 elif isinstance(dt, datetime):
1.29 - return dt.astimezone(timezone("UTC")).strftime("%Y%m%dT%H%M%SZ")
1.30 + return dt.astimezone(timezone("UTC"))
1.31 + else:
1.32 + return dt
1.33 +
1.34 +def format_datetime(dt):
1.35 + if not dt:
1.36 + return None
1.37 + elif isinstance(dt, datetime):
1.38 + return dt.strftime("%Y%m%dT%H%M%SZ")
1.39 else:
1.40 return dt.strftime("%Y%m%d")
1.41
1.42 @@ -442,12 +454,43 @@
1.43 dtstart = self.get_utc_datetime("DTSTART")
1.44 dtend = self.get_utc_datetime("DTEND")
1.45
1.46 + # NOTE: Need also DURATION support.
1.47 +
1.48 + duration = dtend - dtstart
1.49 +
1.50 + # Recurrence rules create multiple instances to be checked.
1.51 + # Conflicts may only be assessed within a period defined by policy
1.52 + # for the agent, with instances outside that period being considered
1.53 + # unchecked.
1.54 +
1.55 + window_end = datetime.now() + timedelta(100)
1.56 +
1.57 + # NOTE: Need also RDATE and EXDATE support.
1.58 +
1.59 + rrule = self.get_value("RRULE")
1.60 +
1.61 + if rrule:
1.62 + selector = get_rule(dtstart, rrule)
1.63 + parameters = get_parameters(rrule)
1.64 + periods = []
1.65 + for start in selector.materialise(dtstart, window_end, parameters.get("COUNT"), parameters.get("BYSETPOS")):
1.66 + start = datetime(*start, tzinfo=timezone("UTC"))
1.67 + end = start + duration
1.68 + periods.append((format_datetime(start), format_datetime(end)))
1.69 + else:
1.70 + periods = [(format_datetime(dtstart), format_datetime(dtend))]
1.71 +
1.72 conflict = False
1.73 freebusy = self.store.get_freebusy(attendee)
1.74
1.75 if freebusy:
1.76 remove_period(freebusy, self.uid)
1.77 - conflict = period_overlaps(freebusy, (dtstart, dtend))
1.78 + conflict = True
1.79 + for start, end in periods:
1.80 + if period_overlaps(freebusy, (start, end)):
1.81 + break
1.82 + else:
1.83 + conflict = False
1.84 else:
1.85 freebusy = []
1.86
1.87 @@ -456,7 +499,8 @@
1.88 # attribute.)
1.89
1.90 if not conflict:
1.91 - insert_period(freebusy, (dtstart, dtend, self.uid))
1.92 + for start, end in periods:
1.93 + insert_period(freebusy, (start, end, self.uid))
1.94
1.95 if self.get_value("TRANSP") in (None, "OPAQUE"):
1.96 self.store.set_freebusy(attendee, freebusy)