imip-agent

Annotated imiptools/handlers/person.py

70:e6f5ff5559db
2014-10-26 Paul Boddie Added support for showing received requests in the manager interface, with the address of each request being included in the message delivered to the recipient.
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@55 5
"""
paul@55 6
paul@60 7
from email.mime.text import MIMEText
paul@70 8
from imiptools.config import MANAGER_PATH, MANAGER_URL
paul@60 9
from imiptools.content import Handler, to_part
paul@70 10
from socket import gethostname
paul@55 11
from vCalendar import to_node
paul@55 12
paul@70 13
def get_manager_url():
paul@70 14
    url_base = MANAGER_URL or "http://%s/" % gethostname()
paul@70 15
    return "%s/%s" % (url_base.rstrip("/"), MANAGER_PATH.lstrip("/"))
paul@70 16
paul@67 17
class PersonHandler(Handler):
paul@55 18
paul@67 19
    "Handling mechanisms specific to people."
paul@55 20
paul@61 21
    def _record_and_deliver(self, queue=False):
paul@55 22
paul@55 23
        oa = self.require_organiser_and_attendees()
paul@55 24
        if not oa:
paul@61 25
            return False
paul@55 26
paul@55 27
        (organiser, organiser_attr), attendees = oa
paul@55 28
paul@55 29
        # Process each attendee separately.
paul@55 30
paul@55 31
        for attendee, attendee_attr in attendees.items():
paul@55 32
paul@55 33
            if not self.have_new_object(attendee, "VEVENT"):
paul@55 34
                continue
paul@55 35
paul@61 36
            # Store the event and queue any request.
paul@55 37
paul@55 38
            self.store.set_event(attendee, self.uid, to_node(
paul@55 39
                {"VEVENT" : [(self.details, {})]}
paul@55 40
                ))
paul@55 41
paul@61 42
            if queue:
paul@61 43
                self.store.queue_request(attendee, self.uid)
paul@61 44
paul@61 45
        return True
paul@61 46
paul@67 47
class Event(PersonHandler):
paul@67 48
paul@67 49
    "An event handler."
paul@67 50
paul@63 51
    def add(self):
paul@63 52
paul@63 53
        # NOTE: Queue a suggested modification to any active event.
paul@63 54
paul@63 55
        # The message is now wrapped and passed on to the recipient.
paul@63 56
paul@63 57
        return "ADD", MIMEText("An addition to an event has been received.")
paul@63 58
paul@63 59
    def cancel(self):
paul@63 60
paul@63 61
        # NOTE: Queue a suggested modification to any active event.
paul@63 62
paul@63 63
        # The message is now wrapped and passed on to the recipient.
paul@63 64
paul@63 65
        return "CANCEL", MIMEText("A cancellation has been received.")
paul@63 66
paul@63 67
    def counter(self):
paul@63 68
paul@63 69
        # NOTE: Queue a suggested modification to any active event.
paul@63 70
paul@63 71
        # The message is now wrapped and passed on to the recipient.
paul@63 72
paul@63 73
        return "COUNTER", MIMEText("A counter proposal has been received.")
paul@63 74
paul@63 75
    def declinecounter(self):
paul@63 76
paul@63 77
        # NOTE: Queue a suggested modification to any active event.
paul@63 78
paul@63 79
        # The message is now wrapped and passed on to the recipient.
paul@63 80
paul@63 81
        return "DECLINECOUNTER", MIMEText("A declining counter proposal has been received.")
paul@63 82
paul@63 83
    def publish(self):
paul@63 84
paul@63 85
        # NOTE: Register details of any relevant event.
paul@63 86
paul@63 87
        # The message is now wrapped and passed on to the recipient.
paul@63 88
paul@63 89
        return "PUBLISH", MIMEText("Details of an event have been received.")
paul@63 90
paul@63 91
    def refresh(self):
paul@63 92
paul@63 93
        # NOTE: Update details of any active event.
paul@63 94
paul@63 95
        # The message is now wrapped and passed on to the recipient.
paul@63 96
paul@63 97
        return "REFRESH", MIMEText("An event update has been received.")
paul@63 98
paul@61 99
    def reply(self):
paul@61 100
paul@61 101
        "Record replies and notify the recipient."
paul@61 102
paul@61 103
        self._record_and_deliver(False)
paul@61 104
paul@61 105
        # The message is now wrapped and passed on to the recipient.
paul@61 106
paul@61 107
        return "REPLY", MIMEText("A reply has been received.")
paul@61 108
paul@61 109
    def request(self):
paul@61 110
paul@61 111
        "Hold requests and notify the recipient."
paul@61 112
paul@61 113
        self._record_and_deliver(True)
paul@55 114
paul@55 115
        # The message is now wrapped and passed on to the recipient.
paul@55 116
paul@70 117
        url = "%s/%s" % (get_manager_url().rstrip("/"), self.uid)
paul@70 118
        return "REQUEST", MIMEText("A request has been queued and can be viewed here: %s" % url)
paul@60 119
paul@67 120
class Freebusy(PersonHandler):
paul@55 121
paul@55 122
    "A free/busy handler."
paul@55 123
paul@55 124
    def publish(self):
paul@63 125
paul@63 126
        # NOTE: Register free/busy information.
paul@63 127
paul@63 128
        # The message is now wrapped and passed on to the recipient.
paul@63 129
paul@63 130
        return "PUBLISH", MIMEText("Details of a contact's availability have been received.")
paul@55 131
paul@55 132
    def reply(self):
paul@55 133
paul@63 134
        "Record replies and notify the recipient."
paul@63 135
paul@63 136
        self._record_and_deliver(False)
paul@55 137
paul@63 138
        # The message is now wrapped and passed on to the recipient.
paul@63 139
paul@63 140
        return "REPLY", MIMEText("A reply has been received.")
paul@55 141
paul@55 142
    def request(self):
paul@55 143
paul@55 144
        """
paul@55 145
        Respond to a request by preparing a reply containing free/busy
paul@55 146
        information for each indicated attendee.
paul@55 147
        """
paul@55 148
paul@55 149
        # NOTE: This is currently the same as the resource handler but should be
paul@55 150
        # NOTE: subject to policy/preferences.
paul@55 151
paul@55 152
        oa = self.require_organiser_and_attendees()
paul@55 153
        if not oa:
paul@55 154
            return None
paul@55 155
paul@55 156
        (organiser, organiser_attr), attendees = oa
paul@55 157
paul@55 158
        # Construct an appropriate fragment.
paul@55 159
paul@55 160
        calendar = []
paul@55 161
        cwrite = calendar.append
paul@55 162
paul@55 163
        # Get the details for each attendee.
paul@55 164
paul@55 165
        for attendee, attendee_attr in attendees.items():
paul@55 166
            freebusy = self.store.get_freebusy(attendee)
paul@55 167
paul@60 168
            record = []
paul@60 169
            rwrite = record.append
paul@55 170
paul@60 171
            rwrite(("ORGANIZER", organiser_attr, organiser))
paul@60 172
            rwrite(("ATTENDEE", attendee_attr, attendee))
paul@60 173
            rwrite(("UID", {}, self.uid))
paul@55 174
paul@60 175
            if freebusy:
paul@55 176
                for start, end, uid in freebusy:
paul@55 177
                    rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, [start, end]))
paul@55 178
paul@60 179
            cwrite(("VFREEBUSY", {}, record))
paul@55 180
paul@55 181
        # Return the reply.
paul@55 182
paul@60 183
        return "REPLY", to_part("REPLY", calendar)
paul@55 184
paul@67 185
class Journal(PersonHandler):
paul@55 186
paul@55 187
    "A journal entry handler."
paul@55 188
paul@55 189
    def add(self):
paul@63 190
paul@63 191
        # NOTE: Queue a suggested modification to any active entry.
paul@63 192
paul@63 193
        # The message is now wrapped and passed on to the recipient.
paul@63 194
paul@63 195
        return "ADD", MIMEText("An addition to a journal entry has been received.")
paul@55 196
paul@55 197
    def cancel(self):
paul@63 198
paul@63 199
        # NOTE: Queue a suggested modification to any active entry.
paul@63 200
paul@63 201
        # The message is now wrapped and passed on to the recipient.
paul@63 202
paul@63 203
        return "CANCEL", MIMEText("A cancellation has been received.")
paul@55 204
paul@55 205
    def publish(self):
paul@63 206
paul@63 207
        # NOTE: Register details of any relevant entry.
paul@63 208
paul@63 209
        # The message is now wrapped and passed on to the recipient.
paul@63 210
paul@63 211
        return "PUBLISH", MIMEText("Details of a journal entry have been received.")
paul@55 212
paul@67 213
class Todo(PersonHandler):
paul@55 214
paul@55 215
    "A to-do item handler."
paul@55 216
paul@55 217
    def add(self):
paul@63 218
paul@63 219
        # NOTE: Queue a suggested modification to any active item.
paul@63 220
paul@63 221
        # The message is now wrapped and passed on to the recipient.
paul@63 222
paul@63 223
        return "ADD", MIMEText("An addition to an item has been received.")
paul@55 224
paul@55 225
    def cancel(self):
paul@63 226
paul@63 227
        # NOTE: Queue a suggested modification to any active item.
paul@63 228
paul@63 229
        # The message is now wrapped and passed on to the recipient.
paul@63 230
paul@63 231
        return "CANCEL", MIMEText("A cancellation has been received.")
paul@55 232
paul@55 233
    def counter(self):
paul@55 234
paul@63 235
        # NOTE: Queue a suggested modification to any active item.
paul@55 236
paul@63 237
        # The message is now wrapped and passed on to the recipient.
paul@63 238
paul@63 239
        return "COUNTER", MIMEText("A counter proposal has been received.")
paul@55 240
paul@55 241
    def declinecounter(self):
paul@55 242
paul@63 243
        # NOTE: Queue a suggested modification to any active item.
paul@55 244
paul@63 245
        # The message is now wrapped and passed on to the recipient.
paul@63 246
paul@63 247
        return "DECLINECOUNTER", MIMEText("A declining counter proposal has been received.")
paul@55 248
paul@55 249
    def publish(self):
paul@63 250
paul@63 251
        # NOTE: Register details of any relevant item.
paul@63 252
paul@63 253
        # The message is now wrapped and passed on to the recipient.
paul@63 254
paul@63 255
        return "PUBLISH", MIMEText("Details of an item have been received.")
paul@55 256
paul@55 257
    def refresh(self):
paul@63 258
paul@63 259
        # NOTE: Update details of any active item.
paul@63 260
paul@63 261
        # The message is now wrapped and passed on to the recipient.
paul@63 262
paul@63 263
        return "REFRESH", MIMEText("An item update has been received.")
paul@55 264
paul@55 265
    def reply(self):
paul@55 266
paul@63 267
        "Record replies and notify the recipient."
paul@63 268
paul@63 269
        self._record_and_deliver(False)
paul@55 270
paul@63 271
        # The message is now wrapped and passed on to the recipient.
paul@63 272
paul@63 273
        return "REPLY", MIMEText("A reply has been received.")
paul@55 274
paul@55 275
    def request(self):
paul@63 276
paul@63 277
        "Hold requests and notify the recipient."
paul@63 278
paul@63 279
        self._record_and_deliver(True)
paul@63 280
paul@63 281
        # The message is now wrapped and passed on to the recipient.
paul@63 282
paul@63 283
        return "REQUEST", MIMEText("A request has been queued.")
paul@55 284
paul@55 285
# Handler registry.
paul@55 286
paul@55 287
handlers = [
paul@55 288
    ("VFREEBUSY",   Freebusy),
paul@55 289
    ("VEVENT",      Event),
paul@55 290
    ("VTODO",       Todo),
paul@55 291
    ("VJOURNAL",    Journal),
paul@55 292
    ]
paul@55 293
paul@55 294
# vim: tabstop=4 expandtab shiftwidth=4