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