1.1 --- a/imiptools/handlers/__init__.py Sun Jul 26 02:01:24 2015 +0200
1.2 +++ b/imiptools/handlers/__init__.py Sun Jul 26 23:43:26 2015 +0200
1.3 @@ -22,13 +22,8 @@
1.4 from email.mime.text import MIMEText
1.5 from imiptools.client import ClientForObject
1.6 from imiptools.config import MANAGER_PATH, MANAGER_URL
1.7 -from imiptools.data import Object, get_address, get_uri, \
1.8 - is_new_object, uri_dict, uri_item, uri_values
1.9 -from imiptools.dates import format_datetime, get_recurrence_start_point, \
1.10 - to_timezone
1.11 -from imiptools.period import can_schedule, remove_period, \
1.12 - remove_additional_periods, remove_affected_period
1.13 -from imiptools.profile import Preferences
1.14 +from imiptools.data import get_address, get_uri, get_sender_identities, \
1.15 + uri_dict, uri_item
1.16 from socket import gethostname
1.17
1.18 # References to the Web interface.
1.19 @@ -101,128 +96,8 @@
1.20 def get_outgoing_methods(self):
1.21 return self.outgoing_methods
1.22
1.23 - # Convenience methods for modifying free/busy collections.
1.24 -
1.25 - def get_recurrence_start_point(self, recurrenceid):
1.26 -
1.27 - "Get 'recurrenceid' in a form suitable for matching free/busy entries."
1.28 -
1.29 - tzid = self.obj.get_tzid() or self.get_tzid()
1.30 - return get_recurrence_start_point(recurrenceid, tzid)
1.31 -
1.32 - def remove_from_freebusy(self, freebusy):
1.33 -
1.34 - "Remove this event from the given 'freebusy' collection."
1.35 -
1.36 - if not remove_period(freebusy, self.uid, self.recurrenceid) and self.recurrenceid:
1.37 - remove_affected_period(freebusy, self.uid, self.get_recurrence_start_point(self.recurrenceid))
1.38 -
1.39 - def remove_freebusy_for_recurrences(self, freebusy, recurrenceids=None):
1.40 -
1.41 - """
1.42 - Remove from 'freebusy' any original recurrence from parent free/busy
1.43 - details for the current object, if the current object is a specific
1.44 - additional recurrence. Otherwise, remove all additional recurrence
1.45 - information corresponding to 'recurrenceids', or if omitted, all
1.46 - recurrences.
1.47 - """
1.48 -
1.49 - if self.recurrenceid:
1.50 - recurrenceid = self.get_recurrence_start_point(self.recurrenceid)
1.51 - remove_affected_period(freebusy, self.uid, recurrenceid)
1.52 - else:
1.53 - # Remove obsolete recurrence periods.
1.54 -
1.55 - remove_additional_periods(freebusy, self.uid, recurrenceids)
1.56 -
1.57 - # Remove original periods affected by additional recurrences.
1.58 -
1.59 - if recurrenceids:
1.60 - for recurrenceid in recurrenceids:
1.61 - recurrenceid = self.get_recurrence_start_point(recurrenceid)
1.62 - remove_affected_period(freebusy, self.uid, recurrenceid)
1.63 -
1.64 - # Convenience methods for updating stored free/busy information.
1.65 -
1.66 - def update_freebusy_from_participant(self, participant_item, for_organiser):
1.67 -
1.68 - """
1.69 - For the calendar user, record the free/busy information for the
1.70 - 'participant_item' (a value plus attributes) representing a different
1.71 - identity, thus maintaining a separate record of their free/busy details.
1.72 - """
1.73 -
1.74 - participant, participant_attr = participant_item
1.75 -
1.76 - # A user does not store free/busy information for themself as another
1.77 - # party.
1.78 -
1.79 - if participant == self.user:
1.80 - return
1.81 -
1.82 - freebusy = self.store.get_freebusy_for_other(self.user, participant)
1.83 -
1.84 - # Obtain the stored object if the current object is not issued by the
1.85 - # organiser.
1.86 -
1.87 - obj = self.get_definitive_object(for_organiser)
1.88 - if not obj:
1.89 - return
1.90 -
1.91 - # Obtain the affected periods.
1.92 -
1.93 - periods = obj.get_periods(self.get_tzid(), self.get_window_end())
1.94 -
1.95 - # Record in the free/busy details unless a non-participating attendee.
1.96 - # Use any attendee information for an organiser, not the organiser's own
1.97 - # attributes.
1.98 -
1.99 - if for_organiser:
1.100 - participant_attr = obj.get_value_map("ATTENDEE").get(participant)
1.101 -
1.102 - self.update_freebusy_for_participant(freebusy, periods, participant_attr,
1.103 - for_organiser and not self.is_attendee(participant))
1.104 -
1.105 - # Tidy up any obsolete recurrences.
1.106 -
1.107 - self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
1.108 - self.store.set_freebusy_for_other(self.user, freebusy, participant)
1.109 -
1.110 - def update_freebusy_from_organiser(self, organiser_item):
1.111 -
1.112 - """
1.113 - For the current user, record free/busy information from the
1.114 - 'organiser_item' (a value plus attributes).
1.115 - """
1.116 -
1.117 - self.update_freebusy_from_participant(organiser_item, True)
1.118 -
1.119 - def update_freebusy_from_attendees(self, attendees):
1.120 -
1.121 - "For the current user, record free/busy information from 'attendees'."
1.122 -
1.123 - for attendee_item in attendees.items():
1.124 - self.update_freebusy_from_participant(attendee_item, False)
1.125 -
1.126 # Logic, filtering and access to calendar structures and other data.
1.127
1.128 - def is_attendee(self, identity, obj=None):
1.129 -
1.130 - """
1.131 - Return whether 'identity' is an attendee in the current object, or in
1.132 - 'obj' if specified.
1.133 - """
1.134 -
1.135 - return identity in uri_values((obj or self.obj).get_values("ATTENDEE"))
1.136 -
1.137 - def can_schedule(self, freebusy, periods):
1.138 -
1.139 - """
1.140 - Indicate whether within 'freebusy' the given 'periods' can be scheduled.
1.141 - """
1.142 -
1.143 - return can_schedule(freebusy, periods, self.uid, self.recurrenceid)
1.144 -
1.145 def filter_by_senders(self, mapping):
1.146
1.147 """
1.148 @@ -233,7 +108,7 @@
1.149
1.150 # Get a mapping from senders to identities.
1.151
1.152 - identities = self.get_sender_identities(mapping)
1.153 + identities = get_sender_identities(mapping)
1.154
1.155 # Find the senders that are valid.
1.156
1.157 @@ -242,7 +117,7 @@
1.158
1.159 # Return the true identities.
1.160
1.161 - return reduce(lambda a, b: a + b, [identities[get_uri(address)] for address in valid])
1.162 + return reduce(lambda a, b: a + b, [identities[get_uri(address)] for address in valid], [])
1.163 else:
1.164 return mapping
1.165
1.166 @@ -317,148 +192,4 @@
1.167
1.168 return organiser_item, attendees
1.169
1.170 - def get_sender_identities(self, mapping):
1.171 -
1.172 - """
1.173 - Return a mapping from actual senders to the identities for which they
1.174 - have provided data, extracting this information from the given
1.175 - 'mapping'.
1.176 - """
1.177 -
1.178 - senders = {}
1.179 -
1.180 - for value, attr in mapping.items():
1.181 - sent_by = attr.get("SENT-BY")
1.182 - if sent_by:
1.183 - sender = get_uri(sent_by)
1.184 - else:
1.185 - sender = value
1.186 -
1.187 - if not senders.has_key(sender):
1.188 - senders[sender] = []
1.189 -
1.190 - senders[sender].append(value)
1.191 -
1.192 - return senders
1.193 -
1.194 - def _get_object(self, uid, recurrenceid):
1.195 -
1.196 - """
1.197 - Return the stored object for the current user, with the given 'uid' and
1.198 - 'recurrenceid'.
1.199 - """
1.200 -
1.201 - fragment = self.store.get_event(self.user, uid, recurrenceid)
1.202 - return fragment and Object(fragment)
1.203 -
1.204 - def get_object(self):
1.205 -
1.206 - """
1.207 - Return the stored object to which the current object refers for the
1.208 - current user.
1.209 - """
1.210 -
1.211 - return self._get_object(self.uid, self.recurrenceid)
1.212 -
1.213 - def get_definitive_object(self, from_organiser):
1.214 -
1.215 - """
1.216 - Return an object considered definitive for the current transaction,
1.217 - using 'from_organiser' to select the current transaction's object if
1.218 - true, or selecting a stored object if false.
1.219 - """
1.220 -
1.221 - return from_organiser and self.obj or self.get_object()
1.222 -
1.223 - def get_parent_object(self):
1.224 -
1.225 - """
1.226 - Return the parent object to which the current object refers for the
1.227 - current user.
1.228 - """
1.229 -
1.230 - return self.recurrenceid and self._get_object(self.uid, None) or None
1.231 -
1.232 - def have_new_object(self, obj=None):
1.233 -
1.234 - """
1.235 - Return whether the current object is new to the current user (or if the
1.236 - given 'obj' is new).
1.237 - """
1.238 -
1.239 - obj = obj or self.get_object()
1.240 -
1.241 - # If found, compare SEQUENCE and potentially DTSTAMP.
1.242 -
1.243 - if obj:
1.244 - sequence = obj.get_value("SEQUENCE")
1.245 - dtstamp = obj.get_value("DTSTAMP")
1.246 -
1.247 - # If the request refers to an older version of the object, ignore
1.248 - # it.
1.249 -
1.250 - return is_new_object(sequence, self.sequence, dtstamp, self.dtstamp,
1.251 - self.is_partstat_updated(obj))
1.252 -
1.253 - return True
1.254 -
1.255 - def is_partstat_updated(self, obj):
1.256 -
1.257 - """
1.258 - Return whether the participant status has been updated in the current
1.259 - object in comparison to the given 'obj'.
1.260 -
1.261 - NOTE: Some clients like Claws Mail erase time information from DTSTAMP
1.262 - NOTE: and make it invalid. Thus, such attendance information may also be
1.263 - NOTE: incorporated into any new object assessment.
1.264 - """
1.265 -
1.266 - old_attendees = uri_dict(obj.get_value_map("ATTENDEE"))
1.267 - new_attendees = uri_dict(self.obj.get_value_map("ATTENDEE"))
1.268 -
1.269 - for attendee, attr in old_attendees.items():
1.270 - old_partstat = attr.get("PARTSTAT")
1.271 - new_attr = new_attendees.get(attendee)
1.272 - new_partstat = new_attr and new_attr.get("PARTSTAT")
1.273 -
1.274 - if old_partstat == "NEEDS-ACTION" and new_partstat and \
1.275 - new_partstat != old_partstat:
1.276 -
1.277 - return True
1.278 -
1.279 - return False
1.280 -
1.281 - def merge_attendance(self, attendees):
1.282 -
1.283 - """
1.284 - Merge attendance from the current object's 'attendees' into the version
1.285 - stored for the current user.
1.286 - """
1.287 -
1.288 - obj = self.get_object()
1.289 -
1.290 - if not obj or not self.have_new_object(obj):
1.291 - return False
1.292 -
1.293 - # Get attendee details in a usable form.
1.294 -
1.295 - attendee_map = uri_dict(obj.get_value_map("ATTENDEE"))
1.296 -
1.297 - for attendee, attendee_attr in attendees.items():
1.298 -
1.299 - # Update attendance in the loaded object.
1.300 -
1.301 - attendee_map[attendee] = attendee_attr
1.302 -
1.303 - # Set the new details and store the object.
1.304 -
1.305 - obj["ATTENDEE"] = attendee_map.items()
1.306 -
1.307 - # Set the complete event if not an additional occurrence.
1.308 -
1.309 - event = obj.to_node()
1.310 - self.store.set_event(self.user, self.uid, self.recurrenceid, event)
1.311 -
1.312 - return True
1.313 -
1.314 # vim: tabstop=4 expandtab shiftwidth=4