1.1 --- a/imiptools/freebusy/common.py Fri May 26 23:58:06 2017 +0200
1.2 +++ b/imiptools/freebusy/common.py Thu Jun 01 23:26:38 2017 +0200
1.3 @@ -30,7 +30,10 @@
1.4 "Interpret 's' using 'encoding', preserving None."
1.5
1.6 if s:
1.7 - return unicode(s, encoding)
1.8 + if isinstance(s, unicode):
1.9 + return s
1.10 + else:
1.11 + return unicode(s, encoding)
1.12 else:
1.13 return s
1.14
1.15 @@ -43,6 +46,39 @@
1.16 else:
1.17 return s
1.18
1.19 +class period_from_tuple:
1.20 +
1.21 + "Convert a tuple to an instance of the given 'period_class'."
1.22 +
1.23 + def __init__(self, period_class):
1.24 + self.period_class = period_class
1.25 + def __call__(self, t):
1.26 + return make_period(t, self.period_class)
1.27 +
1.28 +def period_to_tuple(p):
1.29 +
1.30 + "Convert period 'p' to a tuple for serialisation."
1.31 +
1.32 + return p.as_tuple(strings_only=True)
1.33 +
1.34 +def make_period(t, period_class):
1.35 +
1.36 + "Convert tuple 't' to an instance of the given 'period_class'."
1.37 +
1.38 + args = []
1.39 + for arg, column in zip(t, period_class.period_columns):
1.40 + args.append(from_string(arg, "utf-8"))
1.41 + return period_class(*args)
1.42 +
1.43 +def make_tuple(t, period_class):
1.44 +
1.45 + "Restrict tuple 't' to the columns appropriate for 'period_class'."
1.46 +
1.47 + args = []
1.48 + for arg, column in zip(t, period_class.period_columns):
1.49 + args.append(arg)
1.50 + return tuple(args)
1.51 +
1.52
1.53
1.54 # Period abstractions.
1.55 @@ -51,6 +87,11 @@
1.56
1.57 "A free/busy record abstraction."
1.58
1.59 + period_columns = [
1.60 + "start", "end", "object_uid", "transp", "object_recurrenceid",
1.61 + "summary", "organiser"
1.62 + ]
1.63 +
1.64 def __init__(self, start, end, uid=None, transp=None, recurrenceid=None,
1.65 summary=None, organiser=None):
1.66
1.67 @@ -61,7 +102,7 @@
1.68 """
1.69
1.70 PeriodBase.__init__(self, start, end)
1.71 - self.uid = uid
1.72 + self.uid = uid or None
1.73 self.transp = transp or None
1.74 self.recurrenceid = recurrenceid or None
1.75 self.summary = summary or None
1.76 @@ -143,6 +184,8 @@
1.77
1.78 "A free/busy record abstraction for an offer period."
1.79
1.80 + period_columns = FreeBusyPeriod.period_columns + ["expires"]
1.81 +
1.82 def __init__(self, start, end, uid=None, transp=None, recurrenceid=None,
1.83 summary=None, organiser=None, expires=None):
1.84
1.85 @@ -180,6 +223,8 @@
1.86
1.87 "A free/busy record abstraction for a quota group period."
1.88
1.89 + period_columns = FreeBusyPeriod.period_columns + ["attendee"]
1.90 +
1.91 def __init__(self, start, end, uid=None, transp=None, recurrenceid=None,
1.92 summary=None, organiser=None, attendee=None):
1.93
1.94 @@ -225,15 +270,14 @@
1.95 def __repr__(self):
1.96 return "FreeBusyGroupPeriod%r" % (self.as_tuple(),)
1.97
1.98 +
1.99 +
1.100 +# Collection abstractions.
1.101 +
1.102 class FreeBusyCollectionBase:
1.103
1.104 "Common operations on free/busy period collections."
1.105
1.106 - period_columns = [
1.107 - "start", "end", "object_uid", "transp", "object_recurrenceid",
1.108 - "summary", "organiser"
1.109 - ]
1.110 -
1.111 period_class = FreeBusyPeriod
1.112
1.113 def __init__(self, mutable=True):
1.114 @@ -243,6 +287,12 @@
1.115 if not self.mutable:
1.116 raise TypeError, "Cannot mutate this collection."
1.117
1.118 + def close(self):
1.119 +
1.120 + "Close the collection."
1.121 +
1.122 + pass
1.123 +
1.124 def copy(self):
1.125
1.126 "Make an independent mutable copy of the collection."
1.127 @@ -256,10 +306,7 @@
1.128 column details.
1.129 """
1.130
1.131 - args = []
1.132 - for arg, column in zip(t, self.period_columns):
1.133 - args.append(from_string(arg, "utf-8"))
1.134 - return self.period_class(*args)
1.135 + return make_period(t, self.period_class)
1.136
1.137 def make_tuple(self, t):
1.138
1.139 @@ -268,10 +315,7 @@
1.140 column details.
1.141 """
1.142
1.143 - args = []
1.144 - for arg, column in zip(t, self.period_columns):
1.145 - args.append(arg)
1.146 - return tuple(args)
1.147 + return make_tuple(t, self.period_class)
1.148
1.149 # List emulation methods.
1.150
1.151 @@ -284,6 +328,16 @@
1.152
1.153 # Operations.
1.154
1.155 + def insert_period(self, period):
1.156 +
1.157 + """
1.158 + Insert the given 'period' into the collection.
1.159 +
1.160 + This should be implemented in subclasses.
1.161 + """
1.162 +
1.163 + pass
1.164 +
1.165 def insert_periods(self, periods):
1.166
1.167 "Insert the given 'periods' into the collection."
1.168 @@ -448,7 +502,6 @@
1.169
1.170 "A mix-in that supports the affected attendee in free/busy periods."
1.171
1.172 - period_columns = FreeBusyCollectionBase.period_columns + ["attendee"]
1.173 period_class = FreeBusyGroupPeriod
1.174
1.175 def _update_freebusy(self, periods, uid, recurrenceid, attendee=None):
1.176 @@ -486,7 +539,6 @@
1.177
1.178 "A mix-in that supports the expiry datetime in free/busy periods."
1.179
1.180 - period_columns = FreeBusyCollectionBase.period_columns + ["expires"]
1.181 period_class = FreeBusyOfferPeriod
1.182
1.183 def update_freebusy(self, periods, transp, uid, recurrenceid, summary, organiser, expires=None):
1.184 @@ -526,7 +578,27 @@
1.185 """
1.186
1.187 FreeBusyCollectionBase.__init__(self, mutable)
1.188 - self.periods = periods or []
1.189 +
1.190 + if periods is not None:
1.191 + self.periods = periods
1.192 + else:
1.193 + self.periods = []
1.194 +
1.195 + def get_filename(self):
1.196 +
1.197 + "Return any filename for the periods collection."
1.198 +
1.199 + if hasattr(self.periods, "filename"):
1.200 + return self.periods.filename
1.201 + else:
1.202 + return None
1.203 +
1.204 + def close(self):
1.205 +
1.206 + "Close the collection."
1.207 +
1.208 + if hasattr(self.periods, "close"):
1.209 + self.periods.close()
1.210
1.211 # List emulation methods.
1.212
1.213 @@ -542,6 +614,11 @@
1.214 def __getitem__(self, i):
1.215 return self.periods[i]
1.216
1.217 + # Dictionary emulation methods (even though this is not a mapping).
1.218 +
1.219 + def clear(self):
1.220 + del self.periods[:]
1.221 +
1.222 # Operations.
1.223
1.224 def insert_period(self, period):