# HG changeset patch # User Paul Boddie # Date 1522337413 -7200 # Node ID f3e78bbc8fb8df3a7b87a9faaf9e5dbfb9ca67a9 # Parent 1ade59d71cb982187afc2af1f6f70f1421bad31a Introduced initial support for generating free/busy requests from objects. diff -r 1ade59d71cb9 -r f3e78bbc8fb8 imip_text_client.py --- a/imip_text_client.py Fri Mar 23 15:46:51 2018 +0100 +++ b/imip_text_client.py Thu Mar 29 17:30:13 2018 +0200 @@ -64,6 +64,7 @@ CANCEL_COMMANDS = ("R", "remove", "cancel") CANCEL_PUBLISH_COMMANDS = ("RP", "remove-publish", "cancel-publish") +FREEBUSY_COMMANDS = ("F", "fb", "freebusy", "free/busy", "free-busy") PUBLISH_COMMANDS = ("P", "publish") SEND_COMMANDS = ("S", "send") UPDATE_COMMANDS = ("U", "update") @@ -781,6 +782,13 @@ if message: self.show_message(message, plain, filename) + def show_freebusy_message(self, plain=False, filename=None): + + "Show a free/busy request message for the main period." + + message = self.prepare_freebusy_message() + self.show_message(message, plain, filename) + def show_cancel_publish_message(self, plain=False, filename=None): "Show the cancel message for the current user." @@ -1282,26 +1290,30 @@ print_title("Message inspection commands") print print """\ -P [ ] +%(FREEBUSY_COMMANDS)s + Show free/busy request message, writing to if specified + +%(PUBLISH_COMMANDS)s publish [ ] Show publishing message, writing to if specified -R [ ] -remove [ ] -cancel [ ] +%(CANCEL_COMMANDS)s Show cancellation message sent to uninvited/removed recipients, writing to if specified -RP [ ] -remove-publish [ ] -cancel-publish [ ] +%(CANCEL_PUBLISH_COMMANDS)s Show cancellation message for use by the organiser, writing to if specified -U [ ] -update [ ] +%(UPDATE_COMMANDS)s Show update message, writing to if specified -""" +""" % { + "CANCEL_COMMANDS" : commandlist(CANCEL_COMMANDS, "[ ]"), + "CANCEL_PUBLISH_COMMANDS" : commandlist(CANCEL_PUBLISH_COMMANDS, "[ ]"), + "FREEBUSY_COMMANDS" : commandlist(FREEBUSY_COMMANDS, "[ ]"), + "PUBLISH_COMMANDS" : commandlist(PUBLISH_COMMANDS, "[ ]"), + "UPDATE_COMMANDS" : commandlist(UPDATE_COMMANDS, "[ ]"), + } def edit_object(cl, obj, handle_outgoing=False): @@ -1409,6 +1421,10 @@ filename = get_text_arg(s) cl.show_update_message(plain=not filename, filename=filename) + elif cmd in FREEBUSY_COMMANDS: + filename = get_text_arg(s) + cl.show_freebusy_message(plain=not filename, filename=filename) + # Definitive finishing action. elif cmd in SEND_COMMANDS: diff -r 1ade59d71cb9 -r f3e78bbc8fb8 imiptools/client.py --- a/imiptools/client.py Fri Mar 23 15:46:51 2018 +0100 +++ b/imiptools/client.py Thu Mar 29 17:30:13 2018 +0200 @@ -1047,6 +1047,24 @@ parts = [self.object_to_part("CANCEL", self.obj)] return self.make_message_for_self(parts) + def make_freebusy_message(self): + + "Prepare a free/busy request for the main period." + + user_attr = {} + self.update_sender_attr(user_attr) + + attributes = self.obj.get_items("ATTENDEE") + period = self.obj.get_main_period() + + recipients = self.get_recipients() + + # NOTE: Should choose a different UID to any event. + + fb = make_freebusy(None, self.uid, self.user, user_attr, attributes, period) + parts = [self.to_part("REQUEST", [fb])] + return self.make_message(parts, recipients) + # Action methods. def send_declined_counter_to_attendee(self, attendee): diff -r 1ade59d71cb9 -r f3e78bbc8fb8 imiptools/data.py --- a/imiptools/data.py Fri Mar 23 15:46:51 2018 +0100 +++ b/imiptools/data.py Thu Mar 29 17:30:13 2018 +0200 @@ -770,14 +770,14 @@ nodes ) -def make_freebusy(freebusy, uid, organiser, organiser_attr=None, attendee=None, - attendee_attr=None, period=None): +def make_freebusy(freebusy, uid, organiser, organiser_attr=None, attendees=None, + period=None): """ Return a calendar node defining the free/busy details described in the given 'freebusy' list, employing the given 'uid', for the given 'organiser' and - optional 'organiser_attr', with the optional 'attendee' providing recipient - details together with the optional 'attendee_attr'. + optional 'organiser_attr', with the optional 'attendees' providing a + collection of (address, attribute) items. The result will be constrained to the 'period' if specified. """ @@ -787,12 +787,17 @@ rwrite(("ORGANIZER", organiser_attr or {}, organiser)) - if attendee: - rwrite(("ATTENDEE", attendee_attr or {}, attendee)) + # A request can have many attendees; a response will have just one. + + if attendees: + for attendee, attendee_attr in attendees: + rwrite(("ATTENDEE", attendee_attr or {}, attendee)) rwrite(("UID", {}, uid)) - if freebusy: + # Obtain free/busy periods if publishing or replying. + + if freebusy is not None: # Get a constrained view if start and end limits are specified. @@ -801,20 +806,23 @@ else: periods = freebusy - # Write the limits of the resource. + else: + periods = [] + + # Write the limits of the resource. - if periods: - rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, format_datetime(periods[0].get_start_point()))) - rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, format_datetime(periods[-1].get_end_point()))) - else: - rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, format_datetime(period.get_start_point()))) - rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, format_datetime(period.get_end_point()))) + if periods: + rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, format_datetime(periods[0].get_start_point()))) + rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, format_datetime(periods[-1].get_end_point()))) + elif period: + rwrite(("DTSTART", {"VALUE" : "DATE-TIME"}, format_datetime(period.get_start_point()))) + rwrite(("DTEND", {"VALUE" : "DATE-TIME"}, format_datetime(period.get_end_point()))) - for p in periods: - if p.transp == "OPAQUE": - rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join( - map(format_datetime, [p.get_start_point(), p.get_end_point()]) - ))) + for p in periods: + if p.transp == "OPAQUE": + rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join( + map(format_datetime, [p.get_start_point(), p.get_end_point()]) + ))) return ("VFREEBUSY", {}, record) diff -r 1ade59d71cb9 -r f3e78bbc8fb8 imiptools/editing.py --- a/imiptools/editing.py Fri Mar 23 15:46:51 2018 +0100 +++ b/imiptools/editing.py Thu Mar 29 17:30:13 2018 +0200 @@ -521,6 +521,12 @@ return self.make_cancel_message_for_self() + def prepare_freebusy_message(self): + + "Prepare a free/busy request message for the main period." + + return self.make_freebusy_message() + def prepare_publish_message(self): "Prepare the publishing message for the updated event." diff -r 1ade59d71cb9 -r f3e78bbc8fb8 imiptools/handlers/common.py --- a/imiptools/handlers/common.py Fri Mar 23 15:46:51 2018 +0100 +++ b/imiptools/handlers/common.py Thu Mar 29 17:30:13 2018 +0200 @@ -75,6 +75,12 @@ (organiser, organiser_attr), attendees = oa + # Get the period involved. + + dtstart = self.obj.get_datetime("DTSTART") + dtend = self.obj.get_datetime("DTEND") + period = dtstart and dtend and Period(dtstart, dtend, self.get_tzid()) or None + # Get the details for each attendee. responses = [] @@ -89,11 +95,10 @@ self.update_sender_attr(attendee_attr) - dtstart = self.obj.get_datetime("DTSTART") - dtend = self.obj.get_datetime("DTEND") - period = dtstart and dtend and Period(dtstart, dtend, self.get_tzid()) or None + # Produce a free/busy reply. - rwrite(make_freebusy(freebusy, self.uid, organiser, organiser_attr, attendee, attendee_attr, period)) + rwrite(make_freebusy(freebusy, self.uid, organiser, organiser_attr, + [(attendee, attendee_attr)], period)) # Return the reply.