imip-agent

Annotated imip_store.py

138:d915725f25c0
2014-12-18 Paul Boddie Moved objects into their own subdirectory in each user's store.
paul@2 1
#!/usr/bin/env python
paul@2 2
paul@30 3
from datetime import datetime
paul@68 4
from imiptools.config import STORE_DIR, PUBLISH_DIR
paul@119 5
from os.path import abspath, commonprefix, exists, isfile, join, split
paul@119 6
from os import chmod, listdir, makedirs
paul@15 7
from vCalendar import iterwrite
paul@2 8
paul@2 9
def check_dir(base, dir):
paul@2 10
    return commonprefix([base, abspath(dir)]) == base
paul@2 11
paul@103 12
def fix_permissions(filename):
paul@103 13
    try:
paul@103 14
        chmod(filename, 0660)
paul@103 15
    except OSError:
paul@103 16
        pass
paul@103 17
paul@30 18
def make_calendar(fragment, method=None):
paul@105 19
paul@105 20
    """
paul@105 21
    Return a complete calendar item wrapping the given 'fragment' and employing
paul@105 22
    the given 'method', if indicated.
paul@105 23
    """
paul@105 24
paul@30 25
    return ("VCALENDAR", {},
paul@30 26
            (method and [("METHOD", {}, method)] or []) +
paul@30 27
            [("VERSION", {}, "2.0")] +
paul@30 28
            fragment
paul@30 29
           )
paul@30 30
paul@26 31
def to_stream(out, fragment, encoding="utf-8"):
paul@26 32
    iterwrite(out, encoding=encoding).append(fragment)
paul@15 33
paul@30 34
class FileBase:
paul@2 35
paul@30 36
    "Basic filesystem operations."
paul@2 37
paul@30 38
    def __init__(self, store_dir=STORE_DIR):
paul@30 39
        self.store_dir = store_dir
paul@2 40
        if not exists(self.store_dir):
paul@2 41
            makedirs(self.store_dir)
paul@2 42
paul@15 43
    def get_file_object(self, base, *parts):
paul@15 44
        pathname = join(base, *parts)
paul@15 45
        return check_dir(base, pathname) and pathname or None
paul@2 46
paul@52 47
    def get_object_in_store(self, *parts):
paul@52 48
paul@52 49
        """
paul@52 50
        Return the name of any valid object stored within a hierarchy specified
paul@52 51
        by the given 'parts'.
paul@52 52
        """
paul@52 53
paul@52 54
        parent = expected = self.store_dir
paul@15 55
paul@52 56
        for part in parts:
paul@52 57
            filename = self.get_file_object(expected, part)
paul@52 58
            if not filename:
paul@52 59
                return False
paul@52 60
            parent = expected
paul@52 61
            expected = filename
paul@15 62
paul@52 63
        if not exists(parent):
paul@52 64
            makedirs(parent)
paul@15 65
paul@50 66
        return filename
paul@50 67
paul@50 68
class FileStore(FileBase):
paul@50 69
paul@50 70
    "A file store of tabular free/busy data and objects."
paul@50 71
paul@119 72
    def get_events(self, user):
paul@119 73
paul@119 74
        "Return a list of event identifiers."
paul@119 75
paul@138 76
        filename = self.get_object_in_store(user, "objects")
paul@119 77
        if not filename or not exists(filename):
paul@119 78
            return None
paul@119 79
paul@119 80
        return [name for name in listdir(filename) if isfile(join(filename, name))]
paul@119 81
paul@50 82
    def get_event(self, user, uid):
paul@50 83
paul@50 84
        "Get the event for the given 'user' with the given 'uid'."
paul@50 85
paul@138 86
        filename = self.get_object_in_store(user, "objects", uid)
paul@50 87
        if not filename or not exists(filename):
paul@50 88
            return None
paul@50 89
paul@119 90
        return open(filename) or None
paul@50 91
paul@50 92
    def set_event(self, user, uid, node):
paul@50 93
paul@50 94
        "Set an event for 'user' having the given 'uid' and 'node'."
paul@50 95
paul@138 96
        filename = self.get_object_in_store(user, "objects", uid)
paul@50 97
        if not filename:
paul@50 98
            return False
paul@50 99
paul@15 100
        f = open(filename, "w")
paul@15 101
        try:
paul@26 102
            to_stream(f, node)
paul@15 103
        finally:
paul@15 104
            f.close()
paul@103 105
            fix_permissions(filename)
paul@15 106
paul@15 107
        return True
paul@15 108
paul@15 109
    def get_freebusy(self, user):
paul@15 110
paul@15 111
        "Get free/busy details for the given 'user'."
paul@15 112
paul@52 113
        filename = self.get_object_in_store(user, "freebusy")
paul@15 114
        if not filename or not exists(filename):
paul@2 115
            return None
paul@112 116
        else:
paul@112 117
            return self._get_freebusy(filename)
paul@2 118
paul@112 119
    def get_freebusy_for_other(self, user, other):
paul@112 120
paul@112 121
        "For the given 'user', get free/busy details for the 'other' user."
paul@112 122
paul@112 123
        filename = self.get_object_in_store(user, "freebusy-other", other)
paul@112 124
        if not filename:
paul@112 125
            return None
paul@112 126
        else:
paul@112 127
            return self._get_freebusy(filename)
paul@112 128
paul@112 129
    def _get_freebusy(self, filename):
paul@2 130
        f = open(filename)
paul@2 131
        try:
paul@2 132
            l = []
paul@2 133
            for line in f.readlines():
paul@17 134
                l.append(tuple(line.strip().split("\t")))
paul@2 135
            return l
paul@2 136
        finally:
paul@2 137
            f.close()
paul@2 138
paul@15 139
    def set_freebusy(self, user, freebusy):
paul@15 140
paul@15 141
        "For the given 'user', set 'freebusy' details."
paul@15 142
paul@52 143
        filename = self.get_object_in_store(user, "freebusy")
paul@15 144
        if not filename:
paul@15 145
            return False
paul@15 146
paul@112 147
        self._set_freebusy(filename, freebusy)
paul@15 148
        return True
paul@15 149
paul@110 150
    def set_freebusy_for_other(self, user, freebusy, other):
paul@110 151
paul@110 152
        "For the given 'user', set 'freebusy' details for the 'other' user."
paul@110 153
paul@110 154
        filename = self.get_object_in_store(user, "freebusy-other", other)
paul@110 155
        if not filename:
paul@110 156
            return False
paul@110 157
paul@112 158
        self._set_freebusy(filename, freebusy)
paul@112 159
        return True
paul@112 160
paul@112 161
    def _set_freebusy(self, filename, freebusy):
paul@110 162
        f = open(filename, "w")
paul@110 163
        try:
paul@110 164
            for item in freebusy:
paul@119 165
                f.write("\t".join([(value or "OPAQUE") for value in item]) + "\n")
paul@110 166
        finally:
paul@110 167
            f.close()
paul@110 168
            fix_permissions(filename)
paul@110 169
paul@66 170
    def get_requests(self, user):
paul@66 171
paul@66 172
        "Get requests for the given 'user'."
paul@66 173
paul@66 174
        filename = self.get_object_in_store(user, "requests")
paul@81 175
        if not filename or not exists(filename):
paul@66 176
            return None
paul@66 177
paul@66 178
        f = open(filename)
paul@66 179
        try:
paul@66 180
            return [line.strip() for line in f.readlines()]
paul@66 181
        finally:
paul@66 182
            f.close()
paul@66 183
paul@66 184
    def set_requests(self, user, requests):
paul@55 185
paul@66 186
        "For the given 'user', set the list of queued 'requests'."
paul@66 187
paul@66 188
        filename = self.get_object_in_store(user, "requests")
paul@66 189
        if not filename:
paul@66 190
            return False
paul@66 191
paul@66 192
        f = open(filename, "w")
paul@66 193
        try:
paul@66 194
            for request in requests:
paul@66 195
                print >>f, request
paul@66 196
        finally:
paul@66 197
            f.close()
paul@103 198
            fix_permissions(filename)
paul@66 199
paul@66 200
        return True
paul@66 201
paul@66 202
    def set_request(self, user, request):
paul@66 203
paul@66 204
        "For the given 'user', set the queued 'request'."
paul@55 205
paul@55 206
        filename = self.get_object_in_store(user, "requests")
paul@55 207
        if not filename:
paul@55 208
            return False
paul@55 209
paul@55 210
        f = open(filename, "a")
paul@55 211
        try:
paul@66 212
            print >>f, request
paul@55 213
        finally:
paul@55 214
            f.close()
paul@103 215
            fix_permissions(filename)
paul@55 216
paul@55 217
        return True
paul@55 218
paul@66 219
    def queue_request(self, user, uid):
paul@66 220
paul@66 221
        "Queue a request for 'user' having the given 'uid'."
paul@66 222
paul@81 223
        requests = self.get_requests(user) or []
paul@66 224
paul@66 225
        if uid not in requests:
paul@66 226
            return self.set_request(user, uid)
paul@66 227
paul@66 228
        return False
paul@66 229
paul@105 230
    def dequeue_request(self, user, uid):
paul@105 231
paul@105 232
        "Dequeue a request for 'user' having the given 'uid'."
paul@105 233
paul@105 234
        requests = self.get_requests(user) or []
paul@105 235
paul@105 236
        try:
paul@105 237
            requests.remove(uid)
paul@105 238
            self.set_requests(user, requests)
paul@105 239
        except ValueError:
paul@105 240
            return False
paul@105 241
        else:
paul@105 242
            return True
paul@105 243
paul@30 244
class FilePublisher(FileBase):
paul@30 245
paul@30 246
    "A publisher of objects."
paul@30 247
paul@30 248
    def __init__(self, store_dir=PUBLISH_DIR):
paul@30 249
        FileBase.__init__(self, store_dir)
paul@30 250
paul@30 251
    def set_freebusy(self, user, freebusy):
paul@30 252
paul@30 253
        "For the given 'user', set 'freebusy' details."
paul@30 254
paul@52 255
        filename = self.get_object_in_store(user, "freebusy")
paul@30 256
        if not filename:
paul@30 257
            return False
paul@30 258
paul@30 259
        record = []
paul@30 260
        rwrite = record.append
paul@30 261
paul@30 262
        rwrite(("ORGANIZER", {}, user))
paul@30 263
        rwrite(("UID", {}, user))
paul@30 264
        rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))
paul@30 265
paul@112 266
        for start, end, uid, transp in freebusy:
paul@119 267
            if not transp or transp == "OPAQUE":
paul@112 268
                rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
paul@30 269
paul@30 270
        f = open(filename, "w")
paul@30 271
        try:
paul@30 272
            to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH"))
paul@30 273
        finally:
paul@30 274
            f.close()
paul@103 275
            fix_permissions(filename)
paul@30 276
paul@30 277
        return True
paul@30 278
paul@2 279
# vim: tabstop=4 expandtab shiftwidth=4