imip-agent

imip_store.py

147:ee2bd0ee8f55
2015-01-12 Paul Boddie Moved common filesystem functionality into a new module. Added preferences support so that the timezone and locale associated with individual users can be retrieved and used to configure the management interface. Made the default permissions of stored data a configuration setting.
     1 #!/usr/bin/env python     2      3 """     4 A simple filesystem-based store of calendar data.     5      6 Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 from datetime import datetime    23 from imiptools.config import STORE_DIR, PUBLISH_DIR    24 from imiptools.filesys import fix_permissions, FileBase    25 from os.path import exists, isfile, join    26 from os import listdir    27 from vCalendar import iterwrite    28     29 def make_calendar(fragment, method=None):    30     31     """    32     Return a complete calendar item wrapping the given 'fragment' and employing    33     the given 'method', if indicated.    34     """    35     36     return ("VCALENDAR", {},    37             (method and [("METHOD", {}, method)] or []) +    38             [("VERSION", {}, "2.0")] +    39             fragment    40            )    41     42 def to_stream(out, fragment, encoding="utf-8"):    43     iterwrite(out, encoding=encoding).append(fragment)    44     45 class FileStore(FileBase):    46     47     "A file store of tabular free/busy data and objects."    48     49     def __init__(self, store_dir=STORE_DIR):    50         FileBase.__init__(self, store_dir)    51     52     def get_events(self, user):    53     54         "Return a list of event identifiers."    55     56         filename = self.get_object_in_store(user, "objects")    57         if not filename or not exists(filename):    58             return None    59     60         return [name for name in listdir(filename) if isfile(join(filename, name))]    61     62     def get_event(self, user, uid):    63     64         "Get the event for the given 'user' with the given 'uid'."    65     66         filename = self.get_object_in_store(user, "objects", uid)    67         if not filename or not exists(filename):    68             return None    69     70         return open(filename) or None    71     72     def set_event(self, user, uid, node):    73     74         "Set an event for 'user' having the given 'uid' and 'node'."    75     76         filename = self.get_object_in_store(user, "objects", uid)    77         if not filename:    78             return False    79     80         f = open(filename, "w")    81         try:    82             to_stream(f, node)    83         finally:    84             f.close()    85             fix_permissions(filename)    86     87         return True    88     89     def get_freebusy(self, user):    90     91         "Get free/busy details for the given 'user'."    92     93         filename = self.get_object_in_store(user, "freebusy")    94         if not filename or not exists(filename):    95             return None    96         else:    97             return self._get_freebusy(filename)    98     99     def get_freebusy_for_other(self, user, other):   100    101         "For the given 'user', get free/busy details for the 'other' user."   102    103         filename = self.get_object_in_store(user, "freebusy-other", other)   104         if not filename:   105             return None   106         else:   107             return self._get_freebusy(filename)   108    109     def _get_freebusy(self, filename):   110         f = open(filename)   111         try:   112             l = []   113             for line in f.readlines():   114                 l.append(tuple(line.strip().split("\t")))   115             return l   116         finally:   117             f.close()   118    119     def set_freebusy(self, user, freebusy):   120    121         "For the given 'user', set 'freebusy' details."   122    123         filename = self.get_object_in_store(user, "freebusy")   124         if not filename:   125             return False   126    127         self._set_freebusy(filename, freebusy)   128         return True   129    130     def set_freebusy_for_other(self, user, freebusy, other):   131    132         "For the given 'user', set 'freebusy' details for the 'other' user."   133    134         filename = self.get_object_in_store(user, "freebusy-other", other)   135         if not filename:   136             return False   137    138         self._set_freebusy(filename, freebusy)   139         return True   140    141     def _set_freebusy(self, filename, freebusy):   142         f = open(filename, "w")   143         try:   144             for item in freebusy:   145                 f.write("\t".join([(value or "OPAQUE") for value in item]) + "\n")   146         finally:   147             f.close()   148             fix_permissions(filename)   149    150     def _get_requests(self, user, queue):   151    152         "Get requests for the given 'user' from the given 'queue'."   153    154         filename = self.get_object_in_store(user, queue)   155         if not filename or not exists(filename):   156             return None   157    158         f = open(filename)   159         try:   160             return [line.strip() for line in f.readlines()]   161         finally:   162             f.close()   163    164     def get_requests(self, user):   165    166         "Get requests for the given 'user'."   167    168         return self._get_requests(user, "requests")   169    170     def get_cancellations(self, user):   171    172         "Get cancellations for the given 'user'."   173    174         return self._get_requests(user, "cancellations")   175    176     def _set_requests(self, user, requests, queue):   177    178         """   179         For the given 'user', set the list of queued 'requests' in the given   180         'queue'.   181         """   182    183         filename = self.get_object_in_store(user, queue)   184         if not filename:   185             return False   186    187         f = open(filename, "w")   188         try:   189             for request in requests:   190                 print >>f, request   191         finally:   192             f.close()   193             fix_permissions(filename)   194    195         return True   196    197     def set_requests(self, user, requests):   198    199         "For the given 'user', set the list of queued 'requests'."   200    201         return self._set_requests(user, requests, "requests")   202    203     def set_cancellations(self, user, cancellations):   204    205         "For the given 'user', set the list of queued 'cancellations'."   206    207         return self._set_requests(user, cancellations, "cancellations")   208    209     def _set_request(self, user, request, queue):   210    211         "For the given 'user', set the queued 'request' in the given 'queue'."   212    213         filename = self.get_object_in_store(user, queue)   214         if not filename:   215             return False   216    217         f = open(filename, "a")   218         try:   219             print >>f, request   220         finally:   221             f.close()   222             fix_permissions(filename)   223    224         return True   225    226     def set_request(self, user, request):   227    228         "For the given 'user', set the queued 'request'."   229    230         return self._set_request(user, request, "requests")   231    232     def set_cancellation(self, user, cancellation):   233    234         "For the given 'user', set the queued 'cancellation'."   235    236         return self._set_request(user, cancellation, "cancellations")   237    238     def queue_request(self, user, uid):   239    240         "Queue a request for 'user' having the given 'uid'."   241    242         requests = self.get_requests(user) or []   243    244         if uid not in requests:   245             return self.set_request(user, uid)   246    247         return False   248    249     def dequeue_request(self, user, uid):   250    251         "Dequeue a request for 'user' having the given 'uid'."   252    253         requests = self.get_requests(user) or []   254    255         try:   256             requests.remove(uid)   257             self.set_requests(user, requests)   258         except ValueError:   259             return False   260         else:   261             return True   262    263     def cancel_event(self, user, uid):   264    265         "Queue an event for cancellation for 'user' having the given 'uid'."   266    267         cancellations = self.get_cancellations(user) or []   268    269         if uid not in cancellations:   270             return self.set_cancellation(user, uid)   271    272         return False   273    274 class FilePublisher(FileBase):   275    276     "A publisher of objects."   277    278     def __init__(self, store_dir=PUBLISH_DIR):   279         FileBase.__init__(self, store_dir)   280    281     def set_freebusy(self, user, freebusy):   282    283         "For the given 'user', set 'freebusy' details."   284    285         filename = self.get_object_in_store(user, "freebusy")   286         if not filename:   287             return False   288    289         record = []   290         rwrite = record.append   291    292         rwrite(("ORGANIZER", {}, user))   293         rwrite(("UID", {}, user))   294         rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))   295    296         for start, end, uid, transp in freebusy:   297             if not transp or transp == "OPAQUE":   298                 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))   299    300         f = open(filename, "w")   301         try:   302             to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH"))   303         finally:   304             f.close()   305             fix_permissions(filename)   306    307         return True   308    309 # vim: tabstop=4 expandtab shiftwidth=4