imip-agent

Annotated imiptools/handlers/person_outgoing.py

440:ab91c238fbb6
2015-03-25 Paul Boddie Created an imipweb package, moving the CGIEnvironment class into the env module.
paul@96 1
#!/usr/bin/env python
paul@96 2
paul@96 3
"""
paul@96 4
Handlers for a person for whom scheduling is performed, inspecting outgoing
paul@96 5
messages to obtain scheduling done externally.
paul@146 6
paul@146 7
Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
paul@146 8
paul@146 9
This program is free software; you can redistribute it and/or modify it under
paul@146 10
the terms of the GNU General Public License as published by the Free Software
paul@146 11
Foundation; either version 3 of the License, or (at your option) any later
paul@146 12
version.
paul@146 13
paul@146 14
This program is distributed in the hope that it will be useful, but WITHOUT
paul@146 15
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@146 16
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@146 17
details.
paul@146 18
paul@146 19
You should have received a copy of the GNU General Public License along with
paul@146 20
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@96 21
"""
paul@96 22
paul@360 23
from imiptools.data import get_window_end, uri_dict, uri_item, uri_values
paul@418 24
from imiptools.handlers import Handler
paul@380 25
from imiptools.period import remove_affected_period
paul@96 26
paul@96 27
class PersonHandler(Handler):
paul@96 28
paul@96 29
    "Handling mechanisms specific to people."
paul@96 30
paul@213 31
    def _get_identity(self, from_organiser=True):
paul@130 32
paul@140 33
        """
paul@140 34
        Get the identity of interest in a usable form for any unprocessed
paul@140 35
        object.
paul@140 36
        """
paul@130 37
paul@213 38
        identity, attr = item = uri_item(self.obj.get_item(from_organiser and "ORGANIZER" or "ATTENDEE"))
paul@96 39
paul@97 40
        # Check for event using UID.
paul@97 41
paul@213 42
        if not self.have_new_object(identity):
paul@140 43
            return None
paul@140 44
paul@140 45
        return item
paul@140 46
paul@213 47
    def _record(self, from_organiser=True, update_freebusy=False):
paul@140 48
paul@420 49
        """
paul@420 50
        Record details from the current object given a message originating
paul@420 51
        from an organiser if 'from_organiser' is set to a true value, updating
paul@420 52
        free/busy information if 'update_freebusy' is set to a true value.
paul@420 53
        """
paul@140 54
paul@213 55
        item = self._get_identity(from_organiser)
paul@140 56
        if not item:
paul@99 57
            return False
paul@97 58
paul@140 59
        identity, attr = item
paul@140 60
paul@268 61
        # Update the object.
paul@96 62
paul@268 63
        if from_organiser:
paul@334 64
paul@361 65
            # Set the complete event or an additional occurrence.
paul@334 66
paul@361 67
            self.store.set_event(identity, self.uid, self.recurrenceid, self.obj.to_node())
paul@334 68
paul@381 69
            # Remove additional recurrences if handling a complete event.
paul@381 70
paul@381 71
            if not self.recurrenceid:
paul@381 72
                self.store.remove_recurrences(identity, self.uid)
paul@381 73
paul@268 74
        else:
paul@420 75
            # Obtain valid attendees, merging their attendance with the stored
paul@420 76
            # object.
paul@420 77
paul@420 78
            attendees = self.require_attendees(from_organiser)
paul@268 79
            self.merge_attendance(attendees, identity)
paul@96 80
paul@160 81
        # Remove any associated request.
paul@160 82
paul@343 83
        self.store.dequeue_request(identity, self.uid, self.recurrenceid)
paul@160 84
paul@96 85
        # Update free/busy information.
paul@96 86
paul@96 87
        if update_freebusy:
paul@97 88
paul@360 89
            freebusy = self.store.get_freebusy(identity)
paul@354 90
paul@361 91
            # Interpretation of periods can depend on the time zone.
paul@361 92
paul@361 93
            tzid = self.get_tzid(identity)
paul@361 94
paul@360 95
            # Use the stored event in case the reply is incomplete, as is seen
paul@360 96
            # when Claws sends a REPLY for an object originally employing
paul@381 97
            # recurrence information.
paul@354 98
paul@360 99
            obj = self.get_object(identity)
paul@420 100
            if not obj:
paul@420 101
                return False # although this should not happen
paul@291 102
paul@97 103
            # If newer than any old version, discard old details from the
paul@97 104
            # free/busy record and check for suitability.
paul@97 105
paul@360 106
            periods = obj.get_periods_for_freebusy(tzid, get_window_end(tzid))
paul@97 107
paul@371 108
            self.update_freebusy_for_participant(freebusy, periods, attr,
paul@371 109
                from_organiser and self.is_not_attendee(identity, obj))
paul@97 110
paul@381 111
            # Remove either original recurrence or additional recurrence
paul@381 112
            # details depending on whether an additional recurrence or a
paul@381 113
            # complete event are being handled, respectively.
paul@380 114
paul@382 115
            self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(identity, self.uid))
paul@361 116
            self.store.set_freebusy(identity, freebusy)
paul@360 117
paul@96 118
            if self.publisher:
paul@96 119
                self.publisher.set_freebusy(identity, freebusy)
paul@96 120
paul@96 121
        return True
paul@96 122
paul@261 123
    def _remove(self, from_organiser=True, update_freebusy=False):
paul@140 124
paul@140 125
        "Remove free/busy information for any unprocessed object."
paul@140 126
paul@213 127
        item = self._get_identity(from_organiser)
paul@140 128
        if not item:
paul@140 129
            return False
paul@140 130
paul@140 131
        identity, attr = item
paul@140 132
paul@312 133
        # Only cancel the event completely if all attendees are given.
paul@314 134
        # NOTE: Need to also check for recurrence identifiers and selective
paul@314 135
        # NOTE: cancellations.
paul@312 136
paul@312 137
        obj = self.get_object(identity)
paul@420 138
        if not obj:
paul@420 139
            return False
paul@261 140
paul@312 141
        attendees = uri_dict(obj.get_value_map("ATTENDEE"))
paul@312 142
        all_attendees = set(attendees.keys())
paul@312 143
        given_attendees = set(uri_values(self.obj.get_values("ATTENDEE")))
paul@312 144
paul@312 145
        if given_attendees == all_attendees:
paul@343 146
            self.store.cancel_event(identity, self.uid, self.recurrenceid)
paul@261 147
paul@312 148
        # Otherwise, remove the given attendees and update the event.
paul@312 149
paul@312 150
        else:
paul@312 151
            for attendee in given_attendees:
paul@314 152
                if attendees.has_key(attendee):
paul@314 153
                    del attendees[attendee]
paul@312 154
            obj["ATTENDEE"] = attendees.items()
paul@264 155
paul@312 156
        # Update the stored object with sequence information.
paul@312 157
paul@312 158
        obj["SEQUENCE"] = self.obj.get_items("SEQUENCE")
paul@312 159
        obj["DTSTAMP"] = self.obj.get_items("DTSTAMP")
paul@312 160
paul@334 161
        # Set the complete event if not an additional occurrence.
paul@334 162
paul@361 163
        self.store.set_event(identity, self.uid, self.recurrenceid, obj.to_node())
paul@264 164
paul@261 165
        # Remove any associated request.
paul@261 166
paul@343 167
        self.store.dequeue_request(identity, self.uid, self.recurrenceid)
paul@261 168
paul@261 169
        # Update free/busy information.
paul@261 170
paul@261 171
        if update_freebusy:
paul@261 172
            freebusy = self.store.get_freebusy(identity)
paul@361 173
            self.remove_from_freebusy(freebusy)
paul@361 174
            self.store.set_freebusy(identity, freebusy)
paul@261 175
paul@261 176
            if self.publisher:
paul@261 177
                self.publisher.set_freebusy(identity, freebusy)
paul@140 178
paul@140 179
        return True
paul@140 180
paul@96 181
class Event(PersonHandler):
paul@96 182
paul@96 183
    "An event handler."
paul@96 184
paul@96 185
    def add(self):
paul@96 186
        pass
paul@96 187
paul@96 188
    def cancel(self):
paul@261 189
        self._remove(True, True)
paul@96 190
paul@96 191
    def counter(self):
paul@96 192
        pass
paul@96 193
paul@96 194
    def declinecounter(self):
paul@96 195
        pass
paul@96 196
paul@96 197
    def publish(self):
paul@213 198
        self._record(True, True)
paul@96 199
paul@96 200
    def refresh(self):
paul@353 201
        pass
paul@96 202
paul@96 203
    def reply(self):
paul@213 204
        self._record(False, True)
paul@96 205
paul@96 206
    def request(self):
paul@213 207
        self._record(True, True)
paul@96 208
paul@96 209
class Freebusy(PersonHandler):
paul@96 210
paul@96 211
    "A free/busy handler."
paul@96 212
paul@96 213
    def publish(self):
paul@96 214
        pass
paul@96 215
paul@96 216
    def reply(self):
paul@96 217
        pass
paul@96 218
paul@96 219
    def request(self):
paul@96 220
        pass
paul@96 221
paul@96 222
# Handler registry.
paul@96 223
paul@96 224
handlers = [
paul@96 225
    ("VFREEBUSY",   Freebusy),
paul@96 226
    ("VEVENT",      Event),
paul@96 227
    ]
paul@96 228
paul@96 229
# vim: tabstop=4 expandtab shiftwidth=4