imip-agent

Annotated imip_store.py

50:fb16d908e331
2014-10-22 Paul Boddie Consolidated file access logic in a common method.
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@15 8
STORE_DIR = "/var/lib/imip-agent/store"
paul@30 9
PUBLISH_DIR = "/var/www/imip-agent"
paul@2 10
paul@2 11
def check_dir(base, dir):
paul@2 12
    return commonprefix([base, abspath(dir)]) == base
paul@2 13
paul@30 14
def make_calendar(fragment, method=None):
paul@30 15
    return ("VCALENDAR", {},
paul@30 16
            (method and [("METHOD", {}, method)] or []) +
paul@30 17
            [("VERSION", {}, "2.0")] +
paul@30 18
            fragment
paul@30 19
           )
paul@30 20
paul@26 21
def to_stream(out, fragment, encoding="utf-8"):
paul@26 22
    iterwrite(out, encoding=encoding).append(fragment)
paul@15 23
paul@30 24
class FileBase:
paul@2 25
paul@30 26
    "Basic filesystem operations."
paul@2 27
paul@30 28
    def __init__(self, store_dir=STORE_DIR):
paul@30 29
        self.store_dir = store_dir
paul@2 30
        if not exists(self.store_dir):
paul@2 31
            makedirs(self.store_dir)
paul@2 32
paul@15 33
    def get_file_object(self, base, *parts):
paul@15 34
        pathname = join(base, *parts)
paul@15 35
        return check_dir(base, pathname) and pathname or None
paul@2 36
paul@50 37
    def get_object_for_user(self, user, uid):
paul@15 38
        dir = self.get_file_object(self.store_dir, user)
paul@15 39
        if not dir:
paul@15 40
            return False
paul@15 41
paul@15 42
        filename = self.get_file_object(dir, uid)
paul@15 43
        if not filename:
paul@15 44
            return False
paul@15 45
paul@15 46
        if not exists(dir):
paul@15 47
            makedirs(dir)
paul@15 48
paul@50 49
        return filename
paul@50 50
paul@50 51
class FileStore(FileBase):
paul@50 52
paul@50 53
    "A file store of tabular free/busy data and objects."
paul@50 54
paul@50 55
    def get_event(self, user, uid):
paul@50 56
paul@50 57
        "Get the event for the given 'user' with the given 'uid'."
paul@50 58
paul@50 59
        filename = self.get_object_for_user(user, uid)
paul@50 60
        if not filename or not exists(filename):
paul@50 61
            return None
paul@50 62
paul@50 63
        return exists(filename) and open(filename) or None
paul@50 64
paul@50 65
    def set_event(self, user, uid, node):
paul@50 66
paul@50 67
        "Set an event for 'user' having the given 'uid' and 'node'."
paul@50 68
paul@50 69
        filename = self.get_object_for_user(user, uid)
paul@50 70
        if not filename:
paul@50 71
            return False
paul@50 72
paul@15 73
        f = open(filename, "w")
paul@15 74
        try:
paul@26 75
            to_stream(f, node)
paul@15 76
        finally:
paul@15 77
            f.close()
paul@15 78
paul@15 79
        return True
paul@15 80
paul@15 81
    def get_freebusy(self, user):
paul@15 82
paul@15 83
        "Get free/busy details for the given 'user'."
paul@15 84
paul@50 85
        filename = self.get_object_for_user(user, "freebusy")
paul@15 86
        if not filename or not exists(filename):
paul@2 87
            return None
paul@2 88
paul@2 89
        f = open(filename)
paul@2 90
        try:
paul@2 91
            l = []
paul@2 92
            for line in f.readlines():
paul@17 93
                l.append(tuple(line.strip().split("\t")))
paul@2 94
            return l
paul@2 95
        finally:
paul@2 96
            f.close()
paul@2 97
paul@15 98
    def set_freebusy(self, user, freebusy):
paul@15 99
paul@15 100
        "For the given 'user', set 'freebusy' details."
paul@15 101
paul@50 102
        filename = self.get_object_for_user(user, "freebusy")
paul@15 103
        if not filename:
paul@15 104
            return False
paul@15 105
paul@15 106
        f = open(filename, "w")
paul@15 107
        try:
paul@15 108
            for item in freebusy:
paul@15 109
                f.write("\t".join(item) + "\n")
paul@15 110
        finally:
paul@15 111
            f.close()
paul@15 112
paul@15 113
        return True
paul@15 114
paul@30 115
class FilePublisher(FileBase):
paul@30 116
paul@30 117
    "A publisher of objects."
paul@30 118
paul@30 119
    def __init__(self, store_dir=PUBLISH_DIR):
paul@30 120
        FileBase.__init__(self, store_dir)
paul@30 121
paul@30 122
    def set_freebusy(self, user, freebusy):
paul@30 123
paul@30 124
        "For the given 'user', set 'freebusy' details."
paul@30 125
paul@50 126
        filename = self.get_object_for_user(user, "freebusy")
paul@30 127
        if not filename:
paul@30 128
            return False
paul@30 129
paul@30 130
        record = []
paul@30 131
        rwrite = record.append
paul@30 132
paul@30 133
        rwrite(("ORGANIZER", {}, user))
paul@30 134
        rwrite(("UID", {}, user))
paul@30 135
        rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))
paul@30 136
paul@30 137
        for start, end, uid in freebusy:
paul@30 138
            rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
paul@30 139
paul@30 140
        f = open(filename, "w")
paul@30 141
        try:
paul@30 142
            to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH"))
paul@30 143
        finally:
paul@30 144
            f.close()
paul@30 145
paul@30 146
        return True
paul@30 147
paul@2 148
# vim: tabstop=4 expandtab shiftwidth=4