diff --git a/roundup/admin.py b/roundup/admin.py
index e93bf4f0861122c656826a36fe85776a6addd266..e89f8643df326727cf15b1142a8be69d564fa09b 100644 (file)
--- a/roundup/admin.py
+++ b/roundup/admin.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: admin.py,v 1.55 2003-06-23 08:05:30 neaj Exp $
+# $Id: admin.py,v 1.63 2004-03-21 23:39:08 richard Exp $
'''Administration commands for maintaining Roundup trackers.
'''
+__docformat__ = 'restructuredtext'
import sys, os, getpass, getopt, re, UserDict, shutil, rfc822
-try:
- import csv
-except ImportError:
- csv = None
-from roundup import date, hyperdb, roundupdb, init, password, token
+from roundup import date, hyperdb, roundupdb, init, password, token, rcsv
from roundup import __version__ as roundup_version
import roundup.instance
from roundup.i18n import _
def listTemplates(self):
''' List all the available templates.
- Look in three places:
- <prefix>/share/roundup/templates/*
- <__file__>/../templates/*
- current dir/*
- current dir as a template
+ Look in the following places, where the later rules take precedence:
+
+ 1. <prefix>/share/roundup/templates/*
+ this should be the standard place to find them when Roundup is
+ installed
+ 2. <roundup.admin.__file__>/../templates/*
+ this will be used if Roundup's run in the distro (aka. source)
+ directory
+ 3. <current working dir>/*
+ this is for when someone unpacks a 3rd-party template
+ 4. <current working dir>
+ this is for someone who "cd"s to the 3rd-party template dir
'''
# OK, try <prefix>/share/roundup/templates
# -- this module (roundup.admin) will be installed in something
path = os.path.dirname(path)
tdir = os.path.join(path, 'share', 'roundup', 'templates')
if os.path.isdir(tdir):
- templates = listTemplates(tdir)
+ templates = init.listTemplates(tdir)
break
# OK, now try as if we're in the roundup source distribution
path = os.path.dirname(path)
tdir = os.path.join(path, 'templates')
if os.path.isdir(tdir):
- templates.update(listTemplates(tdir))
+ templates.update(init.listTemplates(tdir))
# Try subdirs of the current dir
- templates.update(listTemplates(os.getcwd()))
+ templates.update(init.listTemplates(os.getcwd()))
# Finally, try the current directory as a template
- template = loadTemplate(os.getcwd())
+ template = init.loadTemplateInfo(os.getcwd())
if template:
templates[template['name']] = template
properties = cl.getprops()
for key, value in props.items():
- proptype = properties[key]
- if isinstance(proptype, hyperdb.Multilink):
- if value is None:
- props[key] = []
- else:
- props[key] = value.split(',')
- elif value is None:
- continue
- elif isinstance(proptype, hyperdb.String):
- continue
- elif isinstance(proptype, hyperdb.Password):
- m = pwre.match(value)
- if m:
- # password is being given to us encrypted
- p = password.Password()
- p.scheme = m.group(1)
- p.password = m.group(2)
- props[key] = p
- else:
- props[key] = password.Password(value)
- elif isinstance(proptype, hyperdb.Date):
- try:
- props[key] = date.Date(value)
- except ValueError, message:
- raise UsageError, '"%s": %s'%(value, message)
- elif isinstance(proptype, hyperdb.Interval):
- try:
- props[key] = date.Interval(value)
- except ValueError, message:
- raise UsageError, '"%s": %s'%(value, message)
- elif isinstance(proptype, hyperdb.Link):
- props[key] = value
- elif isinstance(proptype, hyperdb.Boolean):
- props[key] = value.lower() in ('yes', 'true', 'on', '1')
- elif isinstance(proptype, hyperdb.Number):
- props[key] = float(value)
+ try:
+ props[key] = hyperdb.rawToHyperdb(self.db, cl, itemid,
+ key, value)
+ except hyperdb.HyperdbValueError, message:
+ raise UsageError, message
# try the set
try:
cl = self.get_class(classname)
# display the values
- for key in cl.properties.keys():
+ keys = cl.properties.keys()
+ keys.sort()
+ for key in keys:
value = cl.get(nodeid, key)
print _('%(key)s: %(value)s')%locals()
# convert types
for propname, value in props.items():
- # get the property
try:
- proptype = properties[propname]
- except KeyError:
- raise UsageError, _('%(classname)s has no property '
- '"%(propname)s"')%locals()
-
- if isinstance(proptype, hyperdb.Date):
- try:
- props[propname] = date.Date(value)
- except ValueError, message:
- raise UsageError, _('"%(value)s": %(message)s')%locals()
- elif isinstance(proptype, hyperdb.Interval):
- try:
- props[propname] = date.Interval(value)
- except ValueError, message:
- raise UsageError, _('"%(value)s": %(message)s')%locals()
- elif isinstance(proptype, hyperdb.Password):
- m = pwre.match(value)
- if m:
- # password is being given to us encrypted
- p = password.Password()
- p.scheme = m.group(1)
- p.password = m.group(2)
- props[propname] = p
- else:
- props[propname] = password.Password(value)
- elif isinstance(proptype, hyperdb.Multilink):
- props[propname] = value.split(',')
- elif isinstance(proptype, hyperdb.Boolean):
- props[propname] = value.lower() in ('yes', 'true', 'on', '1')
- elif isinstance(proptype, hyperdb.Number):
- props[propname] = float(value)
+ props[propname] = hyperdb.rawToHyperdb(self.db, cl, None,
+ propname, value)
+ except hyperdb.HyperdbValueError, message:
+ raise UsageError, message
# check for the key property
propname = cl.getkey()
specified, all properties are displayed. By default, the column widths
are the width of the largest value. The width may be explicitly defined
by defining the property as "name:width". For example::
+
roundup> table priority id,name:10
Id Name
1 fatal-bug
4 feature
Also to make the width of the column the width of the label,
- leave a trailing : without a width on the property. E.G.
+ leave a trailing : without a width on the property. For example::
+
roundup> table priority id,name:
Id Name
1 fata
colon-separated-value files that are placed in the nominated
destination directory. The journals are not exported.
'''
- # we need the CSV module
- if csv is None:
- raise UsageError, \
- _('Sorry, you need the csv module to use this function.\n'
- 'Get it from: http://www.object-craft.com.au/projects/csv/')
-
# grab the directory to export to
if len(args) < 1:
raise UsageError, _('Not enough arguments supplied')
+ if rcsv.error:
+ raise UsageError, _(rcsv.error)
+
dir = args[-1]
# get the list of classes to export
else:
classes = self.db.classes.keys()
- # use the csv parser if we can - it's faster
- p = csv.parser(field_sep=':')
-
# do all the classes specified
for classname in classes:
cl = self.get_class(classname)
f = open(os.path.join(dir, classname+'.csv'), 'w')
+ writer = rcsv.writer(f, rcsv.colon_separated)
properties = cl.getprops()
propnames = properties.keys()
propnames.sort()
- l = propnames[:]
- l.append('is retired')
- print >> f, p.join(l)
+ fields = propnames[:]
+ fields.append('is retired')
+ writer.writerow(fields)
# all nodes for this class (not using list() 'cos it doesn't
# include retired nodes)
for nodeid in self.db.getclass(classname).getnodeids():
# get the regular props
- print >>f, p.join(cl.export_list(propnames, nodeid))
+ writer.writerow (cl.export_list(propnames, nodeid))
# close this file
f.close()
'''
if len(args) < 1:
raise UsageError, _('Not enough arguments supplied')
- if csv is None:
- raise UsageError, \
- _('Sorry, you need the csv module to use this function.\n'
- 'Get it from: http://www.object-craft.com.au/projects/csv/')
-
+ if rcsv.error:
+ raise UsageError, _(rcsv.error)
from roundup import hyperdb
for file in os.listdir(args[0]):
# ensure that the properties and the CSV file headings match
cl = self.get_class(classname)
- p = csv.parser(field_sep=':')
- file_props = p.parse(f.readline())
-
-# XXX we don't _really_ need to do this...
-# properties = cl.getprops()
-# propnames = properties.keys()
-# propnames.sort()
-# m = file_props[:]
-# m.sort()
-# if m != propnames:
-# raise UsageError, _('Import file doesn\'t define the same '
-# 'properties as "%(arg0)s".')%{'arg0': args[0]}
+ reader = rcsv.reader(f, rcsv.colon_separated)
+ file_props = None
+ maxid = 1
# loop through the file and create a node for each entry
- maxid = 1
- while 1:
- line = f.readline()
- if not line: break
-
- # parse lines until we get a complete entry
- while 1:
- l = p.parse(line)
- if l: break
- line = f.readline()
- if not line:
- raise ValueError, "Unexpected EOF during CSV parse"
+ for r in reader:
+ if file_props is None:
+ file_props = r
+ continue
# do the import and figure the current highest nodeid
- maxid = max(maxid, int(cl.import_list(file_props, l)))
+ maxid = max(maxid, int(cl.import_list(file_props, r)))
+ # set the id counter
print 'setting', classname, maxid+1
self.db.setid(classname, str(maxid+1))
return 0
if self.db:
self.db.close()
-
-def listTemplates(dir):
- ''' List all the Roundup template directories in a given directory.
-
- Find all the dirs that contain a TEMPLATE-INFO.txt and parse it.
-
- Return a list of dicts of info about the templates.
- '''
- ret = {}
- for idir in os.listdir(dir):
- idir = os.path.join(dir, idir)
- ti = loadTemplate(idir)
- if ti:
- ret[ti['name']] = ti
- return ret
-
-def loadTemplate(dir):
- ''' Attempt to load a Roundup template from the indicated directory.
-
- Return None if there's no template, otherwise a template info
- dictionary.
- '''
- ti = os.path.join(dir, 'TEMPLATE-INFO.txt')
- if not os.path.exists(ti):
- return None
-
- # load up the template's information
- m = rfc822.Message(open(ti))
- ti = {}
- ti['name'] = m['name']
- ti['description'] = m['description']
- ti['intended-for'] = m['intended-for']
- ti['path'] = dir
- return ti
-
if __name__ == '__main__':
tool = AdminTool()
sys.exit(tool.main())