imip-agent

Annotated imiptools/handlers/common.py

1062:2264ab469f6d
2016-03-02 Paul Boddie Introduced a free/busy collection abstraction for potential access and representation efficiency improvements. freebusy-collections
paul@108 1
#!/usr/bin/env python
paul@108 2
paul@108 3
"""
paul@108 4
Common handler functionality for different entities.
paul@146 5
paul@1062 6
Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
paul@146 7
paul@146 8
This program is free software; you can redistribute it and/or modify it under
paul@146 9
the terms of the GNU General Public License as published by the Free Software
paul@146 10
Foundation; either version 3 of the License, or (at your option) any later
paul@146 11
version.
paul@146 12
paul@146 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@146 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@146 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@146 16
details.
paul@146 17
paul@146 18
You should have received a copy of the GNU General Public License along with
paul@146 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@108 20
"""
paul@108 21
paul@580 22
from imiptools.data import get_address, get_uri, make_freebusy, to_part, \
paul@580 23
                           uri_dict
paul@327 24
from imiptools.dates import format_datetime
paul@1062 25
from imiptools.period import FreeBusyPeriod, Period
paul@108 26
paul@222 27
class CommonFreebusy:
paul@108 28
paul@222 29
    "Common free/busy mix-in."
paul@181 30
paul@943 31
    def _record_freebusy(self, from_organiser=True):
paul@943 32
paul@943 33
        """
paul@943 34
        Record free/busy information for a message originating from an organiser
paul@943 35
        if 'from_organiser' is set to a true value.
paul@943 36
        """
paul@943 37
paul@943 38
        if from_organiser:
paul@943 39
            organiser_item = self.require_organiser(from_organiser)
paul@943 40
            if not organiser_item:
paul@943 41
                return
paul@943 42
paul@943 43
            senders = [organiser_item]
paul@943 44
        else:
paul@943 45
            oa = self.require_organiser_and_attendees(from_organiser)
paul@943 46
            if not oa:
paul@943 47
                return
paul@943 48
paul@943 49
            organiser_item, attendees = oa
paul@943 50
            senders = attendees.items()
paul@943 51
paul@943 52
            if not senders:
paul@943 53
                return
paul@943 54
paul@943 55
        freebusy = [FreeBusyPeriod(p.get_start_point(), p.get_end_point()) for p in self.obj.get_period_values("FREEBUSY")]
paul@943 56
        dtstart = self.obj.get_datetime("DTSTART")
paul@943 57
        dtend = self.obj.get_datetime("DTEND")
paul@943 58
        period = Period(dtstart, dtend, self.get_tzid())
paul@943 59
paul@943 60
        for sender, sender_attr in senders:
paul@943 61
            stored_freebusy = self.store.get_freebusy_for_other(self.user, sender)
paul@1062 62
            stored_freebusy.replace_overlapping(period, freebusy)
paul@943 63
            self.store.set_freebusy_for_other(self.user, stored_freebusy, sender)
paul@943 64
paul@222 65
    def request(self):
paul@181 66
paul@181 67
        """
paul@222 68
        Respond to a request by preparing a reply containing free/busy
paul@222 69
        information for each indicated attendee.
paul@181 70
        """
paul@181 71
paul@108 72
        oa = self.require_organiser_and_attendees()
paul@108 73
        if not oa:
paul@228 74
            return
paul@108 75
paul@181 76
        (organiser, organiser_attr), attendees = oa
paul@108 77
paul@181 78
        # Get the details for each attendee.
paul@108 79
paul@222 80
        responses = []
paul@222 81
        rwrite = responses.append
paul@222 82
paul@222 83
        # For replies, the organiser and attendee are preserved.
paul@108 84
paul@181 85
        for attendee, attendee_attr in attendees.items():
paul@222 86
            freebusy = self.store.get_freebusy(attendee)
paul@292 87
paul@292 88
            # Indicate the actual sender of the reply.
paul@292 89
paul@830 90
            self.update_sender(attendee_attr)
paul@292 91
paul@562 92
            dtstart = self.obj.get_datetime("DTSTART")
paul@562 93
            dtend = self.obj.get_datetime("DTEND")
paul@620 94
            period = dtstart and dtend and Period(dtstart, dtend, self.get_tzid()) or None
paul@327 95
paul@562 96
            rwrite(make_freebusy(freebusy, self.uid, organiser, organiser_attr, attendee, attendee_attr, period))
paul@183 97
paul@183 98
        # Return the reply.
paul@183 99
paul@228 100
        self.add_result("REPLY", [get_address(organiser)], to_part("REPLY", responses))
paul@183 101
paul@683 102
class CommonEvent:
paul@580 103
paul@606 104
    "Common outgoing message handling functionality mix-in."
paul@580 105
paul@727 106
    def is_usable(self, method=None):
paul@727 107
paul@727 108
        "Return whether the current object is usable with the given 'method'."
paul@720 109
paul@727 110
        return self.obj and (
paul@727 111
            method in ("CANCEL", "REFRESH") or
paul@727 112
            self.obj.get_datetime("DTSTART") and
paul@727 113
                (self.obj.get_datetime("DTEND") or self.obj.get_duration("DURATION")))
paul@720 114
paul@737 115
    def will_refresh(self):
paul@737 116
paul@737 117
        """
paul@737 118
        Indicate whether a REFRESH message should be used to respond to an ADD
paul@737 119
        message.
paul@737 120
        """
paul@737 121
paul@737 122
        return not self.get_stored_object_version() or self.get_add_method_response() == "refresh"
paul@737 123
paul@737 124
    def make_refresh(self):
paul@737 125
paul@737 126
        "Make a REFRESH message."
paul@737 127
paul@737 128
        organiser = get_uri(self.obj.get_value("ORGANIZER"))
paul@737 129
        attendees = uri_dict(self.obj.get_value_map("ATTENDEE"))
paul@737 130
paul@830 131
        # Indicate the actual sender of the message.
paul@737 132
paul@737 133
        attendee_attr = attendees[self.user]
paul@737 134
        self.update_sender(attendee_attr)
paul@737 135
paul@737 136
        # Make a new object with a minimal property selection.
paul@737 137
paul@737 138
        obj = self.obj.copy()
paul@737 139
        obj.preserve(("ORGANIZER", "DTSTAMP", "UID", "RECURRENCE-ID"))
paul@737 140
        obj["ATTENDEE"] = [(self.user, attendee_attr)]
paul@737 141
paul@737 142
        # Send a REFRESH message in response.
paul@737 143
paul@737 144
        self.add_result("REFRESH", [get_address(organiser)], obj.to_part("REFRESH"))
paul@737 145
paul@108 146
# vim: tabstop=4 expandtab shiftwidth=4