imip-agent

Annotated imiptools/handlers/person.py

412:964f71637e1b
2015-03-10 Paul Boddie Improved event datetime control styling.
paul@55 1
#!/usr/bin/env python
paul@55 2
paul@55 3
"""
paul@55 4
Handlers for a person for whom scheduling is performed.
paul@146 5
paul@146 6
Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
paul@146 7
paul@146 8
This program is free software; you can redistribute it and/or modify it under
paul@146 9
the terms of the GNU General Public License as published by the Free Software
paul@146 10
Foundation; either version 3 of the License, or (at your option) any later
paul@146 11
version.
paul@146 12
paul@146 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@146 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@146 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@146 16
details.
paul@146 17
paul@146 18
You should have received a copy of the GNU General Public License along with
paul@146 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@55 20
"""
paul@55 21
paul@213 22
from imiptools.content import Handler
paul@271 23
from imiptools.data import get_uri
paul@327 24
from imiptools.dates import format_datetime
paul@216 25
from imiptools.handlers.common import CommonFreebusy
paul@327 26
from imiptools.period import replace_overlapping
paul@179 27
from imiptools.profile import Preferences
paul@55 28
paul@67 29
class PersonHandler(Handler):
paul@55 30
paul@67 31
    "Handling mechanisms specific to people."
paul@55 32
paul@271 33
    def _record(self, from_organiser=True, queue=False, cancel=False):
paul@55 34
paul@100 35
        oa = self.require_organiser_and_attendees(from_organiser)
paul@55 36
        if not oa:
paul@61 37
            return False
paul@55 38
paul@94 39
        (organiser, organiser_attr), attendees = organiser_item, attendees = oa
paul@94 40
paul@110 41
        # Handle notifications and invitations.
paul@55 42
paul@100 43
        if from_organiser:
paul@110 44
paul@110 45
            # Process each attendee separately.
paul@110 46
paul@100 47
            for attendee, attendee_attr in attendees.items():
paul@100 48
paul@213 49
                if not self.have_new_object(attendee):
paul@100 50
                    continue
paul@100 51
paul@381 52
                # Set the complete event or an additional occurrence.
paul@334 53
paul@361 54
                self.store.set_event(attendee, self.uid, self.recurrenceid, self.obj.to_node())
paul@361 55
paul@381 56
                # Remove additional recurrences if handling a complete event.
paul@381 57
paul@381 58
                if not self.recurrenceid:
paul@381 59
                    self.store.remove_recurrences(attendee, self.uid)
paul@381 60
paul@334 61
                # Queue any request.
paul@55 62
paul@100 63
                if queue:
paul@343 64
                    self.store.queue_request(attendee, self.uid, self.recurrenceid)
paul@142 65
                elif cancel:
paul@343 66
                    self.store.cancel_event(attendee, self.uid, self.recurrenceid)
paul@100 67
paul@261 68
                    # No return message will occur to update the free/busy
paul@261 69
                    # information, so this is done here.
paul@261 70
paul@261 71
                    freebusy = self.store.get_freebusy(attendee)
paul@361 72
                    self.remove_from_freebusy(freebusy)
paul@361 73
paul@361 74
                    self.store.set_freebusy(attendee, freebusy)
paul@261 75
paul@261 76
                    if self.publisher:
paul@261 77
                        self.publisher.set_freebusy(attendee, freebusy)
paul@261 78
paul@268 79
                self.update_freebusy_from_organiser(attendee, organiser_item)
paul@268 80
paul@100 81
        # As organiser, update attendance.
paul@55 82
paul@100 83
        else:
paul@268 84
            if self.merge_attendance(attendees, organiser):
paul@268 85
                self.update_freebusy_from_attendees(organiser, attendees)
paul@61 86
paul@61 87
        return True
paul@61 88
paul@110 89
    def _record_freebusy(self, from_organiser=True):
paul@110 90
paul@110 91
        "Record free/busy information for the received information."
paul@110 92
paul@292 93
        if from_organiser:
paul@292 94
            organiser_item = self.require_organiser(from_organiser)
paul@292 95
            if not organiser_item:
paul@292 96
                return
paul@271 97
paul@292 98
            senders = [organiser_item]
paul@292 99
        else:
paul@292 100
            oa = self.require_organiser_and_attendees(from_organiser)
paul@292 101
            if not oa:
paul@292 102
                return
paul@218 103
paul@292 104
            organiser_item, attendees = oa
paul@292 105
            senders = attendees.items()
paul@292 106
paul@292 107
            if not senders:
paul@292 108
                return
paul@218 109
paul@110 110
        freebusy = []
paul@111 111
paul@213 112
        for value in self.obj.get_values("FREEBUSY") or []:
paul@110 113
            if not isinstance(value, list):
paul@110 114
                value = [value]
paul@110 115
            for v in value:
paul@110 116
                try:
paul@110 117
                    start, end = v.split("/", 1)
paul@110 118
                    freebusy.append((start, end))
paul@110 119
                except ValueError:
paul@110 120
                    pass
paul@110 121
paul@327 122
        dtstart = format_datetime(self.obj.get_utc_datetime("DTSTART"))
paul@327 123
        dtend = format_datetime(self.obj.get_utc_datetime("DTEND"))
paul@327 124
        user = get_uri(self.recipient)
paul@327 125
paul@271 126
        for sender, sender_attr in senders:
paul@335 127
            stored_freebusy = self.store.get_freebusy_for_other(user, sender)
paul@327 128
            replace_overlapping(stored_freebusy, (dtstart, dtend), freebusy)
paul@339 129
            self.store.set_freebusy_for_other(user, stored_freebusy, sender)
paul@110 130
paul@216 131
class Event(PersonHandler):
paul@67 132
paul@67 133
    "An event handler."
paul@67 134
paul@63 135
    def add(self):
paul@63 136
paul@63 137
        # NOTE: Queue a suggested modification to any active event.
paul@63 138
paul@182 139
        return self.wrap("An addition to an event has been received.", link=False)
paul@63 140
paul@63 141
    def cancel(self):
paul@63 142
paul@142 143
        "Queue a cancellation of any active event."
paul@63 144
paul@271 145
        self._record(from_organiser=True, queue=False, cancel=True)
paul@182 146
        return self.wrap("A cancellation has been received.", link=False)
paul@63 147
paul@63 148
    def counter(self):
paul@63 149
paul@63 150
        # NOTE: Queue a suggested modification to any active event.
paul@63 151
paul@182 152
        return self.wrap("A counter proposal has been received.", link=False)
paul@63 153
paul@63 154
    def declinecounter(self):
paul@63 155
paul@63 156
        # NOTE: Queue a suggested modification to any active event.
paul@63 157
paul@182 158
        return self.wrap("A declining counter proposal has been received.", link=False)
paul@63 159
paul@63 160
    def publish(self):
paul@63 161
paul@139 162
        "Register details of any relevant event."
paul@63 163
paul@271 164
        self._record(from_organiser=True, queue=False)
paul@182 165
        return self.wrap("Details of an event have been received.")
paul@63 166
paul@63 167
    def refresh(self):
paul@63 168
paul@353 169
        "Generate details of any active event."
paul@63 170
paul@353 171
        # NOTE: Return event details if configured to do so.
paul@353 172
paul@353 173
        return self.wrap("A request for updated event details has been received.")
paul@63 174
paul@61 175
    def reply(self):
paul@61 176
paul@61 177
        "Record replies and notify the recipient."
paul@61 178
paul@271 179
        self._record(from_organiser=False, queue=False)
paul@182 180
        return self.wrap("A reply has been received.")
paul@61 181
paul@61 182
    def request(self):
paul@61 183
paul@61 184
        "Hold requests and notify the recipient."
paul@61 185
paul@271 186
        self._record(from_organiser=True, queue=True)
paul@216 187
        return self.wrap("A request has been received.")
paul@60 188
paul@108 189
class Freebusy(PersonHandler, CommonFreebusy):
paul@55 190
paul@55 191
    "A free/busy handler."
paul@55 192
paul@55 193
    def publish(self):
paul@63 194
paul@110 195
        "Register free/busy information."
paul@110 196
paul@180 197
        self._record_freebusy(from_organiser=True)
paul@180 198
paul@180 199
        # Produce a message if configured to do so.
paul@63 200
paul@180 201
        preferences = Preferences(get_uri(self.recipient))
paul@180 202
        if preferences.get("freebusy_messages") == "notify":
paul@182 203
            return self.wrap("A free/busy update has been received.", link=False)
paul@55 204
paul@55 205
    def reply(self):
paul@55 206
paul@63 207
        "Record replies and notify the recipient."
paul@63 208
paul@180 209
        self._record_freebusy(from_organiser=False)
paul@180 210
paul@180 211
        # Produce a message if configured to do so.
paul@139 212
paul@180 213
        preferences = Preferences(get_uri(self.recipient))
paul@180 214
        if preferences.get("freebusy_messages") == "notify":
paul@182 215
            return self.wrap("A reply to a free/busy request has been received.", link=False)
paul@55 216
paul@55 217
    def request(self):
paul@55 218
paul@55 219
        """
paul@55 220
        Respond to a request by preparing a reply containing free/busy
paul@55 221
        information for each indicated attendee.
paul@55 222
        """
paul@55 223
paul@180 224
        # Produce a reply if configured to do so.
paul@55 225
paul@180 226
        preferences = Preferences(get_uri(self.recipient))
paul@180 227
        if preferences.get("freebusy_sharing") == "share":
paul@180 228
            return CommonFreebusy.request(self)
paul@55 229
paul@55 230
# Handler registry.
paul@55 231
paul@55 232
handlers = [
paul@55 233
    ("VFREEBUSY",   Freebusy),
paul@55 234
    ("VEVENT",      Event),
paul@55 235
    ]
paul@55 236
paul@55 237
# vim: tabstop=4 expandtab shiftwidth=4