imip-agent

Annotated imiptools/handlers/person.py

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