imip-agent

Annotated imip_store.py

66:4e8cbf5e400f
2014-10-25 Paul Boddie Prevented request duplication in the queue; added more comprehensive methods.
paul@2 1
#!/usr/bin/env python
paul@2 2
paul@30 3
from datetime import datetime
paul@15 4
from os.path import abspath, commonprefix, exists, join, split
paul@2 5
from os import makedirs
paul@15 6
from vCalendar import iterwrite
paul@2 7
paul@62 8
# The location of the stored calendar information.
paul@62 9
paul@15 10
STORE_DIR = "/var/lib/imip-agent/store"
paul@62 11
paul@62 12
# The location of published static free/busy information.
paul@62 13
paul@62 14
PUBLISH_DIR = "/var/www/imip-agent/static"
paul@2 15
paul@2 16
def check_dir(base, dir):
paul@2 17
    return commonprefix([base, abspath(dir)]) == base
paul@2 18
paul@30 19
def make_calendar(fragment, method=None):
paul@30 20
    return ("VCALENDAR", {},
paul@30 21
            (method and [("METHOD", {}, method)] or []) +
paul@30 22
            [("VERSION", {}, "2.0")] +
paul@30 23
            fragment
paul@30 24
           )
paul@30 25
paul@26 26
def to_stream(out, fragment, encoding="utf-8"):
paul@26 27
    iterwrite(out, encoding=encoding).append(fragment)
paul@15 28
paul@30 29
class FileBase:
paul@2 30
paul@30 31
    "Basic filesystem operations."
paul@2 32
paul@30 33
    def __init__(self, store_dir=STORE_DIR):
paul@30 34
        self.store_dir = store_dir
paul@2 35
        if not exists(self.store_dir):
paul@2 36
            makedirs(self.store_dir)
paul@2 37
paul@15 38
    def get_file_object(self, base, *parts):
paul@15 39
        pathname = join(base, *parts)
paul@15 40
        return check_dir(base, pathname) and pathname or None
paul@2 41
paul@52 42
    def get_object_in_store(self, *parts):
paul@52 43
paul@52 44
        """
paul@52 45
        Return the name of any valid object stored within a hierarchy specified
paul@52 46
        by the given 'parts'.
paul@52 47
        """
paul@52 48
paul@52 49
        parent = expected = self.store_dir
paul@15 50
paul@52 51
        for part in parts:
paul@52 52
            filename = self.get_file_object(expected, part)
paul@52 53
            if not filename:
paul@52 54
                return False
paul@52 55
            parent = expected
paul@52 56
            expected = filename
paul@15 57
paul@52 58
        if not exists(parent):
paul@52 59
            makedirs(parent)
paul@15 60
paul@50 61
        return filename
paul@50 62
paul@50 63
class FileStore(FileBase):
paul@50 64
paul@50 65
    "A file store of tabular free/busy data and objects."
paul@50 66
paul@50 67
    def get_event(self, user, uid):
paul@50 68
paul@50 69
        "Get the event for the given 'user' with the given 'uid'."
paul@50 70
paul@52 71
        filename = self.get_object_in_store(user, uid)
paul@50 72
        if not filename or not exists(filename):
paul@50 73
            return None
paul@50 74
paul@50 75
        return exists(filename) and open(filename) or None
paul@50 76
paul@50 77
    def set_event(self, user, uid, node):
paul@50 78
paul@50 79
        "Set an event for 'user' having the given 'uid' and 'node'."
paul@50 80
paul@52 81
        filename = self.get_object_in_store(user, uid)
paul@50 82
        if not filename:
paul@50 83
            return False
paul@50 84
paul@15 85
        f = open(filename, "w")
paul@15 86
        try:
paul@26 87
            to_stream(f, node)
paul@15 88
        finally:
paul@15 89
            f.close()
paul@15 90
paul@15 91
        return True
paul@15 92
paul@15 93
    def get_freebusy(self, user):
paul@15 94
paul@15 95
        "Get free/busy details for the given 'user'."
paul@15 96
paul@52 97
        filename = self.get_object_in_store(user, "freebusy")
paul@15 98
        if not filename or not exists(filename):
paul@2 99
            return None
paul@2 100
paul@2 101
        f = open(filename)
paul@2 102
        try:
paul@2 103
            l = []
paul@2 104
            for line in f.readlines():
paul@17 105
                l.append(tuple(line.strip().split("\t")))
paul@2 106
            return l
paul@2 107
        finally:
paul@2 108
            f.close()
paul@2 109
paul@15 110
    def set_freebusy(self, user, freebusy):
paul@15 111
paul@15 112
        "For the given 'user', set 'freebusy' details."
paul@15 113
paul@52 114
        filename = self.get_object_in_store(user, "freebusy")
paul@15 115
        if not filename:
paul@15 116
            return False
paul@15 117
paul@15 118
        f = open(filename, "w")
paul@15 119
        try:
paul@15 120
            for item in freebusy:
paul@15 121
                f.write("\t".join(item) + "\n")
paul@15 122
        finally:
paul@15 123
            f.close()
paul@15 124
paul@15 125
        return True
paul@15 126
paul@66 127
    def get_requests(self, user):
paul@66 128
paul@66 129
        "Get requests for the given 'user'."
paul@66 130
paul@66 131
        filename = self.get_object_in_store(user, "requests")
paul@66 132
        if not filename:
paul@66 133
            return None
paul@66 134
paul@66 135
        f = open(filename)
paul@66 136
        try:
paul@66 137
            return [line.strip() for line in f.readlines()]
paul@66 138
        finally:
paul@66 139
            f.close()
paul@66 140
paul@66 141
    def set_requests(self, user, requests):
paul@55 142
paul@66 143
        "For the given 'user', set the list of queued 'requests'."
paul@66 144
paul@66 145
        filename = self.get_object_in_store(user, "requests")
paul@66 146
        if not filename:
paul@66 147
            return False
paul@66 148
paul@66 149
        f = open(filename, "w")
paul@66 150
        try:
paul@66 151
            for request in requests:
paul@66 152
                print >>f, request
paul@66 153
        finally:
paul@66 154
            f.close()
paul@66 155
paul@66 156
        return True
paul@66 157
paul@66 158
    def set_request(self, user, request):
paul@66 159
paul@66 160
        "For the given 'user', set the queued 'request'."
paul@55 161
paul@55 162
        filename = self.get_object_in_store(user, "requests")
paul@55 163
        if not filename:
paul@55 164
            return False
paul@55 165
paul@55 166
        f = open(filename, "a")
paul@55 167
        try:
paul@66 168
            print >>f, request
paul@55 169
        finally:
paul@55 170
            f.close()
paul@55 171
paul@55 172
        return True
paul@55 173
paul@66 174
    def queue_request(self, user, uid):
paul@66 175
paul@66 176
        "Queue a request for 'user' having the given 'uid'."
paul@66 177
paul@66 178
        requests = self.get_requests(user)
paul@66 179
paul@66 180
        if uid not in requests:
paul@66 181
            return self.set_request(user, uid)
paul@66 182
paul@66 183
        return False
paul@66 184
paul@30 185
class FilePublisher(FileBase):
paul@30 186
paul@30 187
    "A publisher of objects."
paul@30 188
paul@30 189
    def __init__(self, store_dir=PUBLISH_DIR):
paul@30 190
        FileBase.__init__(self, store_dir)
paul@30 191
paul@30 192
    def set_freebusy(self, user, freebusy):
paul@30 193
paul@30 194
        "For the given 'user', set 'freebusy' details."
paul@30 195
paul@52 196
        filename = self.get_object_in_store(user, "freebusy")
paul@30 197
        if not filename:
paul@30 198
            return False
paul@30 199
paul@30 200
        record = []
paul@30 201
        rwrite = record.append
paul@30 202
paul@30 203
        rwrite(("ORGANIZER", {}, user))
paul@30 204
        rwrite(("UID", {}, user))
paul@30 205
        rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))
paul@30 206
paul@30 207
        for start, end, uid in freebusy:
paul@30 208
            rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
paul@30 209
paul@30 210
        f = open(filename, "w")
paul@30 211
        try:
paul@30 212
            to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH"))
paul@30 213
        finally:
paul@30 214
            f.close()
paul@30 215
paul@30 216
        return True
paul@30 217
paul@2 218
# vim: tabstop=4 expandtab shiftwidth=4