[CalendarServer-changes] [3274] CalendarServer/trunk/calendarserver/tools/export.py
source_changes at macosforge.org
source_changes at macosforge.org
Wed Oct 29 19:34:06 PDT 2008
Revision: 3274
http://trac.macosforge.org/projects/calendarserver/changeset/3274
Author: cdaboo at apple.com
Date: 2008-10-29 19:34:06 -0700 (Wed, 29 Oct 2008)
Log Message:
-----------
More sophisticated exporter that uses the OD directory to map user names to GUIDs and iterates
over all calendars for a user.
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tools/export.py
Modified: CalendarServer/trunk/calendarserver/tools/export.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/export.py 2008-10-30 01:13:13 UTC (rev 3273)
+++ CalendarServer/trunk/calendarserver/tools/export.py 2008-10-30 02:34:06 UTC (rev 3274)
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+import dsquery
##
# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
@@ -24,22 +25,173 @@
from twistedcaldav.resource import isCalendarCollectionResource
from twistedcaldav.static import CalDAVFile
-class UsageError (StandardError):
- pass
+try:
+ import opendirectory
+ import dsattributes
+except ImportError:
+ sys.path.append("/usr/share/caldavd/lib/python")
+ import opendirectory
+ import dsattributes
+try:
+ from plistlib import readPlist
+except ImportError:
+ from twistedcaldav.py.plistlib import readPlist
+
+class CalendarExporter(object):
+
+ def __init__(self, plistpath, users, output_dir):
+
+ self.plistpath = plistpath
+ self.users = users
+ self.output_dir = output_dir
+
+ self.dsnode = None
+ self.docroot = None
+
+ def run(self):
+ self._extractPlistPieces()
+ self.od = opendirectory.odInit(self.dsnode)
+
+ for user in self.users:
+ print ""
+ print "Dumping user: %s" % (user,)
+ self._dumpUser(user)
+
+ def _extractPlistPieces(self):
+
+ plist = readPlist(self.plistpath)
+
+ try:
+ self.dsnode = plist["DirectoryService"]["params"]["node"]
+ except KeyError:
+ raise ValueError("Unable to read DirectoryService/params/node key from plist: %s" % (self.plistpath,))
+
+ try:
+ self.docroot = plist["DocumentRoot"]
+ except KeyError:
+ raise ValueError("Unable to read DocumentRoot key from plist: %s" % (self.plistpath,))
+
+ print ""
+ print "Parsed: %s" % (self.plistpath,)
+ print "Found DS Node: %s" % (self.dsnode,)
+ print "Found Server docroot: %s" % (self.docroot)
+ print "Output directory: %s" % (self.output_dir)
+
+ def _getCalendarHome(self, user):
+
+ guid = self._getUserGUID(user)
+ return os.path.join(self.docroot, "calendars/__uids__", guid[0:2], guid[2:4], guid)
+
+ def _getUserGUID(self, user):
+
+ query = dsquery.match(dsattributes.kDSNAttrRecordName, user, dsattributes.eDSExact)
+
+ results = opendirectory.queryRecordsWithAttribute_list(
+ self.od,
+ query.attribute,
+ query.value,
+ query.matchType,
+ False,
+ dsattributes.kDSStdRecordTypeUsers,
+ [dsattributes.kDS1AttrGeneratedUID,]
+ )
+
+ for (_ignore, record) in results:
+ guid = record.get(dsattributes.kDS1AttrGeneratedUID, None)
+ if guid:
+ return guid
+ else:
+ raise ValueError("No directory record for user: %s" % (user,))
+
+ def _findCalendars(self, basepath):
+
+ paths = []
+
+ def _addDirectories(path):
+
+ for child in os.listdir(path):
+ childpath = os.path.join(path, child)
+ resource = CalDAVFile(childpath)
+ if resource.exists() and isCalendarCollectionResource(resource):
+ paths.append(childpath)
+ continue
+ elif os.path.isdir(childpath):
+ _addDirectories(childpath)
+
+ _addDirectories(basepath)
+ return paths
+
+ def _dumpUser(self, user):
+
+ # Find the user's calendar home
+ calendar_home = self._getCalendarHome(user)
+ if not os.path.exists(calendar_home):
+ print "Error: No calendar home for user: %s" % (user,)
+ return
+
+ # Create an output directory for the user
+ user_dir = os.path.join(self.output_dir, user)
+ if not os.path.exists(user_dir):
+ os.makedirs(user_dir)
+
+ # List all possible calendars
+ calendar_paths = self._findCalendars(calendar_home)
+
+ for calendar_path in calendar_paths:
+ resource = CalDAVFile(calendar_path)
+
+ if not resource.exists() or not isCalendarCollectionResource(resource):
+ continue
+
+ calendar = iComponent("VCALENDAR")
+ calendar.addProperty(iProperty("VERSION", "2.0"))
+
+ tzids = set()
+
+ for name, _ignore_uid, type in resource.index().search(None):
+ child = resource.getChild(name)
+ child_data = child.iCalendarText()
+
+ try:
+ child_calendar = iComponent.fromString(child_data)
+ except ValueError:
+ continue
+ assert child_calendar.name() == "VCALENDAR"
+
+ for component in child_calendar.subcomponents():
+ # Only insert VTIMEZONEs once
+ if component.name() == "VTIMEZONE":
+ tzid = component.propertyValue("TZID")
+ if tzid in tzids:
+ continue
+ else:
+ tzids.add(tzid)
+
+ calendar.addComponent(component)
+
+ f = file(os.path.join(user_dir, os.path.basename(calendar_path)), "w")
+ f.write(str(calendar))
+ f.close()
+ print "Dumped calendar for user '%s': %s" % (user, calendar_path,)
+
def usage(e=None):
if e:
print e
print ""
name = os.path.basename(sys.argv[0])
- print "usage: %s [-c collection]" % (name,)
+ print "usage: %s [-f plistfile] [-o outputdir] [-u user]" % (name,)
print ""
print "Generate an iCalendar file containing the merged content of each calendar"
print "collection specified."
print ""
print "options:"
print " -h: print this help"
+ print " -f: plist file for server configuration (/etc/caldavd/caldavd.plist)"
+ print " -o: directory in which to write results (./exported)"
+ print " -u: user record name to lookup (can appear multiple times)"
+ print " -h: print this help"
if e:
sys.exit(64)
@@ -48,64 +200,39 @@
def main():
try:
- (optargs, args) = getopt.getopt(sys.argv[1:], "hc:", ["help", "collection="])
+ (optargs, args) = getopt.getopt(sys.argv[1:], "hf:o:u:", ["help",])
except getopt.GetoptError, e:
usage(e)
- collections = set()
+ plistpath = "/etc/caldavd/caldavd.plist"
+ users = set()
+ output_dir = os.path.join(os.getcwd(), "exported")
for opt, arg in optargs:
if opt in ("-h", "--help"):
usage()
- if opt in ("-c", "--collection"):
- collections.add(arg)
+ elif opt in ("-o",):
+ output_dir = arg
+ elif opt in ("-u",):
+ users.add(arg)
+ elif opt == "-f":
+ plistpath = arg
if args:
usage("Too many arguments: %s" % (" ".join(args),))
try:
- calendar = iComponent("VCALENDAR")
- calendar.addProperty(iProperty("VERSION", "2.0"))
+ print "CalendarServer calendar user export tool"
+ print "====================================="
+
+ if not os.path.exists(plistpath):
+ raise ValueError("caldavd.plist file does not exist: %s" % (plistpath,))
+
+ CalendarExporter(plistpath, users, output_dir).run()
- uids = set()
- tzids = set()
+ except ValueError, e:
+ print ""
+ print "Failed: %s" % (str(e),)
- for collection in collections:
- resource = CalDAVFile(collection)
-
- if not resource.exists() or not isCalendarCollectionResource(resource):
- sys.stderr.write("Not a calendar collection: %s\n" % (collection,))
- sys.exit(1)
-
- for name, uid, type in resource.index().search(None):
- child = resource.getChild(name)
- child_data = child.iCalendarText()
-
- try:
- child_calendar = iComponent.fromString(child_data)
- except ValueError:
- continue
- assert child_calendar.name() == "VCALENDAR"
-
- if uid in uids:
- sys.stderr.write("Skipping duplicate event UID: %s" % (uid,))
- continue
- else:
- uids.add(uid)
-
- for component in child_calendar.subcomponents():
- # Only insert VTIMEZONEs once
- if component.name() == "VTIMEZONE":
- tzid = component.propertyValue("TZID")
- if tzid in tzids:
- continue
- else:
- tzids.add(tzid)
-
- calendar.addComponent(component)
-
- except UsageError, e:
- usage(e)
-
if __name__ == "__main__":
main()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20081029/696507e7/attachment-0001.html>
More information about the calendarserver-changes
mailing list