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