1.1 --- a/imip_manager.py Sun Feb 08 01:26:33 2015 +0100
1.2 +++ b/imip_manager.py Sun Feb 08 18:39:03 2015 +0100
1.3 @@ -34,7 +34,7 @@
1.4 from imiptools.data import get_address, get_uri, make_freebusy, parse_object, \
1.5 Object, to_part
1.6 from imiptools.dates import format_datetime, format_time, get_date, get_datetime, \
1.7 - get_datetime_item, \
1.8 + get_datetime_item, get_default_timezone, \
1.9 get_end_of_day, get_start_of_day, get_start_of_next_day, \
1.10 get_timestamp, ends_on_same_day, to_timezone
1.11 from imiptools.mail import Messenger
1.12 @@ -292,7 +292,7 @@
1.13
1.14 def get_tzid(self):
1.15 prefs = self.get_preferences()
1.16 - return prefs.get("TZID", "UTC")
1.17 + return prefs.get("TZID") or get_default_timezone()
1.18
1.19 # Prettyprinting of dates and times.
1.20
1.21 @@ -1297,7 +1297,7 @@
1.22
1.23 has_continued = continuation and point != start
1.24 will_continue = not ends_on_same_day(point, end, tzid)
1.25 - is_organiser = obj.get_value("ORGANIZER") == self.user
1.26 + is_organiser = obj and obj.get_value("ORGANIZER") == self.user
1.27
1.28 css = " ".join(
1.29 ["event"] +
1.30 @@ -1314,7 +1314,7 @@
1.31 page.td(class_=css, rowspan=span)
1.32
1.33 if not obj:
1.34 - page.span("")
1.35 + page.span("(Participant is busy)")
1.36 else:
1.37 summary = obj.get_value("SUMMARY")
1.38
2.1 --- a/imiptools/content.py Sun Feb 08 01:26:33 2015 +0100
2.2 +++ b/imiptools/content.py Sun Feb 08 18:39:03 2015 +0100
2.3 @@ -175,7 +175,7 @@
2.4 def update_freebusy(self, freebusy, attendee, periods):
2.5 return update_freebusy(freebusy, attendee, periods, self.obj.get_value("TRANSP"), self.uid, self.store)
2.6
2.7 - def update_freebusy_from_organiser(self, attendee, organiser_item):
2.8 + def update_freebusy_from_organiser(self, attendee, organiser_item, tzid=None):
2.9
2.10 """
2.11 For the 'attendee', record free/busy information from the
2.12 @@ -189,12 +189,13 @@
2.13
2.14 if organiser_attr.get("PARTSTAT") != "DECLINED":
2.15 update_freebusy_for_other(freebusy, attendee, organiser,
2.16 - self.obj.get_periods(), self.obj.get_value("TRANSP"),
2.17 + self.obj.get_periods_for_freebusy(tzid),
2.18 + self.obj.get_value("TRANSP"),
2.19 self.uid, self.store)
2.20 else:
2.21 self.remove_from_freebusy_for_other(freebusy, attendee, organiser)
2.22
2.23 - def update_freebusy_from_attendees(self, organiser, attendees):
2.24 + def update_freebusy_from_attendees(self, organiser, attendees, tzid=None):
2.25
2.26 "For the 'organiser', record free/busy information from 'attendees'."
2.27
2.28 @@ -204,7 +205,8 @@
2.29
2.30 if attendee_attr.get("PARTSTAT") != "DECLINED":
2.31 update_freebusy_for_other(freebusy, organiser, attendee,
2.32 - self.obj.get_periods(), self.obj.get_value("TRANSP"),
2.33 + self.obj.get_periods_for_freebusy(tzid),
2.34 + self.obj.get_value("TRANSP"),
2.35 self.uid, self.store)
2.36 else:
2.37 self.remove_from_freebusy_for_other(freebusy, organiser, attendee)
3.1 --- a/imiptools/data.py Sun Feb 08 01:26:33 2015 +0100
3.2 +++ b/imiptools/data.py Sun Feb 08 18:39:03 2015 +0100
3.3 @@ -21,7 +21,8 @@
3.4
3.5 from datetime import datetime, timedelta
3.6 from email.mime.text import MIMEText
3.7 -from imiptools.dates import format_datetime, get_datetime, to_utc_datetime
3.8 +from imiptools.dates import format_datetime, get_datetime, get_freebusy_period, \
3.9 + to_utc_datetime
3.10 from vCalendar import iterwrite, parse, ParseError, to_dict, to_node
3.11 from vRecurrence import get_parameters, get_rule
3.12 import email.utils
3.13 @@ -81,6 +82,10 @@
3.14 def get_periods(self, window_size=100):
3.15 return get_periods(self, window_size)
3.16
3.17 + def get_periods_for_freebusy(self, tzid, window_size=100):
3.18 + periods = self.get_periods(window_size)
3.19 + return get_periods_for_freebusy(self, periods, tzid)
3.20 +
3.21 # Construction and serialisation.
3.22
3.23 def make_calendar(nodes, method=None):
3.24 @@ -307,10 +312,25 @@
3.25 for start in selector.materialise(dtstart, window_end, parameters.get("COUNT"), parameters.get("BYSETPOS")):
3.26 start = datetime(*start, tzinfo=timezone("UTC"))
3.27 end = start + duration
3.28 - periods.append((format_datetime(start), format_datetime(end)))
3.29 + periods.append((start, end))
3.30 else:
3.31 - periods = [(format_datetime(dtstart), format_datetime(dtend))]
3.32 + periods = [(dtstart, dtend)]
3.33
3.34 return periods
3.35
3.36 +def get_periods_for_freebusy(obj, periods, tzid):
3.37 +
3.38 + start, start_attr = obj.get_datetime_item("DTSTART")
3.39 + end, end_attr = obj.get_datetime_item("DTEND")
3.40 +
3.41 + tzid = start_attr.get("TZID") or end_attr.get("TZID") or tzid
3.42 +
3.43 + l = []
3.44 +
3.45 + for start, end in periods:
3.46 + start, end = get_freebusy_period(start, end, tzid)
3.47 + l.append((format_datetime(start), format_datetime(end)))
3.48 +
3.49 + return l
3.50 +
3.51 # vim: tabstop=4 expandtab shiftwidth=4
4.1 --- a/imiptools/dates.py Sun Feb 08 01:26:33 2015 +0100
4.2 +++ b/imiptools/dates.py Sun Feb 08 18:39:03 2015 +0100
4.3 @@ -20,6 +20,7 @@
4.4 """
4.5
4.6 from datetime import date, datetime, timedelta
4.7 +from os.path import exists
4.8 from pytz import timezone, UnknownTimeZoneError
4.9 import re
4.10
4.11 @@ -46,6 +47,21 @@
4.12 else:
4.13 return dt
4.14
4.15 +def get_default_timezone():
4.16 +
4.17 + "Return the system time regime."
4.18 +
4.19 + filename = "/etc/timezone"
4.20 +
4.21 + if exists(filename):
4.22 + f = open(filename)
4.23 + try:
4.24 + return f.read().strip()
4.25 + finally:
4.26 + f.close()
4.27 + else:
4.28 + return None
4.29 +
4.30 def to_timezone(dt, name):
4.31
4.32 """
4.33 @@ -219,4 +235,31 @@
4.34
4.35 return format_datetime(to_timezone(datetime.utcnow(), "UTC"))
4.36
4.37 +def get_freebusy_period(start, end, tzid):
4.38 +
4.39 + """
4.40 + For the given 'start' datetime, together with the given 'end' datetime, and
4.41 + given a 'tzid' either from the datetimes or provided for the user, return a
4.42 + (start, end) tuple containing datetimes in the UTC time zone, where dates
4.43 + are converted to points in time so that each day has a specific start and
4.44 + end point defined in UTC.
4.45 + """
4.46 +
4.47 + start = to_utc_datetime_only(start, tzid)
4.48 + end = to_utc_datetime_only(end, tzid)
4.49 + return start, end
4.50 +
4.51 +def to_utc_datetime_only(dt, tzid):
4.52 +
4.53 + """
4.54 + Return the datetime 'dt' as a point in time in the UTC time zone, given the
4.55 + 'tzid' defined for the datetime. Where 'dt' is a date, the start of the
4.56 + indicated day is returned, defined in UTC.
4.57 + """
4.58 +
4.59 + if not isinstance(dt, datetime):
4.60 + return to_timezone(get_start_of_day(dt, tzid), "UTC")
4.61 + else:
4.62 + return to_timezone(dt, "UTC")
4.63 +
4.64 # vim: tabstop=4 expandtab shiftwidth=4
5.1 --- a/imiptools/handlers/person_outgoing.py Sun Feb 08 01:26:33 2015 +0100
5.2 +++ b/imiptools/handlers/person_outgoing.py Sun Feb 08 18:39:03 2015 +0100
5.3 @@ -22,6 +22,8 @@
5.4
5.5 from imiptools.content import Handler
5.6 from imiptools.data import uri_item
5.7 +from imiptools.dates import get_default_timezone
5.8 +from imiptools.profile import Preferences
5.9
5.10 class PersonHandler(Handler):
5.11
5.12 @@ -69,10 +71,15 @@
5.13
5.14 if update_freebusy:
5.15
5.16 + # Interpretation of periods can depend on the time zone.
5.17 +
5.18 + preferences = Preferences(identity)
5.19 + tzid = preferences.get("TZID") or get_default_timezone()
5.20 +
5.21 # If newer than any old version, discard old details from the
5.22 # free/busy record and check for suitability.
5.23
5.24 - periods = self.obj.get_periods()
5.25 + periods = self.obj.get_periods_for_freebusy(tzid)
5.26 freebusy = self.store.get_freebusy(identity)
5.27
5.28 if attr.get("PARTSTAT") != "DECLINED":
6.1 --- a/imiptools/handlers/resource.py Sun Feb 08 01:26:33 2015 +0100
6.2 +++ b/imiptools/handlers/resource.py Sun Feb 08 18:39:03 2015 +0100
6.3 @@ -21,7 +21,9 @@
6.4
6.5 from imiptools.content import Handler
6.6 from imiptools.data import get_address, get_uri, to_part
6.7 +from imiptools.dates import get_default_timezone
6.8 from imiptools.handlers.common import CommonFreebusy
6.9 +from imiptools.profile import Preferences
6.10
6.11 class ResourceHandler(Handler):
6.12
6.13 @@ -56,10 +58,15 @@
6.14
6.15 def _schedule_for_attendee(self, attendee, attendee_attr):
6.16
6.17 + # Interpretation of periods can depend on the time zone.
6.18 +
6.19 + preferences = Preferences(attendee)
6.20 + tzid = preferences.get("TZID") or get_default_timezone()
6.21 +
6.22 # If newer than any old version, discard old details from the
6.23 # free/busy record and check for suitability.
6.24
6.25 - periods = self.obj.get_periods()
6.26 + periods = self.obj.get_periods_for_freebusy(tzid)
6.27 freebusy = self.store.get_freebusy(attendee)
6.28 scheduled = self.can_schedule(freebusy, periods)
6.29
7.1 --- a/imiptools/period.py Sun Feb 08 01:26:33 2015 +0100
7.2 +++ b/imiptools/period.py Sun Feb 08 18:39:03 2015 +0100
7.3 @@ -23,7 +23,7 @@
7.4 from datetime import datetime, timedelta
7.5 from imiptools.dates import get_datetime, get_start_of_day, to_timezone
7.6
7.7 -# Time management with datetime strings.
7.8 +# Time management with datetime strings in the UTC time zone.
7.9
7.10 def can_schedule(freebusy, periods, uid):
7.11
7.12 @@ -396,5 +396,4 @@
7.13 _update_freebusy(freebusy, periods, transp, uid)
7.14 store.set_freebusy_for_other(user, freebusy, other)
7.15
7.16 -
7.17 # vim: tabstop=4 expandtab shiftwidth=4
8.1 --- a/tools/make_freebusy.py Sun Feb 08 01:26:33 2015 +0100
8.2 +++ b/tools/make_freebusy.py Sun Feb 08 18:39:03 2015 +0100
8.3 @@ -1,7 +1,8 @@
8.4 #!/usr/bin/env python
8.5
8.6 -from imiptools.data import get_utc_datetime, get_value, get_value_map, parse_object
8.7 -from imiptools.dates import format_datetime
8.8 +from imiptools.data import get_freebusy_period, get_datetime_item, get_value, get_value_map, parse_object
8.9 +from imiptools.dates import format_datetime, get_default_timezone
8.10 +from imiptools.profile import Preferences
8.11 from imip_store import FileStore, FilePublisher
8.12 import sys
8.13
8.14 @@ -11,11 +12,14 @@
8.15 print >>sys.stderr, "Need a user."
8.16 sys.exit(1)
8.17
8.18 +preferences = Preferences(user)
8.19 +tzid = preferences.get("TZID") or get_default_timezone()
8.20 +
8.21 s = FileStore()
8.22 p = FilePublisher()
8.23
8.24 events = set(s.get_events(user))
8.25 -cancelled = s.get_cancellations(user)
8.26 +cancelled = s.get_cancellations(user) or []
8.27
8.28 events.difference_update(cancelled)
8.29
8.30 @@ -30,11 +34,19 @@
8.31 continue
8.32 details, details_attr = obj.values()[0]
8.33
8.34 - for attendee, attendee_attr in get_value_map(details, "ATTENDEE").items():
8.35 - if attendee == user:
8.36 - if attendee_attr.get("PARTSTAT") != "DECLINED":
8.37 - fb.append((format_datetime(get_utc_datetime(details, "DTSTART")),
8.38 - format_datetime(get_utc_datetime(details, "DTEND")),
8.39 + participants = {}
8.40 + participants.update(get_value_map(details, "ATTENDEE"))
8.41 + participants.update(get_value_map(details, "ORGANIZER"))
8.42 +
8.43 + for participant, participant_attr in participants.items():
8.44 + if participant == user:
8.45 + if participant_attr.get("PARTSTAT") != "DECLINED":
8.46 + dtstart, dtstart_attr = get_datetime_item(details, "DTSTART")
8.47 + dtend, dtend_attr = get_datetime_item(details, "DTEND")
8.48 + event_tzid = dtstart_attr.get("TZID") or dtend_attr.get("TZID") or tzid
8.49 + dtstart, dtend = get_freebusy_period(dtstart, dtend, event_tzid)
8.50 + fb.append((format_datetime(dtstart),
8.51 + format_datetime(dtend),
8.52 get_value(details, "UID"),
8.53 get_value(details, "TRANSP")))
8.54 break