1 #!/usr/bin/env python 2 3 """ 4 Handlers for a resource. 5 """ 6 7 from imiptools.content import Handler, format_datetime, to_part 8 from imiptools.period import have_conflict, insert_period, remove_period 9 from vCalendar import to_node 10 11 class Event(Handler): 12 13 "An event handler." 14 15 def add(self): 16 pass 17 18 def cancel(self): 19 pass 20 21 def counter(self): 22 23 "Since this handler does not send requests, it will not handle replies." 24 25 pass 26 27 def declinecounter(self): 28 29 """ 30 Since this handler does not send counter proposals, it will not handle 31 replies to such proposals. 32 """ 33 34 pass 35 36 def publish(self): 37 pass 38 39 def refresh(self): 40 pass 41 42 def reply(self): 43 44 "Since this handler does not send requests, it will not handle replies." 45 46 pass 47 48 def request(self): 49 50 """ 51 Respond to a request by preparing a reply containing accept/decline 52 information for each indicated attendee. 53 54 No support for countering requests is implemented. 55 """ 56 57 oa = self.require_organiser_and_attendees() 58 if not oa: 59 return None 60 61 (organiser, organiser_attr), attendees = oa 62 63 # Process each attendee separately. 64 65 calendar = [] 66 67 for attendee, attendee_attr in attendees.items(): 68 69 # Check for event using UID. 70 71 if not self.have_new_object(attendee, "VEVENT"): 72 continue 73 74 # If newer than any old version, discard old details from the 75 # free/busy record and check for suitability. 76 77 periods = self.get_periods() 78 79 conflict = False 80 freebusy = self.store.get_freebusy(attendee) or [] 81 82 if freebusy: 83 remove_period(freebusy, self.uid) 84 conflict = have_conflict(freebusy, periods) 85 86 # If the event can be scheduled, it is registered and a reply sent 87 # accepting the event. (The attendee has PARTSTAT=ACCEPTED as an 88 # attribute.) 89 90 if not conflict: 91 for start, end in periods: 92 insert_period(freebusy, (start, end, self.uid)) 93 94 if self.get_value("TRANSP") in (None, "OPAQUE"): 95 self.store.set_freebusy(attendee, freebusy) 96 97 if self.publisher: 98 self.publisher.set_freebusy(attendee, freebusy) 99 100 self.store.set_event(attendee, self.uid, to_node( 101 {"VEVENT" : [(self.details, {})]} 102 )) 103 attendee_attr["PARTSTAT"] = "ACCEPTED" 104 105 # If the event cannot be scheduled, it is not registered and a reply 106 # sent declining the event. (The attendee has PARTSTAT=DECLINED as an 107 # attribute.) 108 109 else: 110 attendee_attr["PARTSTAT"] = "DECLINED" 111 112 self.details["ATTENDEE"] = [(attendee, attendee_attr)] 113 calendar.append(to_node( 114 {"VEVENT" : [(self.details, {})]} 115 )) 116 117 return "REPLY", to_part("REPLY", calendar) 118 119 class Freebusy(Handler): 120 121 "A free/busy handler." 122 123 def publish(self): 124 pass 125 126 def reply(self): 127 128 "Since this handler does not send requests, it will not handle replies." 129 130 pass 131 132 def request(self): 133 134 """ 135 Respond to a request by preparing a reply containing free/busy 136 information for each indicated attendee. 137 """ 138 139 oa = self.require_organiser_and_attendees() 140 if not oa: 141 return None 142 143 (organiser, organiser_attr), attendees = oa 144 145 # Construct an appropriate fragment. 146 147 calendar = [] 148 cwrite = calendar.append 149 150 # Get the details for each attendee. 151 152 for attendee, attendee_attr in attendees.items(): 153 freebusy = self.store.get_freebusy(attendee) 154 155 record = [] 156 rwrite = record.append 157 158 rwrite(("ORGANIZER", organiser_attr, organiser)) 159 rwrite(("ATTENDEE", attendee_attr, attendee)) 160 rwrite(("UID", {}, self.uid)) 161 162 if freebusy: 163 for start, end, uid in freebusy: 164 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, [start, end])) 165 166 cwrite(("VFREEBUSY", {}, record)) 167 168 # Return the reply. 169 170 return "REPLY", to_part("REPLY", calendar) 171 172 class Journal(Handler): 173 174 "A journal entry handler." 175 176 def add(self): 177 pass 178 179 def cancel(self): 180 pass 181 182 def publish(self): 183 pass 184 185 class Todo(Handler): 186 187 "A to-do item handler." 188 189 def add(self): 190 pass 191 192 def cancel(self): 193 pass 194 195 def counter(self): 196 197 "Since this handler does not send requests, it will not handle replies." 198 199 pass 200 201 def declinecounter(self): 202 203 """ 204 Since this handler does not send counter proposals, it will not handle 205 replies to such proposals. 206 """ 207 208 pass 209 210 def publish(self): 211 pass 212 213 def refresh(self): 214 pass 215 216 def reply(self): 217 218 "Since this handler does not send requests, it will not handle replies." 219 220 pass 221 222 def request(self): 223 pass 224 225 # Handler registry. 226 227 handlers = [ 228 ("VFREEBUSY", Freebusy), 229 ("VEVENT", Event), 230 ("VTODO", Todo), 231 ("VJOURNAL", Journal), 232 ] 233 234 # vim: tabstop=4 expandtab shiftwidth=4