1.1 --- a/conf/postgresql/schema.sql Wed May 11 14:04:30 2016 +0200
1.2 +++ b/conf/postgresql/schema.sql Wed May 11 15:59:28 2016 +0200
1.3 @@ -44,8 +44,7 @@
1.4 transp varchar,
1.5 object_recurrenceid varchar,
1.6 summary varchar,
1.7 - organiser varchar,
1.8 - expires varchar
1.9 + organiser varchar
1.10 );
1.11
1.12 create index freebusy_start on freebusy(store_user, "start");
1.13 @@ -75,8 +74,7 @@
1.14 transp varchar,
1.15 object_recurrenceid varchar,
1.16 summary varchar,
1.17 - organiser varchar,
1.18 - expires varchar
1.19 + organiser varchar
1.20 );
1.21
1.22 create index freebusy_other_start on freebusy_other(store_user, other, "start");
1.23 @@ -124,7 +122,7 @@
1.24 object_recurrenceid varchar,
1.25 summary varchar,
1.26 organiser varchar,
1.27 - expires varchar
1.28 + attendee varchar
1.29 );
1.30
1.31 create index quota_freebusy_start on quota_freebusy(quota, user_group, "start");
1.32 @@ -139,8 +137,7 @@
1.33 transp varchar,
1.34 object_recurrenceid varchar,
1.35 summary varchar,
1.36 - organiser varchar,
1.37 - expires varchar
1.38 + organiser varchar
1.39 );
1.40
1.41 create index user_freebusy_start on user_freebusy(quota, store_user, "start");
2.1 --- a/imiptools/client.py Wed May 11 14:04:30 2016 +0200
2.2 +++ b/imiptools/client.py Wed May 11 15:59:28 2016 +0200
2.3 @@ -27,6 +27,7 @@
2.4 from imiptools.dates import check_permitted_values, format_datetime, get_default_timezone, \
2.5 get_duration, get_timestamp
2.6 from imiptools.i18n import get_translator
2.7 +from imiptools.period import SupportAttendee, SupportExpires
2.8 from imiptools.profile import Preferences
2.9 from imiptools.stores import get_store, get_publisher, get_journal
2.10
2.11 @@ -285,7 +286,20 @@
2.12 offer.
2.13 """
2.14
2.15 - freebusy.update_freebusy(periods, transp, uid, recurrenceid, summary, organiser, expires)
2.16 + # Add specific attendee information for certain collections.
2.17 +
2.18 + if isinstance(freebusy, SupportAttendee):
2.19 + freebusy.update_freebusy(periods, transp, uid, recurrenceid, summary, organiser, self.user)
2.20 +
2.21 + # Add expiry datetime for certain collections.
2.22 +
2.23 + elif isinstance(freebusy, SupportExpires):
2.24 + freebusy.update_freebusy(periods, transp, uid, recurrenceid, summary, organiser, expires)
2.25 +
2.26 + # Provide only the essential attributes for other collections.
2.27 +
2.28 + else:
2.29 + freebusy.update_freebusy(periods, transp, uid, recurrenceid, summary, organiser)
2.30
2.31 # Preparation of messages communicating the state of events.
2.32
3.1 --- a/imiptools/period.py Wed May 11 14:04:30 2016 +0200
3.2 +++ b/imiptools/period.py Wed May 11 15:59:28 2016 +0200
3.3 @@ -34,9 +34,6 @@
3.4 if x is None: return y
3.5 else: return x
3.6
3.7 -def from_strings(t, encoding):
3.8 - return tuple([from_string(s, encoding) for s in t])
3.9 -
3.10 def from_string(s, encoding):
3.11 if s:
3.12 return unicode(s, encoding)
3.13 @@ -146,10 +143,17 @@
3.14 "A basic period abstraction."
3.15
3.16 def __init__(self, start, end):
3.17 +
3.18 + """
3.19 + Define a period according to 'start' and 'end' which may be special
3.20 + start/end of time values or iCalendar-format datetime strings.
3.21 + """
3.22 +
3.23 if isinstance(start, (date, PointInTime)):
3.24 self.start = start
3.25 else:
3.26 self.start = get_datetime(start) or StartOfTime()
3.27 +
3.28 if isinstance(end, (date, PointInTime)):
3.29 self.end = end
3.30 else:
3.31 @@ -358,16 +362,12 @@
3.32 "A free/busy record abstraction."
3.33
3.34 def __init__(self, start, end, uid=None, transp=None, recurrenceid=None,
3.35 - summary=None, organiser=None, expires=None):
3.36 + summary=None, organiser=None):
3.37
3.38 """
3.39 Initialise a free/busy period with the given 'start' and 'end' points,
3.40 plus any 'uid', 'transp', 'recurrenceid', 'summary' and 'organiser'
3.41 details.
3.42 -
3.43 - An additional 'expires' parameter can be used to indicate an expiry
3.44 - datetime in conjunction with free/busy offers made when countering
3.45 - event proposals.
3.46 """
3.47
3.48 PeriodBase.__init__(self, start, end)
3.49 @@ -376,7 +376,6 @@
3.50 self.recurrenceid = recurrenceid or None
3.51 self.summary = summary or None
3.52 self.organiser = organiser or None
3.53 - self.expires = expires or None
3.54
3.55 def as_tuple(self, strings_only=False, string_datetimes=False):
3.56
3.57 @@ -395,8 +394,7 @@
3.58 self.transp or strings_only and "OPAQUE" or None,
3.59 self.recurrenceid or null(self.recurrenceid),
3.60 self.summary or null(self.summary),
3.61 - self.organiser or null(self.organiser),
3.62 - self.expires or null(self.expires)
3.63 + self.organiser or null(self.organiser)
3.64 )
3.65
3.66 def __cmp__(self, other):
3.67 @@ -451,6 +449,79 @@
3.68 def make_corrected(self, start, end):
3.69 return self.__class__(start, end)
3.70
3.71 +class FreeBusyOfferPeriod(FreeBusyPeriod):
3.72 +
3.73 + "A free/busy record abstraction for an offer period."
3.74 +
3.75 + def __init__(self, start, end, uid=None, transp=None, recurrenceid=None,
3.76 + summary=None, organiser=None, expires=None):
3.77 +
3.78 + """
3.79 + Initialise a free/busy period with the given 'start' and 'end' points,
3.80 + plus any 'uid', 'transp', 'recurrenceid', 'summary' and 'organiser'
3.81 + details.
3.82 +
3.83 + An additional 'expires' parameter can be used to indicate an expiry
3.84 + datetime in conjunction with free/busy offers made when countering
3.85 + event proposals.
3.86 + """
3.87 +
3.88 + FreeBusyPeriod.__init__(self, start, end, uid, transp, recurrenceid,
3.89 + summary, organiser)
3.90 + self.expires = expires or None
3.91 +
3.92 + def as_tuple(self, strings_only=False, string_datetimes=False):
3.93 +
3.94 + """
3.95 + Return the initialisation parameter tuple, converting datetimes and
3.96 + false value parameters to strings if 'strings_only' is set to a true
3.97 + value. Otherwise, if 'string_datetimes' is set to a true value, only the
3.98 + datetime values are converted to strings.
3.99 + """
3.100 +
3.101 + null = lambda x: (strings_only and [""] or [x])[0]
3.102 + return FreeBusyPeriod.as_tuple(self, strings_only, string_datetimes) + (
3.103 + self.expires or null(self.expires),)
3.104 +
3.105 + def __repr__(self):
3.106 + return "FreeBusyOfferPeriod%r" % (self.as_tuple(),)
3.107 +
3.108 +class FreeBusyGroupPeriod(FreeBusyPeriod):
3.109 +
3.110 + "A free/busy record abstraction for a quota group period."
3.111 +
3.112 + def __init__(self, start, end, uid=None, transp=None, recurrenceid=None,
3.113 + summary=None, organiser=None, attendee=None):
3.114 +
3.115 + """
3.116 + Initialise a free/busy period with the given 'start' and 'end' points,
3.117 + plus any 'uid', 'transp', 'recurrenceid', 'summary' and 'organiser'
3.118 + details.
3.119 +
3.120 + An additional 'attendee' parameter can be used to indicate the identity
3.121 + of the attendee recording the period.
3.122 + """
3.123 +
3.124 + FreeBusyPeriod.__init__(self, start, end, uid, transp, recurrenceid,
3.125 + summary, organiser)
3.126 + self.attendee = attendee or None
3.127 +
3.128 + def as_tuple(self, strings_only=False, string_datetimes=False):
3.129 +
3.130 + """
3.131 + Return the initialisation parameter tuple, converting datetimes and
3.132 + false value parameters to strings if 'strings_only' is set to a true
3.133 + value. Otherwise, if 'string_datetimes' is set to a true value, only the
3.134 + datetime values are converted to strings.
3.135 + """
3.136 +
3.137 + null = lambda x: (strings_only and [""] or [x])[0]
3.138 + return FreeBusyPeriod.as_tuple(self, strings_only, string_datetimes) + (
3.139 + self.attendee or null(self.attendee),)
3.140 +
3.141 + def __repr__(self):
3.142 + return "FreeBusyGroupPeriod%r" % (self.as_tuple(),)
3.143 +
3.144 class RecurringPeriod(Period):
3.145
3.146 """
3.147 @@ -482,6 +553,13 @@
3.148
3.149 "Common operations on free/busy period collections."
3.150
3.151 + period_columns = [
3.152 + "start", "end", "object_uid", "transp", "object_recurrenceid",
3.153 + "summary", "organiser"
3.154 + ]
3.155 +
3.156 + period_class = FreeBusyPeriod
3.157 +
3.158 def __init__(self, mutable=True):
3.159 self.mutable = mutable
3.160
3.161 @@ -495,6 +573,30 @@
3.162
3.163 return FreeBusyCollection(list(self), True)
3.164
3.165 + def make_period(self, t):
3.166 +
3.167 + """
3.168 + Make a period using the given tuple of arguments and the collection's
3.169 + column details.
3.170 + """
3.171 +
3.172 + args = []
3.173 + for arg, column in zip(t, self.period_columns):
3.174 + args.append(from_string(arg, "utf-8"))
3.175 + return self.period_class(*args)
3.176 +
3.177 + def make_tuple(self, t):
3.178 +
3.179 + """
3.180 + Return a tuple from the given tuple 't' conforming to the collection's
3.181 + column details.
3.182 + """
3.183 +
3.184 + args = []
3.185 + for arg, column in zip(t, self.period_columns):
3.186 + args.append(arg)
3.187 + return tuple(args)
3.188 +
3.189 # List emulation methods.
3.190
3.191 def __iadd__(self, periods):
3.192 @@ -589,7 +691,7 @@
3.193 while True:
3.194 period = it.next()
3.195 if period.get_start_point() > end:
3.196 - fb.append(FreeBusyPeriod(start, end))
3.197 + fb.append(self.period_class(start, end))
3.198 start = period.get_start_point()
3.199 end = period.get_end_point()
3.200 else:
3.201 @@ -597,7 +699,7 @@
3.202 except StopIteration:
3.203 pass
3.204
3.205 - fb.append(FreeBusyPeriod(start, end))
3.206 + fb.append(self.period_class(start, end))
3.207 return FreeBusyCollection(fb)
3.208
3.209 def invert_freebusy(self):
3.210 @@ -605,7 +707,7 @@
3.211 "Return the free periods from the collection as a new collection."
3.212
3.213 if not self:
3.214 - return FreeBusyCollection([FreeBusyPeriod(None, None)])
3.215 + return FreeBusyCollection([self.period_class(None, None)])
3.216
3.217 # Coalesce periods that overlap or are adjacent.
3.218
3.219 @@ -616,21 +718,83 @@
3.220
3.221 first = fb[0].get_start_point()
3.222 if first:
3.223 - free.append(FreeBusyPeriod(None, first))
3.224 + free.append(self.period_class(None, first))
3.225
3.226 start = fb[0].get_end_point()
3.227
3.228 for period in fb[1:]:
3.229 - free.append(FreeBusyPeriod(start, period.get_start_point()))
3.230 + free.append(self.period_class(start, period.get_start_point()))
3.231 start = period.get_end_point()
3.232
3.233 # Add an end-of-time period if appropriate.
3.234
3.235 if start:
3.236 - free.append(FreeBusyPeriod(start, None))
3.237 + free.append(self.period_class(start, None))
3.238
3.239 return FreeBusyCollection(free)
3.240
3.241 + def _update_freebusy(self, periods, uid, recurrenceid):
3.242 +
3.243 + """
3.244 + Update the free/busy details with the given 'periods', using the given
3.245 + 'uid' plus 'recurrenceid' to remove existing periods.
3.246 + """
3.247 +
3.248 + self._check_mutable()
3.249 +
3.250 + self.remove_event_periods(uid, recurrenceid)
3.251 +
3.252 + for p in periods:
3.253 + self.insert_period(p)
3.254 +
3.255 + def update_freebusy(self, periods, transp, uid, recurrenceid, summary, organiser):
3.256 +
3.257 + """
3.258 + Update the free/busy details with the given 'periods', 'transp' setting,
3.259 + 'uid' plus 'recurrenceid' and 'summary' and 'organiser' details.
3.260 + """
3.261 +
3.262 + new_periods = []
3.263 +
3.264 + for p in periods:
3.265 + new_periods.append(
3.266 + self.period_class(p.get_start_point(), p.get_end_point(), uid, transp, recurrenceid, summary, organiser)
3.267 + )
3.268 +
3.269 + self._update_freebusy(new_periods, uid, recurrenceid)
3.270 +
3.271 +class SupportAttendee:
3.272 +
3.273 + "A mix-in that supports the affected attendee in free/busy periods."
3.274 +
3.275 + period_columns = FreeBusyCollectionBase.period_columns + ["attendee"]
3.276 + period_class = FreeBusyGroupPeriod
3.277 +
3.278 + def update_freebusy(self, periods, transp, uid, recurrenceid, summary, organiser, attendee=None):
3.279 +
3.280 + """
3.281 + Update the free/busy details with the given 'periods', 'transp' setting,
3.282 + 'uid' plus 'recurrenceid' and 'summary' and 'organiser' details.
3.283 +
3.284 + An optional 'attendee' indicates the attendee affected by the period.
3.285 + """
3.286 +
3.287 + new_periods = []
3.288 +
3.289 + for p in periods:
3.290 + new_periods.append(
3.291 + self.period_class(p.get_start_point(), p.get_end_point(), uid, transp, recurrenceid, summary, organiser, attendee)
3.292 + )
3.293 +
3.294 + self._update_freebusy(new_periods, uid, recurrenceid)
3.295 +
3.296 +class SupportExpires:
3.297 +
3.298 + "A mix-in that supports the expiry datetime in free/busy periods."
3.299 +
3.300 + period_columns = FreeBusyCollectionBase.period_columns + ["expires"]
3.301 + period_class = FreeBusyOfferPeriod
3.302 +
3.303 def update_freebusy(self, periods, transp, uid, recurrenceid, summary, organiser, expires=None):
3.304
3.305 """
3.306 @@ -641,12 +805,14 @@
3.307 free/busy offer.
3.308 """
3.309
3.310 - self._check_mutable()
3.311 -
3.312 - self.remove_event_periods(uid, recurrenceid)
3.313 + new_periods = []
3.314
3.315 for p in periods:
3.316 - self.insert_period(FreeBusyPeriod(p.get_start_point(), p.get_end_point(), uid, transp, recurrenceid, summary, organiser, expires))
3.317 + new_periods.append(
3.318 + self.period_class(p.get_start_point(), p.get_end_point(), uid, transp, recurrenceid, summary, organiser, expires)
3.319 + )
3.320 +
3.321 + self._update_freebusy(new_periods, uid, recurrenceid)
3.322
3.323 class FreeBusyCollection(FreeBusyCollectionBase):
3.324
3.325 @@ -851,6 +1017,18 @@
3.326 for fb in overlapping:
3.327 self.periods.remove(fb)
3.328
3.329 +class FreeBusyGroupCollection(SupportAttendee, FreeBusyCollection):
3.330 +
3.331 + "A collection of quota group free/busy objects."
3.332 +
3.333 + pass
3.334 +
3.335 +class FreeBusyOffersCollection(SupportExpires, FreeBusyCollection):
3.336 +
3.337 + "A collection of offered free/busy objects."
3.338 +
3.339 + pass
3.340 +
3.341 class FreeBusyDatabaseCollection(FreeBusyCollectionBase, DatabaseOperations):
3.342
3.343 """
3.344 @@ -858,8 +1036,6 @@
3.345 system.
3.346 """
3.347
3.348 - period_columns = ["start", "end", "object_uid", "transp", "object_recurrenceid", "summary", "organiser", "expires"]
3.349 -
3.350 def __init__(self, cursor, table_name, column_names=None, filter_values=None,
3.351 mutable=True, paramstyle=None):
3.352
3.353 @@ -875,9 +1051,6 @@
3.354 self.cursor = cursor
3.355 self.table_name = table_name
3.356
3.357 - def make_period(self, t):
3.358 - return FreeBusyPeriod(*from_strings(t, "utf-8"))
3.359 -
3.360 # List emulation methods.
3.361
3.362 def __nonzero__(self):
3.363 @@ -1151,6 +1324,18 @@
3.364
3.365 return columns, values
3.366
3.367 +class FreeBusyGroupDatabaseCollection(SupportAttendee, FreeBusyDatabaseCollection):
3.368 +
3.369 + "A collection of quota group free/busy objects."
3.370 +
3.371 + pass
3.372 +
3.373 +class FreeBusyOffersDatabaseCollection(SupportExpires, FreeBusyDatabaseCollection):
3.374 +
3.375 + "A collection of offered free/busy objects."
3.376 +
3.377 + pass
3.378 +
3.379 # Period layout.
3.380
3.381 def get_scale(periods, tzid, view_period=None):
4.1 --- a/imiptools/stores/database/common.py Wed May 11 14:04:30 2016 +0200
4.2 +++ b/imiptools/stores/database/common.py Wed May 11 15:59:28 2016 +0200
4.3 @@ -24,7 +24,9 @@
4.4 from datetime import datetime
4.5 from imiptools.data import parse_string, to_string
4.6 from imiptools.dates import format_datetime, get_datetime, to_timezone
4.7 -from imiptools.period import FreeBusyDatabaseCollection
4.8 +from imiptools.period import FreeBusyDatabaseCollection, \
4.9 + FreeBusyGroupDatabaseCollection, \
4.10 + FreeBusyOffersDatabaseCollection
4.11 from imiptools.sql import DatabaseOperations
4.12
4.13 class DatabaseStoreBase(DatabaseOperations):
4.14 @@ -429,12 +431,13 @@
4.15
4.16 # Free/busy period access.
4.17
4.18 - def get_freebusy(self, user, name=None, mutable=False):
4.19 + def get_freebusy(self, user, name=None, mutable=False, cls=None):
4.20
4.21 "Get free/busy details for the given 'user'."
4.22
4.23 table = name or "freebusy"
4.24 - return FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], mutable, self.paramstyle)
4.25 + cls = cls or FreeBusyDatabaseCollection
4.26 + return cls(self.cursor, table, ["store_user"], [user], mutable, self.paramstyle)
4.27
4.28 def get_freebusy_for_other(self, user, other, mutable=False):
4.29
4.30 @@ -443,14 +446,15 @@
4.31 table = "freebusy_other"
4.32 return FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], mutable, self.paramstyle)
4.33
4.34 - def set_freebusy(self, user, freebusy, name=None):
4.35 + def set_freebusy(self, user, freebusy, name=None, cls=None):
4.36
4.37 "For the given 'user', set 'freebusy' details."
4.38
4.39 table = name or "freebusy"
4.40 + cls = cls or FreeBusyDatabaseCollection
4.41
4.42 - if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table:
4.43 - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], True, self.paramstyle)
4.44 + if not isinstance(freebusy, cls) or freebusy.table_name != table:
4.45 + fbc = cls(self.cursor, table, ["store_user"], [user], True, self.paramstyle)
4.46 fbc += freebusy
4.47
4.48 return True
4.49 @@ -502,13 +506,13 @@
4.50
4.51 self.cursor.execute(query, values)
4.52
4.53 - return self.get_freebusy(user, "freebusy_offers", mutable)
4.54 + return self.get_freebusy(user, "freebusy_offers", mutable, FreeBusyOffersDatabaseCollection)
4.55
4.56 def set_freebusy_offers(self, user, freebusy):
4.57
4.58 "For the given 'user', set 'freebusy' offers."
4.59
4.60 - return self.set_freebusy(user, freebusy, "freebusy_offers")
4.61 + return self.set_freebusy(user, freebusy, "freebusy_offers", cls=FreeBusyOffersDatabaseCollection)
4.62
4.63 # Requests and counter-proposals.
4.64
4.65 @@ -952,21 +956,23 @@
4.66 self.cursor.execute(query, values)
4.67 return [r[0] for r in self.cursor.fetchall()]
4.68
4.69 - def get_freebusy(self, quota, user, mutable=False):
4.70 + def get_freebusy(self, quota, user, mutable=False, cls=None):
4.71
4.72 "Get free/busy details for the given 'quota' and 'user'."
4.73
4.74 table = "user_freebusy"
4.75 - return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], mutable, self.paramstyle)
4.76 + cls = cls or FreeBusyDatabaseCollection
4.77 + return cls(self.cursor, table, ["quota", "store_user"], [quota, user], mutable, self.paramstyle)
4.78
4.79 - def set_freebusy(self, quota, user, freebusy):
4.80 + def set_freebusy(self, quota, user, freebusy, cls=None):
4.81
4.82 "For the given 'quota' and 'user', set 'freebusy' details."
4.83
4.84 table = "user_freebusy"
4.85 + cls = cls or FreeBusyDatabaseCollection
4.86
4.87 - if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table:
4.88 - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], True, self.paramstyle)
4.89 + if not isinstance(freebusy, cls) or freebusy.table_name != table:
4.90 + fbc = cls(self.cursor, table, ["quota", "store_user"], [quota, user], True, self.paramstyle)
4.91 fbc += freebusy
4.92
4.93 return True
4.94 @@ -981,7 +987,7 @@
4.95 """
4.96
4.97 table = "quota_freebusy"
4.98 - return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], mutable, self.paramstyle)
4.99 + return FreeBusyGroupDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], mutable, self.paramstyle)
4.100
4.101 def set_entries(self, quota, group, entries):
4.102
4.103 @@ -992,8 +998,8 @@
4.104
4.105 table = "quota_freebusy"
4.106
4.107 - if not isinstance(entries, FreeBusyDatabaseCollection) or entries.table_name != table:
4.108 - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], True, self.paramstyle)
4.109 + if not isinstance(entries, FreeBusyGroupDatabaseCollection) or entries.table_name != table:
4.110 + fbc = FreeBusyGroupDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], True, self.paramstyle)
4.111 fbc += entries
4.112
4.113 return True
5.1 --- a/imiptools/stores/file.py Wed May 11 14:04:30 2016 +0200
5.2 +++ b/imiptools/stores/file.py Wed May 11 15:59:28 2016 +0200
5.3 @@ -26,7 +26,9 @@
5.4 from imiptools.data import make_calendar, parse_object, to_stream
5.5 from imiptools.dates import format_datetime, get_datetime, to_timezone
5.6 from imiptools.filesys import fix_permissions, FileBase
5.7 -from imiptools.period import FreeBusyPeriod, FreeBusyCollection
5.8 +from imiptools.period import FreeBusyPeriod, FreeBusyGroupPeriod, \
5.9 + FreeBusyOfferPeriod, FreeBusyCollection, \
5.10 + FreeBusyGroupCollection, FreeBusyOffersCollection
5.11 from imiptools.text import parse_line
5.12 from os.path import isdir, isfile, join
5.13 from os import listdir, remove, rmdir
5.14 @@ -148,6 +150,18 @@
5.15 finally:
5.16 self.release_lock(user)
5.17
5.18 + def _set_freebusy(self, user, freebusy, filename):
5.19 +
5.20 + """
5.21 + For the given 'user', convert the 'freebusy' details to a form suitable
5.22 + for writing to 'filename'.
5.23 + """
5.24 +
5.25 + # Obtain tuples from the free/busy objects.
5.26 +
5.27 + self._set_table_atomic(user, filename,
5.28 + map(lambda fb: freebusy.make_tuple(fb.as_tuple(strings_only=True)), list(freebusy)))
5.29 +
5.30 class Store(FileStoreBase, StoreBase):
5.31
5.32 "A file store of tabular free/busy data and objects."
5.33 @@ -462,7 +476,7 @@
5.34
5.35 # Free/busy period access.
5.36
5.37 - def get_freebusy(self, user, name=None, mutable=False):
5.38 + def get_freebusy(self, user, name=None, mutable=False, cls=None):
5.39
5.40 "Get free/busy details for the given 'user'."
5.41
5.42 @@ -471,7 +485,8 @@
5.43 if not filename or not isfile(filename):
5.44 periods = []
5.45 else:
5.46 - periods = map(lambda t: FreeBusyPeriod(*t),
5.47 + cls = cls or FreeBusyPeriod
5.48 + periods = map(lambda t: cls(*t),
5.49 self._get_table_atomic(user, filename))
5.50
5.51 return FreeBusyCollection(periods, mutable)
5.52 @@ -498,8 +513,7 @@
5.53 if not filename:
5.54 return False
5.55
5.56 - self._set_table_atomic(user, filename,
5.57 - map(lambda fb: fb.as_tuple(strings_only=True), list(freebusy)))
5.58 + self._set_freebusy(user, freebusy, filename)
5.59 return True
5.60
5.61 def set_freebusy_for_other(self, user, freebusy, other):
5.62 @@ -510,8 +524,7 @@
5.63 if not filename:
5.64 return False
5.65
5.66 - self._set_table_atomic(user, filename,
5.67 - map(lambda fb: fb.as_tuple(strings_only=True), list(freebusy)))
5.68 + self._set_freebusy(user, freebusy, filename)
5.69 return True
5.70
5.71 def get_freebusy_others(self, user):
5.72 @@ -542,7 +555,7 @@
5.73
5.74 self.acquire_lock(user)
5.75 try:
5.76 - l = self.get_freebusy(user, "freebusy-offers")
5.77 + l = self.get_freebusy(user, "freebusy-offers", cls=FreeBusyOfferPeriod)
5.78 for fb in l:
5.79 if fb.expires and get_datetime(fb.expires) <= now:
5.80 expired.append(fb)
5.81 @@ -554,7 +567,7 @@
5.82 finally:
5.83 self.release_lock(user)
5.84
5.85 - return FreeBusyCollection(offers, mutable)
5.86 + return FreeBusyOffersCollection(offers, mutable)
5.87
5.88 # Requests and counter-proposals.
5.89
5.90 @@ -884,7 +897,7 @@
5.91
5.92 return listdir(filename)
5.93
5.94 - def get_freebusy(self, quota, user, mutable=False):
5.95 + def get_freebusy(self, quota, user, mutable=False, cls=None):
5.96
5.97 "Get free/busy details for the given 'quota' and 'user'."
5.98
5.99 @@ -893,7 +906,8 @@
5.100 if not filename or not isfile(filename):
5.101 periods = []
5.102 else:
5.103 - periods = map(lambda t: FreeBusyPeriod(*t),
5.104 + cls = cls or FreeBusyPeriod
5.105 + periods = map(lambda t: cls(*t),
5.106 self._get_table_atomic(quota, filename))
5.107
5.108 return FreeBusyCollection(periods, mutable)
5.109 @@ -906,8 +920,7 @@
5.110 if not filename:
5.111 return False
5.112
5.113 - self._set_table_atomic(quota, filename,
5.114 - map(lambda fb: fb.as_tuple(strings_only=True), list(freebusy)))
5.115 + self._set_freebusy(quota, freebusy, filename)
5.116 return True
5.117
5.118 # Journal entry methods.
5.119 @@ -924,10 +937,10 @@
5.120 if not filename or not isfile(filename):
5.121 periods = []
5.122 else:
5.123 - periods = map(lambda t: FreeBusyPeriod(*t),
5.124 + periods = map(lambda t: FreeBusyGroupPeriod(*t),
5.125 self._get_table_atomic(quota, filename))
5.126
5.127 - return FreeBusyCollection(periods, mutable)
5.128 + return FreeBusyGroupCollection(periods, mutable)
5.129
5.130 def set_entries(self, quota, group, entries):
5.131
5.132 @@ -940,8 +953,7 @@
5.133 if not filename:
5.134 return False
5.135
5.136 - self._set_table_atomic(quota, filename,
5.137 - map(lambda fb: fb.as_tuple(strings_only=True), list(entries)))
5.138 + self._set_freebusy(quota, entries, filename)
5.139 return True
5.140
5.141 # vim: tabstop=4 expandtab shiftwidth=4