diff --git a/roundup/admin.py b/roundup/admin.py
index e54ef7c609ebfbb4ddcf646311e0922bfa1b4c40..94e38462d4eb60dda9283f68ecf66d4893fdef73 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.
#
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: admin.py,v 1.16 2002-07-09 04:19:09 richard Exp $
+# $Id: admin.py,v 1.38 2003-02-25 10:19:31 richard Exp $
+
+'''Administration commands for maintaining Roundup trackers.
+'''
import sys, os, getpass, getopt, re, UserDict, shlex, shutil
try:
import sys, os, getpass, getopt, re, UserDict, shlex, shutil
try:
pass
class AdminTool:
pass
class AdminTool:
+ ''' A collection of methods used in maintaining Roundup trackers.
+
+ Typically these methods are accessed through the roundup-admin
+ script. The main() method provided on this class gives the main
+ loop for the roundup-admin script.
+ Actions are defined by do_*() methods, with help for the action
+ given in the method docstring.
+
+ Additional help may be supplied by help_*() methods.
+ '''
def __init__(self):
self.commands = CommandDict()
for k in AdminTool.__dict__.keys():
def __init__(self):
self.commands = CommandDict()
for k in AdminTool.__dict__.keys():
for k in AdminTool.__dict__.keys():
if k[:5] == 'help_':
self.help[k[5:]] = getattr(self, k)
for k in AdminTool.__dict__.keys():
if k[:5] == 'help_':
self.help[k[5:]] = getattr(self, k)
- self.instance_home = ''
+ self.tracker_home = ''
self.db = None
def get_class(self, classname):
self.db = None
def get_class(self, classname):
raise UsageError, _('no such class "%(classname)s"')%locals()
def props_from_args(self, args):
raise UsageError, _('no such class "%(classname)s"')%locals()
def props_from_args(self, args):
+ ''' Produce a dictionary of prop: value from the args list.
+
+ The args list is specified as ``prop=value prop=value ...``.
+ '''
props = {}
for arg in args:
if arg.find('=') == -1:
props = {}
for arg in args:
if arg.find('=') == -1:
- raise UsageError, _('argument "%(arg)s" not propname=value')%locals()
+ raise UsageError, _('argument "%(arg)s" not propname=value'
+ )%locals()
try:
key, value = arg.split('=')
except ValueError:
try:
key, value = arg.split('=')
except ValueError:
- raise UsageError, _('argument "%(arg)s" not propname=value')%locals()
- props[key] = value
+ raise UsageError, _('argument "%(arg)s" not propname=value'
+ )%locals()
+ if value:
+ props[key] = value
+ else:
+ props[key] = None
return props
def usage(self, message=''):
return props
def usage(self, message=''):
+ ''' Display a simple usage message.
+ '''
if message:
message = _('Problem: %(message)s)\n\n')%locals()
if message:
message = _('Problem: %(message)s)\n\n')%locals()
- print _('''%(message)sUsage: roundup-admin [-i instance home] [-u login] [-c] <command> <arguments>
+ print _('''%(message)sUsage: roundup-admin [options] <command> <arguments>
+
+Options:
+ -i instance home -- specify the issue tracker "home directory" to administer
+ -u -- the user[:password] to use for commands
+ -c -- when outputting lists of data, just comma-separate them
Help:
roundup-admin -h
roundup-admin help -- this help
roundup-admin help <command> -- command-specific help
roundup-admin help all -- all available help
Help:
roundup-admin -h
roundup-admin help -- this help
roundup-admin help <command> -- command-specific help
roundup-admin help all -- all available help
-Options:
- -i instance home -- specify the issue tracker "home directory" to administer
- -u -- the user[:password] to use for commands
- -c -- when outputting lists of data, just comma-separate them''')%locals()
+''')%locals()
self.help_commands()
def help_commands(self):
self.help_commands()
def help_commands(self):
+ ''' List the commands available with their precis help.
+ '''
print _('Commands:'),
commands = ['']
for command in self.commands.values():
print _('Commands:'),
commands = ['']
for command in self.commands.values():
print
def help_commands_html(self, indent_re=re.compile(r'^(\s+)\S+')):
print
def help_commands_html(self, indent_re=re.compile(r'^(\s+)\S+')):
- commands = self.commands.values()
+ ''' Produce an HTML command list.
+ '''
+ commands = self.commands.values()
def sortfun(a, b):
return cmp(a.__name__, b.__name__)
commands.sort(sortfun)
def sortfun(a, b):
return cmp(a.__name__, b.__name__)
commands.sort(sortfun)
- for command in commands:
+ for command in commands:
h = command.__doc__.split('\n')
name = command.__name__[3:]
usage = h[0]
h = command.__doc__.split('\n')
name = command.__name__[3:]
usage = h[0]
def help_all(self):
print _('''
def help_all(self):
print _('''
-All commands (except help) require an instance specifier. This is just the path
-to the roundup instance you're working with. A roundup instance is where
+All commands (except help) require a tracker specifier. This is just the path
+to the roundup tracker you're working with. A roundup tracker is where
roundup keeps the database and configuration file that defines an issue
tracker. It may be thought of as the issue tracker's "home directory". It may
roundup keeps the database and configuration file that defines an issue
tracker. It may be thought of as the issue tracker's "home directory". It may
-be specified in the environment variable ROUNDUP_INSTANCE or on the command
-line as "-i instance".
+be specified in the environment variable TRACKER_HOME or on the command
+line as "-i tracker".
A designator is a classname and a nodeid concatenated, eg. bug1, user10, ...
A designator is a classname and a nodeid concatenated, eg. bug1, user10, ...
backends = roundup.backends.__all__
print _('Back ends:'), ', '.join(backends)
backends = roundup.backends.__all__
print _('Back ends:'), ', '.join(backends)
-
- def do_install(self, instance_home, args):
+ def do_install(self, tracker_home, args):
'''Usage: install [template [backend [admin password]]]
'''Usage: install [template [backend [admin password]]]
- Install a new Roundup instance.
+ Install a new Roundup tracker.
- The command will prompt for the instance home directory (if not supplied
- through INSTANCE_HOME or the -i option). The template, backend and admin
+ The command will prompt for the tracker home directory (if not supplied
+ through TRACKER_HOME or the -i option). The template, backend and admin
password may be specified on the command-line as arguments, in that
order.
The initialise command must be called after this command in order
password may be specified on the command-line as arguments, in that
order.
The initialise command must be called after this command in order
- to initialise the instance's database. You may edit the instance's
+ to initialise the tracker's database. You may edit the tracker's
initial database contents before running that command by editing
initial database contents before running that command by editing
- the instance's dbinit.py module init() function.
+ the tracker's dbinit.py module init() function.
See also initopts help.
'''
if len(args) < 1:
raise UsageError, _('Not enough arguments supplied')
See also initopts help.
'''
if len(args) < 1:
raise UsageError, _('Not enough arguments supplied')
- # make sure the instance home can be created
- parent = os.path.split(instance_home)[0]
+ # make sure the tracker home can be created
+ parent = os.path.split(tracker_home)[0]
if not os.path.exists(parent):
raise UsageError, _('Instance home parent directory "%(parent)s"'
' does not exist')%locals()
if not os.path.exists(parent):
raise UsageError, _('Instance home parent directory "%(parent)s"'
' does not exist')%locals()
backend = raw_input(_('Select backend [anydbm]: ')).strip()
if not backend:
backend = 'anydbm'
backend = raw_input(_('Select backend [anydbm]: ')).strip()
if not backend:
backend = 'anydbm'
+ # XXX perform a unit test based on the user's selections
# install!
# install!
- init.install(instance_home, template, backend)
+ init.install(tracker_home, template)
+ init.write_select_db(tracker_home, backend)
print _('''
print _('''
- You should now edit the instance configuration file:
- %(instance_config_file)s
- ... at a minimum, you must set MAILHOST, MAIL_DOMAIN and ADMIN_EMAIL.
+ You should now edit the tracker configuration file:
+ %(config_file)s
+ ... at a minimum, you must set MAILHOST, TRACKER_WEB, MAIL_DOMAIN and
+ ADMIN_EMAIL.
If you wish to modify the default schema, you should also edit the database
initialisation file:
%(database_config_file)s
... see the documentation on customizing for more information.
''')%{
If you wish to modify the default schema, you should also edit the database
initialisation file:
%(database_config_file)s
... see the documentation on customizing for more information.
''')%{
- 'instance_config_file': os.path.join(instance_home, 'instance_config.py'),
- 'database_config_file': os.path.join(instance_home, 'dbinit.py')
+ 'config_file': os.path.join(tracker_home, 'config.py'),
+ 'database_config_file': os.path.join(tracker_home, 'dbinit.py')
}
return 0
}
return 0
- def do_initialise(self, instance_home, args):
+ def do_initialise(self, tracker_home, args):
'''Usage: initialise [adminpw]
'''Usage: initialise [adminpw]
- Initialise a new Roundup instance.
+ Initialise a new Roundup tracker.
The administrator details will be set at this step.
The administrator details will be set at this step.
- Execute the instance's initialisation function dbinit.init()
+ Execute the tracker's initialisation function dbinit.init()
'''
# password
if len(args) > 1:
'''
# password
if len(args) > 1:
adminpw = getpass.getpass(_('Admin Password: '))
confirm = getpass.getpass(_(' Confirm: '))
adminpw = getpass.getpass(_('Admin Password: '))
confirm = getpass.getpass(_(' Confirm: '))
- # make sure the instance home is installed
- if not os.path.exists(instance_home):
+ # make sure the tracker home is installed
+ if not os.path.exists(tracker_home):
raise UsageError, _('Instance home does not exist')%locals()
raise UsageError, _('Instance home does not exist')%locals()
- if not os.path.exists(os.path.join(instance_home, 'html')):
+ try:
+ tracker = roundup.instance.open(tracker_home)
+ except roundup.instance.TrackerError:
raise UsageError, _('Instance has not been installed')%locals()
# is there already a database?
raise UsageError, _('Instance has not been installed')%locals()
# is there already a database?
- if os.path.exists(os.path.join(instance_home, 'db')):
+ try:
+ db_exists = tracker.select_db.Database.exists(tracker.config)
+ except AttributeError:
+ # TODO: move this code to exists() static method in every backend
+ db_exists = os.path.exists(os.path.join(tracker_home, 'db'))
+ if db_exists:
print _('WARNING: The database is already initialised!')
print _('If you re-initialise it, you will lose all the data!')
ok = raw_input(_('Erase it? Y/[N]: ')).strip()
if ok.lower() != 'y':
return 0
print _('WARNING: The database is already initialised!')
print _('If you re-initialise it, you will lose all the data!')
ok = raw_input(_('Erase it? Y/[N]: ')).strip()
if ok.lower() != 'y':
return 0
- # nuke it
- shutil.rmtree(os.path.join(instance_home, 'db'))
+ # Get a database backend in use by tracker
+ try:
+ # nuke it
+ tracker.select_db.Database.nuke(tracker.config)
+ except AttributeError:
+ # TODO: move this code to nuke() static method in every backend
+ shutil.rmtree(os.path.join(tracker_home, 'db'))
# GO
# GO
- init.initialise(instance_home, adminpw)
+ init.initialise(tracker_home, adminpw)
return 0
return 0
for designator in designators:
# decode the node designator
try:
for designator in designators:
# decode the node designator
try:
- classname, nodeid = roundupdb.splitDesignator(designator)
- except roundupdb.DesignatorError, message:
+ classname, nodeid = hyperdb.splitDesignator(designator)
+ except hyperdb.DesignatorError, message:
raise UsageError, message
# get the class
raise UsageError, message
# get the class
return 0
return 0
- def do_set(self, args):
- '''Usage: set designator[,designator]* propname=value ...
- Set the given property of one or more designator(s).
+ def do_set(self, args, pwre = re.compile(r'{(\w+)}(.+)')):
+ '''Usage: set [items] property=value property=value ...
+ Set the given properties of one or more items(s).
+
+ The items may be specified as a class or as a comma-separeted
+ list of item designators (ie "designator[,designator,...]").
- Sets the property to the value for all designators given.
+ This command sets the properties to the values for all designators
+ given. If the value is missing (ie. "property=") then the property is
+ un-set.
'''
if len(args) < 2:
raise UsageError, _('Not enough arguments supplied')
from roundup import hyperdb
designators = args[0].split(',')
'''
if len(args) < 2:
raise UsageError, _('Not enough arguments supplied')
from roundup import hyperdb
designators = args[0].split(',')
+ if len(designators) == 1:
+ designator = designators[0]
+ try:
+ designator = hyperdb.splitDesignator(designator)
+ designators = [designator]
+ except hyperdb.DesignatorError:
+ cl = self.get_class(designator)
+ designators = [(designator, x) for x in cl.list()]
+ else:
+ try:
+ designators = [hyperdb.splitDesignator(x) for x in designators]
+ except hyperdb.DesignatorError, message:
+ raise UsageError, message
# get the props from the args
props = self.props_from_args(args[1:])
# now do the set for all the nodes
# get the props from the args
props = self.props_from_args(args[1:])
# now do the set for all the nodes
- for designator in designators:
- # decode the node designator
- try:
- classname, nodeid = roundupdb.splitDesignator(designator)
- except roundupdb.DesignatorError, message:
- raise UsageError, message
-
- # get the class
+ for classname, itemid in designators:
cl = self.get_class(classname)
properties = cl.getprops()
for key, value in props.items():
proptype = properties[key]
cl = self.get_class(classname)
properties = cl.getprops()
for key, value in props.items():
proptype = properties[key]
- if isinstance(proptype, hyperdb.String):
+ 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):
continue
elif isinstance(proptype, hyperdb.Password):
- props[key] = password.Password(value)
+ 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)
elif isinstance(proptype, hyperdb.Date):
try:
props[key] = date.Date(value)
raise UsageError, '"%s": %s'%(value, message)
elif isinstance(proptype, hyperdb.Link):
props[key] = value
raise UsageError, '"%s": %s'%(value, message)
elif isinstance(proptype, hyperdb.Link):
props[key] = value
- elif isinstance(proptype, hyperdb.Multilink):
- props[key] = value.split(',')
+ elif isinstance(proptype, hyperdb.Boolean):
+ props[key] = value.lower() in ('yes', 'true', 'on', '1')
+ elif isinstance(proptype, hyperdb.Number):
+ props[key] = float(value)
# try the set
try:
# try the set
try:
- apply(cl.set, (nodeid, ), props)
+ apply(cl.set, (itemid, ), props)
except (TypeError, IndexError, ValueError), message:
except (TypeError, IndexError, ValueError), message:
+ import traceback; traceback.print_exc()
raise UsageError, message
return 0
raise UsageError, message
return 0
# decode the node designator
try:
# decode the node designator
try:
- classname, nodeid = roundupdb.splitDesignator(args[0])
- except roundupdb.DesignatorError, message:
+ classname, nodeid = hyperdb.splitDesignator(args[0])
+ except hyperdb.DesignatorError, message:
raise UsageError, message
# get the class
raise UsageError, message
# get the class
value = cl.get(nodeid, key)
print _('%(key)s: %(value)s')%locals()
value = cl.get(nodeid, key)
print _('%(key)s: %(value)s')%locals()
- def do_create(self, args):
+ def do_create(self, args, pwre = re.compile(r'{(\w+)}(.+)')):
'''Usage: create classname property=value ...
Create a new entry of a given class.
'''Usage: create classname property=value ...
Create a new entry of a given class.
except ValueError, message:
raise UsageError, _('"%(value)s": %(message)s')%locals()
elif isinstance(proptype, hyperdb.Password):
except ValueError, message:
raise UsageError, _('"%(value)s": %(message)s')%locals()
elif isinstance(proptype, hyperdb.Password):
- props[propname] = password.Password(value)
+ 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.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)
# check for the key property
propname = cl.getkey()
# check for the key property
propname = cl.getkey()
if len(args) < 1:
raise UsageError, _('Not enough arguments supplied')
try:
if len(args) < 1:
raise UsageError, _('Not enough arguments supplied')
try:
- classname, nodeid = roundupdb.splitDesignator(args[0])
- except roundupdb.DesignatorError, message:
+ classname, nodeid = hyperdb.splitDesignator(args[0])
+ except hyperdb.DesignatorError, message:
raise UsageError, message
try:
raise UsageError, message
try:
designators = args[0].split(',')
for designator in designators:
try:
designators = args[0].split(',')
for designator in designators:
try:
- classname, nodeid = roundupdb.splitDesignator(designator)
- except roundupdb.DesignatorError, message:
+ classname, nodeid = hyperdb.splitDesignator(designator)
+ except hyperdb.DesignatorError, message:
raise UsageError, message
try:
self.db.getclass(classname).retire(nodeid)
raise UsageError, message
try:
self.db.getclass(classname).retire(nodeid)
return 0
def do_export(self, args):
return 0
def do_export(self, args):
- '''Usage: export class[,class] destination_dir
- Export the database to tab-separated-value files.
+ '''Usage: export [class[,class]] export_dir
+ Export the database to colon-separated-value files.
This action exports the current data from the database into
This action exports the current data from the database into
- tab-separated-value files that are placed in the nominated destination
- directory. The journals are not exported.
+ colon-separated-value files that are placed in the nominated
+ destination directory. The journals are not exported.
'''
'''
- if len(args) < 2:
+ # 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')
raise UsageError, _('Not enough arguments supplied')
- classes = args[0].split(',')
- dir = args[1]
+ dir = args[-1]
+
+ # get the list of classes to export
+ if len(args) == 2:
+ classes = args[0].split(',')
+ else:
+ classes = self.db.classes.keys()
# use the csv parser if we can - it's faster
# use the csv parser if we can - it's faster
- if csv is not None:
- p = csv.parser(field_sep=':')
+ 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')
# do all the classes specified
for classname in classes:
cl = self.get_class(classname)
f = open(os.path.join(dir, classname+'.csv'), 'w')
- f.write(':'.join(cl.properties.keys()) + '\n')
+ properties = cl.getprops()
+ propnames = properties.keys()
+ propnames.sort()
+ print >> f, p.join(propnames)
# all nodes for this class
# all nodes for this class
- properties = cl.properties.items()
for nodeid in cl.list():
for nodeid in cl.list():
- l = []
- for prop, proptype in properties:
- value = cl.get(nodeid, prop)
- # convert data where needed
- if isinstance(proptype, hyperdb.Date):
- value = value.get_tuple()
- elif isinstance(proptype, hyperdb.Interval):
- value = value.get_tuple()
- elif isinstance(proptype, hyperdb.Password):
- value = str(value)
- l.append(repr(value))
-
- # now write
- if csv is not None:
- f.write(p.join(l) + '\n')
- else:
- # escape the individual entries to they're valid CSV
- m = []
- for entry in l:
- if '"' in entry:
- entry = '""'.join(entry.split('"'))
- if ':' in entry:
- entry = '"%s"'%entry
- m.append(entry)
- f.write(':'.join(m) + '\n')
+ print >>f, p.join(cl.export_list(propnames, nodeid))
return 0
def do_import(self, args):
return 0
def do_import(self, args):
- '''Usage: import class file
- Import the contents of the tab-separated-value file.
-
- The file must define the same properties as the class (including having
- a "header" line with those property names.) The new nodes are added to
- the existing database - if you want to create a new database using the
- imported data, then create a new database (or, tediously, retire all
- the old data.)
+ '''Usage: import import_dir
+ Import a database from the directory containing CSV files, one per
+ class to import.
+
+ The files must define the same properties as the class (including having
+ a "header" line with those property names.)
+
+ The imported nodes will have the same nodeid as defined in the
+ import file, thus replacing any existing content.
+
+ The new nodes are added to the existing database - if you want to
+ create a new database using the imported data, then create a new
+ database (or, tediously, retire all the old data.)
'''
'''
- if len(args) < 2:
+ if len(args) < 1:
raise UsageError, _('Not enough arguments supplied')
if csv is None:
raise UsageError, \
raise UsageError, _('Not enough arguments supplied')
if csv is None:
raise UsageError, \
from roundup import hyperdb
from roundup import hyperdb
- # ensure that the properties and the CSV file headings match
- classname = args[0]
- cl = self.get_class(classname)
- f = open(args[1])
- p = csv.parser(field_sep=':')
- file_props = p.parse(f.readline())
- props = cl.properties.keys()
- m = file_props[:]
- m.sort()
- props.sort()
- if m != props:
- raise UsageError, _('Import file doesn\'t define the same '
- 'properties as "%(arg0)s".')%{'arg0': args[0]}
-
- # loop through the file and create a node for each entry
- n = range(len(props))
- while 1:
- line = f.readline()
- if not line: break
+ for file in os.listdir(args[0]):
+ f = open(os.path.join(args[0], file))
+
+ # get the classname
+ classname = os.path.splitext(file)[0]
- # parse lines until we get a complete entry
+ # 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())
+ 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]}
+
+ # loop through the file and create a node for each entry
+ maxid = 1
while 1:
while 1:
- l = p.parse(line)
- if l: break
line = f.readline()
line = f.readline()
- if not line:
- raise ValueError, "Unexpected EOF during CSV parse"
-
- # make the new node's property map
- d = {}
- for i in n:
- # Use eval to reverse the repr() used to output the CSV
- value = eval(l[i])
- # Figure the property for this column
- key = file_props[i]
- proptype = cl.properties[key]
- # Convert for property type
- if isinstance(proptype, hyperdb.Date):
- value = date.Date(value)
- elif isinstance(proptype, hyperdb.Interval):
- value = date.Interval(value)
- elif isinstance(proptype, hyperdb.Password):
- pwd = password.Password()
- pwd.unpack(value)
- value = pwd
- if value is not None:
- d[key] = value
-
- # and create the new node
- apply(cl.create, (), d)
+ 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"
+
+ # do the import and figure the current highest nodeid
+ maxid = max(maxid, int(cl.import_list(propnames, l)))
+
+ print 'setting', classname, maxid+1
+ self.db.setid(classname, str(maxid+1))
return 0
def do_pack(self, args):
return 0
def do_pack(self, args):
raise ValueError, _('Invalid format')
m = m.groupdict()
if m['period']:
raise ValueError, _('Invalid format')
m = m.groupdict()
if m['period']:
- # TODO: need to fix date module. one should be able to say
- # pack_before = date.Date(". - %s"%value)
- pack_before = date.Date(".") + date.Interval("- %s"%value)
+ pack_before = date.Date(". - %s"%value)
elif m['date']:
pack_before = date.Date(value)
self.db.pack(pack_before)
elif m['date']:
pack_before = date.Date(value)
self.db.pack(pack_before)
def do_reindex(self, args):
'''Usage: reindex
def do_reindex(self, args):
'''Usage: reindex
- Re-generate an instance's search indexes.
+ Re-generate a tracker's search indexes.
- This will re-generate the search indexes for an instance. This will
+ This will re-generate the search indexes for a tracker. This will
typically happen automatically.
'''
self.db.indexer.force_reindex()
self.db.reindex()
return 0
typically happen automatically.
'''
self.db.indexer.force_reindex()
self.db.reindex()
return 0
+ def do_security(self, args):
+ '''Usage: security [Role name]
+ Display the Permissions available to one or all Roles.
+ '''
+ if len(args) == 1:
+ role = args[0]
+ try:
+ roles = [(args[0], self.db.security.role[args[0]])]
+ except KeyError:
+ print _('No such Role "%(role)s"')%locals()
+ return 1
+ else:
+ roles = self.db.security.role.items()
+ role = self.db.config.NEW_WEB_USER_ROLES
+ if ',' in role:
+ print _('New Web users get the Roles "%(role)s"')%locals()
+ else:
+ print _('New Web users get the Role "%(role)s"')%locals()
+ role = self.db.config.NEW_EMAIL_USER_ROLES
+ if ',' in role:
+ print _('New Email users get the Roles "%(role)s"')%locals()
+ else:
+ print _('New Email users get the Role "%(role)s"')%locals()
+ roles.sort()
+ for rolename, role in roles:
+ print _('Role "%(name)s":')%role.__dict__
+ for permission in role.permissions:
+ if permission.klass:
+ print _(' %(description)s (%(name)s for "%(klass)s" '
+ 'only)')%permission.__dict__
+ else:
+ print _(' %(description)s (%(name)s)')%permission.__dict__
+ return 0
+
def run_command(self, args):
'''Run a single command
'''
def run_command(self, args):
'''Run a single command
'''
return 1
command, function = functions[0]
return 1
command, function = functions[0]
- # make sure we have an instance_home
- while not self.instance_home:
- self.instance_home = raw_input(_('Enter instance home: ')).strip()
+ # make sure we have a tracker_home
+ while not self.tracker_home:
+ self.tracker_home = raw_input(_('Enter tracker home: ')).strip()
# before we open the db, we may be doing an install or init
if command == 'initialise':
try:
# before we open the db, we may be doing an install or init
if command == 'initialise':
try:
- return self.do_initialise(self.instance_home, args)
+ return self.do_initialise(self.tracker_home, args)
except UsageError, message:
print _('Error: %(message)s')%locals()
return 1
elif command == 'install':
try:
except UsageError, message:
print _('Error: %(message)s')%locals()
return 1
elif command == 'install':
try:
- return self.do_install(self.instance_home, args)
+ return self.do_install(self.tracker_home, args)
except UsageError, message:
print _('Error: %(message)s')%locals()
return 1
except UsageError, message:
print _('Error: %(message)s')%locals()
return 1
- # get the instance
+ # get the tracker
try:
try:
- instance = roundup.instance.open(self.instance_home)
+ tracker = roundup.instance.open(self.tracker_home)
except ValueError, message:
except ValueError, message:
- self.instance_home = ''
- print _("Error: Couldn't open instance: %(message)s")%locals()
+ self.tracker_home = ''
+ print _("Error: Couldn't open tracker: %(message)s")%locals()
return 1
# only open the database once!
if not self.db:
return 1
# only open the database once!
if not self.db:
- self.db = instance.open('admin')
+ self.db = tracker.open('admin')
# do the command
ret = 0
# do the command
ret = 0
return 1
# handle command-line args
return 1
# handle command-line args
- self.instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
+ self.tracker_home = os.environ.get('TRACKER_HOME', '')
# TODO: reinstate the user/password stuff (-u arg too)
name = password = ''
if os.environ.has_key('ROUNDUP_LOGIN'):
# TODO: reinstate the user/password stuff (-u arg too)
name = password = ''
if os.environ.has_key('ROUNDUP_LOGIN'):
self.usage()
return 0
if opt == '-i':
self.usage()
return 0
if opt == '-i':
- self.instance_home = arg
+ self.tracker_home = arg
if opt == '-c':
self.comma_sep = 1
# if no command - go interactive
if opt == '-c':
self.comma_sep = 1
# if no command - go interactive
+ # wrap in a try/finally so we always close off the db
ret = 0
ret = 0
- if not args:
- self.interactive()
- else:
- ret = self.run_command(args)
- if self.db: self.db.commit()
- return ret
-
+ try:
+ if not args:
+ self.interactive()
+ else:
+ ret = self.run_command(args)
+ if self.db: self.db.commit()
+ return ret
+ finally:
+ if self.db:
+ self.db.close()
if __name__ == '__main__':
tool = AdminTool()
sys.exit(tool.main())
if __name__ == '__main__':
tool = AdminTool()
sys.exit(tool.main())
-#
-# $Log: not supported by cvs2svn $
-# Revision 1.15 2002/06/17 23:14:44 richard
-# . #569415 ] {version}
-#
-# Revision 1.14 2002/06/11 06:41:50 richard
-# Removed prompt for admin email in initialisation.
-#
-# Revision 1.13 2002/05/30 23:58:14 richard
-# oops
-#
-# Revision 1.12 2002/05/26 09:04:42 richard
-# out by one in the init args
-#
-# Revision 1.11 2002/05/23 01:14:20 richard
-# . split instance initialisation into two steps, allowing config changes
-# before the database is initialised.
-#
-# Revision 1.10 2002/04/27 10:07:23 richard
-# minor fix to error message
-#
-# Revision 1.9 2002/03/12 22:51:47 richard
-# . #527416 ] roundup-admin uses undefined value
-# . #527503 ] unfriendly init blowup when parent dir
-# (also handles UsageError correctly now in init)
-#
-# Revision 1.8 2002/02/27 03:28:21 richard
-# Ran it through pychecker, made fixes
-#
-# Revision 1.7 2002/02/20 05:04:32 richard
-# Wasn't handling the cvs parser feeding properly.
-#
-# Revision 1.6 2002/01/23 07:27:19 grubert
-# . allow abbreviation of "help" in admin tool too.
-#
-# Revision 1.5 2002/01/21 16:33:19 rochecompaan
-# You can now use the roundup-admin tool to pack the database
-#
-# Revision 1.4 2002/01/14 06:51:09 richard
-# . #503164 ] create and passwords
-#
-# Revision 1.3 2002/01/08 05:26:32 rochecompaan
-# Missing "self" in props_from_args
-#
-# Revision 1.2 2002/01/07 10:41:44 richard
-# #500140 ] AdminTool.get_class() returns nothing
-#
-# Revision 1.1 2002/01/05 02:11:22 richard
-# I18N'ed roundup admin - and split the code off into a module so it can be used
-# elsewhere.
-# Big issue with this is the doc strings - that's the help. We're probably going to
-# have to switch to not use docstrings, which will suck a little :(
-#
-#
-#
# vim: set filetype=python ts=4 sw=4 et si
# vim: set filetype=python ts=4 sw=4 et si