1.1 --- a/imip_manager.py Sat Mar 07 00:13:41 2015 +0100
1.2 +++ b/imip_manager.py Sun Mar 08 01:35:43 2015 +0100
1.3 @@ -228,7 +228,9 @@
1.4 update_freebusy(freebusy,
1.5 self.obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()),
1.6 self.obj.get_value("TRANSP") or "OPAQUE",
1.7 - self.uid, self.recurrenceid)
1.8 + self.uid, self.recurrenceid,
1.9 + self.obj.get_value("SUMMARY"),
1.10 + organiser)
1.11
1.12 user_attr = self.messenger and self.messenger.sender != get_address(self.user) and \
1.13 {"SENT-BY" : get_uri(self.messenger.sender)} or {}
1.14 @@ -374,15 +376,23 @@
1.15 obj = self._get_object(uid, recurrenceid)
1.16 if obj:
1.17 periods = obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end())
1.18 -
1.19 - # Subtract any recurrences from the free/busy details of a parent
1.20 - # object.
1.21 -
1.22 recurrenceids = self._get_recurrences(uid)
1.23
1.24 + # Convert the periods to more substantial free/busy items.
1.25 +
1.26 for start, end in periods:
1.27 +
1.28 + # Subtract any recurrences from the free/busy details of a
1.29 + # parent object.
1.30 +
1.31 if recurrenceid or start not in recurrenceids:
1.32 - summary.append((start, end, uid, obj.get_value("TRANSP"), recurrenceid))
1.33 + summary.append((
1.34 + start, end, uid,
1.35 + obj.get_value("TRANSP"),
1.36 + recurrenceid,
1.37 + obj.get_value("SUMMARY"),
1.38 + obj.get_value("ORGANIZER")
1.39 + ))
1.40 return summary
1.41
1.42 # Preference methods.
1.43 @@ -430,7 +440,9 @@
1.44 update_freebusy(freebusy,
1.45 obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()),
1.46 is_only_organiser and "ORG" or obj.get_value("TRANSP"),
1.47 - uid, recurrenceid)
1.48 + uid, recurrenceid,
1.49 + obj.get_value("SUMMARY"),
1.50 + obj.get_value("ORGANIZER"))
1.51
1.52 # Subtract any recurrences from the free/busy details of a parent
1.53 # object.
1.54 @@ -709,7 +721,7 @@
1.55 elif save:
1.56 to_cancel = self.update_attendees(obj, added, removed)
1.57 self.store.set_event(self.user, uid, recurrenceid, node=obj.to_node())
1.58 - self.update_freebusy(uid, recurrenceid, obj=obj)
1.59 + self.update_freebusy(uid, recurrenceid, obj)
1.60 self.remove_request(uid, recurrenceid)
1.61
1.62 # Remove the request and the object.
1.63 @@ -1702,7 +1714,7 @@
1.64 page.td.close()
1.65 empty = 0
1.66
1.67 - start, end, uid, recurrenceid, key = get_freebusy_details(t)
1.68 + start, end, uid, recurrenceid, summary, organiser, key = get_freebusy_details(t)
1.69 span = spans[key]
1.70
1.71 # Produce a table cell only at the start of the period
1.72 @@ -1710,11 +1722,9 @@
1.73
1.74 if point == start or continuation:
1.75
1.76 - obj = self._get_object(uid, recurrenceid)
1.77 -
1.78 has_continued = continuation and point != start
1.79 will_continue = not ends_on_same_day(point, end, tzid)
1.80 - is_organiser = obj and get_uri(obj.get_value("ORGANIZER")) == self.user
1.81 + is_organiser = organiser == self.user
1.82
1.83 css = " ".join(
1.84 ["event"] +
1.85 @@ -1732,18 +1742,13 @@
1.86 else:
1.87 page.td(class_=css, rowspan=span)
1.88
1.89 - if not obj:
1.90 - page.span("(Participant is busy)")
1.91 + # Only link to events if they are not being
1.92 + # updated by requests.
1.93 +
1.94 + if not summary or (uid, recurrenceid) in self._get_requests() and group_type != "request":
1.95 + page.span(summary or "(Participant is busy)")
1.96 else:
1.97 - summary = obj.get_value("SUMMARY")
1.98 -
1.99 - # Only link to events if they are not being
1.100 - # updated by requests.
1.101 -
1.102 - if (uid, recurrenceid) in self._get_requests() and group_type != "request":
1.103 - page.span(summary)
1.104 - else:
1.105 - page.a(summary, href=self.link_to(uid, recurrenceid))
1.106 + page.a(summary, href=self.link_to(uid, recurrenceid))
1.107
1.108 page.td.close()
1.109 else:
2.1 --- a/imip_store.py Sat Mar 07 00:13:41 2015 +0100
2.2 +++ b/imip_store.py Sun Mar 08 01:35:43 2015 +0100
2.3 @@ -26,6 +26,7 @@
2.4 from os.path import exists, isfile, join
2.5 from os import listdir, remove, rmdir
2.6 from time import sleep
2.7 +import codecs
2.8
2.9 class FileStore(FileBase):
2.10
2.11 @@ -61,7 +62,7 @@
2.12
2.13 self.acquire_lock(user)
2.14 try:
2.15 - f = open(filename, "rb")
2.16 + f = codecs.open(filename, "rb", encoding="utf-8")
2.17 try:
2.18 l = []
2.19 for line in f.readlines():
2.20 @@ -88,7 +89,7 @@
2.21
2.22 self.acquire_lock(user)
2.23 try:
2.24 - f = open(filename, "wb")
2.25 + f = codecs.open(filename, "wb", encoding="utf-8")
2.26 try:
2.27 for item in items:
2.28 if empty_defaults:
2.29 @@ -514,11 +515,11 @@
2.30 rwrite(("UID", {}, user))
2.31 rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))
2.32
2.33 - for start, end, uid, transp, recurrenceid in freebusy:
2.34 + for start, end, uid, transp, recurrenceid, summary, organiser in freebusy:
2.35 if not transp or transp == "OPAQUE":
2.36 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
2.37
2.38 - f = open(filename, "w")
2.39 + f = open(filename, "wb")
2.40 try:
2.41 to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH"))
2.42 finally:
3.1 --- a/imiptools/content.py Sat Mar 07 00:13:41 2015 +0100
3.2 +++ b/imiptools/content.py Sun Mar 08 01:35:43 2015 +0100
3.3 @@ -207,8 +207,11 @@
3.4 event.
3.5 """
3.6
3.7 - update_freebusy(freebusy, periods, transp or self.obj.get_value("TRANSP"),
3.8 - self.uid, recurrenceid)
3.9 + update_freebusy(freebusy, periods,
3.10 + transp or self.obj.get_value("TRANSP"),
3.11 + self.uid, recurrenceid,
3.12 + self.obj.get_value("SUMMARY"),
3.13 + self.obj.get_value("ORGANIZER"))
3.14
3.15 def update_freebusy(self, freebusy, periods, transp=None):
3.16
4.1 --- a/imiptools/data.py Sat Mar 07 00:13:41 2015 +0100
4.2 +++ b/imiptools/data.py Sun Mar 08 01:35:43 2015 +0100
4.3 @@ -159,7 +159,7 @@
4.4 rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, periods[0][0]))
4.5 rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, periods[-1][1]))
4.6
4.7 - for start, end, uid, transp, recurrenceid in periods:
4.8 + for start, end, uid, transp, recurrenceid, summary, organiser in periods:
4.9 if transp == "OPAQUE":
4.10 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
4.11
5.1 --- a/imiptools/period.py Sat Mar 07 00:13:41 2015 +0100
5.2 +++ b/imiptools/period.py Sun Mar 08 01:35:43 2015 +0100
5.3 @@ -33,7 +33,7 @@
5.4 """
5.5
5.6 for conflict in have_conflict(freebusy, periods, True):
5.7 - start, end, found_uid, found_transp, found_recurrenceid = conflict
5.8 + start, end, found_uid, found_transp, found_recurrenceid = conflict[:5]
5.9 if found_uid != uid and found_recurrenceid != recurrenceid:
5.10 return False
5.11
5.12 @@ -390,7 +390,7 @@
5.13 for point, active in slots:
5.14 for t in active:
5.15 if t and len(t) >= 2:
5.16 - start, end, uid, recurrenceid, key = get_freebusy_details(t)
5.17 + start, end, uid, recurrenceid, summary, organiser, key = get_freebusy_details(t)
5.18
5.19 try:
5.20 start_slot = points.index(start)
5.21 @@ -406,12 +406,15 @@
5.22
5.23 def get_freebusy_details(t):
5.24
5.25 - "Return a tuple of the form (start, end, uid, recurrenceid, key) from 't'."
5.26 + """
5.27 + Return a tuple of the form (start, end, uid, recurrenceid, summary,
5.28 + organiser, key) from 't'.
5.29 + """
5.30
5.31 # Handle both complete free/busy details...
5.32
5.33 - if len(t) > 4:
5.34 - start, end, uid, transp, recurrenceid = t[:5]
5.35 + if len(t) >= 7:
5.36 + start, end, uid, transp, recurrenceid, summary, organiser = t[:7]
5.37 key = uid, recurrenceid
5.38
5.39 # ...and published details without specific event details.
5.40 @@ -420,20 +423,22 @@
5.41 start, end = t[:2]
5.42 uid = None
5.43 recurrenceid = None
5.44 + summary = None
5.45 + organiser = None
5.46 key = (start, end)
5.47
5.48 - return start, end, uid, recurrenceid, key
5.49 + return start, end, uid, recurrenceid, summary, organiser, key
5.50
5.51 -def update_freebusy(freebusy, periods, transp, uid, recurrenceid):
5.52 +def update_freebusy(freebusy, periods, transp, uid, recurrenceid, summary, organiser):
5.53
5.54 """
5.55 - Update the free/busy details with the given 'periods', 'transp' setting and
5.56 - 'uid' plus 'recurrenceid'.
5.57 + Update the free/busy details with the given 'periods', 'transp' setting,
5.58 + 'uid' plus 'recurrenceid' and 'summary' and 'organiser' details.
5.59 """
5.60
5.61 remove_period(freebusy, uid, recurrenceid)
5.62
5.63 for start, end in periods:
5.64 - insert_period(freebusy, (start, end, uid, transp, recurrenceid))
5.65 + insert_period(freebusy, (start, end, uid, transp, recurrenceid, summary, organiser))
5.66
5.67 # vim: tabstop=4 expandtab shiftwidth=4
6.1 --- a/tools/make_freebusy.py Sat Mar 07 00:13:41 2015 +0100
6.2 +++ b/tools/make_freebusy.py Sun Mar 08 01:35:43 2015 +0100
6.3 @@ -6,24 +6,36 @@
6.4 from imip_store import FileStore, FilePublisher
6.5 import sys
6.6
6.7 -def get_periods(fb, obj, tzid, window_end, only_organiser):
6.8 +def get_periods(fb, obj, tzid, window_end, only_organiser, recurrenceids):
6.9
6.10 # Update free/busy details with the actual periods associated with the event.
6.11
6.12 + recurrenceid = format_datetime(obj.get_utc_datetime("RECURRENCE-ID")) or ""
6.13 +
6.14 for start, end in obj.get_periods_for_freebusy(tzid, window_end):
6.15 - fb.append((start, end,
6.16 - obj.get_value("UID"),
6.17 - only_organiser and "ORG" or obj.get_value("TRANSP") or "OPAQUE",
6.18 - format_datetime(obj.get_utc_datetime("RECURRENCE-ID")) or "",
6.19 - ))
6.20 + if recurrenceid or start not in recurrenceids:
6.21 + fb.append((
6.22 + start, end,
6.23 + obj.get_value("UID"),
6.24 + only_organiser and "ORG" or obj.get_value("TRANSP") or "OPAQUE",
6.25 + recurrenceid,
6.26 + obj.get_value("SUMMARY"),
6.27 + obj.get_value("ORGANIZER")
6.28 + ))
6.29
6.30 # Main program.
6.31
6.32 try:
6.33 user = sys.argv[1]
6.34 - store_and_publish = "-s" in sys.argv[2:]
6.35 + args = sys.argv[2:]
6.36 + participant = args and args[0] not in ("-n", "-s") and args[0] or user
6.37 + store_and_publish = "-s" in args
6.38 + include_needs_action = "-n" in args
6.39 except IndexError:
6.40 - print >>sys.stderr, "Need a user, along with the -s option if updating the store."
6.41 + print >>sys.stderr, """\
6.42 +Need a user and an optional participant (if different from the user),
6.43 +along with the -s option if updating the store and the published details.
6.44 +"""
6.45 sys.exit(1)
6.46
6.47 preferences = Preferences(user)
6.48 @@ -69,30 +81,39 @@
6.49 for obj in objs:
6.50 attendees = obj.get_value_map("ATTENDEE")
6.51 organiser = obj.get_value("ORGANIZER")
6.52 + recurrenceids = store.get_recurrences(user, obj.get_value("UID"))
6.53
6.54 for attendee, attendee_attr in attendees.items():
6.55
6.56 - # Only consider events where this user actually attends.
6.57 + # Only consider events where the stated participant actually attends.
6.58 +
6.59 + if attendee == participant:
6.60 + partstat = attendee_attr.get("PARTSTAT", "NEEDS-ACTION")
6.61
6.62 - if attendee == user:
6.63 - if attendee_attr.get("PARTSTAT", "NEEDS-ACTION") not in ("DECLINED", "DELEGATED", "NEEDS-ACTION"):
6.64 - get_periods(fb, obj, tzid, window_end, False)
6.65 + if partstat not in ("DECLINED", "DELEGATED", "NEEDS-ACTION") or \
6.66 + include_needs_action and partstat == "NEEDS-ACTION":
6.67 +
6.68 + get_periods(fb, obj, tzid, window_end, False, recurrenceids)
6.69 +
6.70 break
6.71
6.72 # Where not attending, retain the affected periods and mark them as
6.73 # organising periods.
6.74
6.75 else:
6.76 - if organiser == user:
6.77 - get_periods(fb, obj, tzid, window_end, True)
6.78 + if organiser == participant:
6.79 + get_periods(fb, obj, tzid, window_end, True, recurrenceids)
6.80
6.81 fb.sort()
6.82
6.83 # Store and publish the free/busy collection.
6.84
6.85 if store_and_publish:
6.86 - store.set_freebusy(user, fb)
6.87 - publisher.set_freebusy(user, fb)
6.88 + if user == participant:
6.89 + store.set_freebusy(user, fb)
6.90 + publisher.set_freebusy(user, fb)
6.91 + else:
6.92 + store.set_freebusy_for_other(user, fb, participant)
6.93 else:
6.94 for item in fb:
6.95 print "\t".join(item)