imip-agent

Annotated imip_store.py

30:e5396f3af28f
2014-10-01 Paul Boddie Fixed the opacity of events for free/busy consideration and publishing. Tidied calendar object output. Added free/busy publishing support.
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@30 37
class FileStore(FileBase):
paul@30 38
paul@30 39
    "A file store of tabular free/busy data and objects."
paul@30 40
paul@15 41
    def get_event(self, user, uid):
paul@2 42
paul@15 43
        "Get the event for the given 'user' with the given 'uid'."
paul@15 44
paul@15 45
        filename = self.get_file_object(self.store_dir, user, uid)
paul@15 46
        if not filename:
paul@2 47
            return None
paul@2 48
paul@15 49
        return exists(filename) and open(filename) or None
paul@15 50
paul@26 51
    def set_event(self, user, uid, node):
paul@15 52
paul@26 53
        "Set an event for 'user' having the given 'uid' and 'node'."
paul@15 54
paul@15 55
        dir = self.get_file_object(self.store_dir, user)
paul@15 56
        if not dir:
paul@15 57
            return False
paul@15 58
paul@15 59
        filename = self.get_file_object(dir, uid)
paul@15 60
        if not filename:
paul@15 61
            return False
paul@15 62
paul@15 63
        if not exists(dir):
paul@15 64
            makedirs(dir)
paul@15 65
paul@15 66
        f = open(filename, "w")
paul@15 67
        try:
paul@26 68
            to_stream(f, node)
paul@15 69
        finally:
paul@15 70
            f.close()
paul@15 71
paul@15 72
        return True
paul@15 73
paul@15 74
    def get_freebusy(self, user):
paul@15 75
paul@15 76
        "Get free/busy details for the given 'user'."
paul@15 77
paul@15 78
        filename = self.get_file_object(self.store_dir, user, "freebusy")
paul@15 79
        if not filename or not exists(filename):
paul@2 80
            return None
paul@2 81
paul@2 82
        f = open(filename)
paul@2 83
        try:
paul@2 84
            l = []
paul@2 85
            for line in f.readlines():
paul@17 86
                l.append(tuple(line.strip().split("\t")))
paul@2 87
            return l
paul@2 88
        finally:
paul@2 89
            f.close()
paul@2 90
paul@15 91
    def set_freebusy(self, user, freebusy):
paul@15 92
paul@15 93
        "For the given 'user', set 'freebusy' details."
paul@15 94
paul@15 95
        filename = self.get_file_object(self.store_dir, user, "freebusy")
paul@15 96
        if not filename:
paul@15 97
            return False
paul@15 98
paul@15 99
        dir = split(filename)[0]
paul@15 100
paul@15 101
        if not exists(dir):
paul@15 102
            makedirs(dir)
paul@15 103
paul@15 104
        f = open(filename, "w")
paul@15 105
        try:
paul@15 106
            for item in freebusy:
paul@15 107
                f.write("\t".join(item) + "\n")
paul@15 108
        finally:
paul@15 109
            f.close()
paul@15 110
paul@15 111
        return True
paul@15 112
paul@30 113
class FilePublisher(FileBase):
paul@30 114
paul@30 115
    "A publisher of objects."
paul@30 116
paul@30 117
    def __init__(self, store_dir=PUBLISH_DIR):
paul@30 118
        FileBase.__init__(self, store_dir)
paul@30 119
paul@30 120
    def set_freebusy(self, user, freebusy):
paul@30 121
paul@30 122
        "For the given 'user', set 'freebusy' details."
paul@30 123
paul@30 124
        filename = self.get_file_object(self.store_dir, user, "freebusy")
paul@30 125
        if not filename:
paul@30 126
            return False
paul@30 127
paul@30 128
        dir = split(filename)[0]
paul@30 129
paul@30 130
        if not exists(dir):
paul@30 131
            makedirs(dir)
paul@30 132
paul@30 133
        record = []
paul@30 134
        rwrite = record.append
paul@30 135
paul@30 136
        rwrite(("ORGANIZER", {}, user))
paul@30 137
        rwrite(("UID", {}, user))
paul@30 138
        rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))
paul@30 139
paul@30 140
        for start, end, uid in freebusy:
paul@30 141
            rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
paul@30 142
paul@30 143
        f = open(filename, "w")
paul@30 144
        try:
paul@30 145
            to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH"))
paul@30 146
        finally:
paul@30 147
            f.close()
paul@30 148
paul@30 149
        return True
paul@30 150
paul@2 151
# vim: tabstop=4 expandtab shiftwidth=4