diff --git a/roundup/admin.py b/roundup/admin.py
index 51695e2bd46b44789f4bb88ba23532d631070ab5..1a4e241df277116f00424244eefa5658264f3a4c 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.110 2008-02-07 03:28:33 richard Exp $
-'''Administration commands for maintaining Roundup trackers.
-'''
+"""Administration commands for maintaining Roundup trackers.
+"""
__docformat__ = 'restructuredtext'
-import csv, getopt, getpass, os, re, shutil, sys, UserDict
+import csv, getopt, getpass, os, re, shutil, sys, UserDict, operator
from roundup import date, hyperdb, roundupdb, init, password, token
from roundup import __version__ as roundup_version
import roundup.instance
from roundup.configuration import CoreConfig
from roundup.i18n import _
+from roundup.exceptions import UsageError
class CommandDict(UserDict.UserDict):
- '''Simple dictionary that lets us do lookups using partial keys.
+ """Simple dictionary that lets us do lookups using partial keys.
Original code submitted by Engelbert Gruber.
- '''
+ """
_marker = []
def get(self, key, default=_marker):
- if self.data.has_key(key):
+ if key in self.data:
return [(key, self.data[key])]
- keylist = self.data.keys()
- keylist.sort()
+ keylist = sorted(self.data)
l = []
for ki in keylist:
if ki.startswith(key):
l.append((ki, self.data[ki]))
if not l and default is self._marker:
- raise KeyError, key
+ raise KeyError(key)
return l
-class UsageError(ValueError):
- pass
-
class AdminTool:
- ''' A collection of methods used in maintaining Roundup trackers.
+ """ 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
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():
+ for k in AdminTool.__dict__:
if k[:3] == 'do_':
self.commands[k[3:]] = getattr(self, k)
self.help = {}
- for k in AdminTool.__dict__.keys():
+ for k in AdminTool.__dict__:
if k[:5] == 'help_':
self.help[k[5:]] = getattr(self, k)
self.tracker_home = ''
self.db_uncommitted = False
def get_class(self, classname):
- '''Get the class - raise an exception if it doesn't exist.
- '''
+ """Get the class - raise an exception if it doesn't exist.
+ """
try:
return self.db.getclass(classname)
except KeyError:
- raise UsageError, _('no such class "%(classname)s"')%locals()
+ raise UsageError(_('no such class "%(classname)s"')%locals())
def props_from_args(self, args):
- ''' Produce a dictionary of prop: value from the args list.
+ """ 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:
- raise UsageError, _('argument "%(arg)s" not propname=value'
- )%locals()
+ raise UsageError(_('argument "%(arg)s" not propname=value'
+ )%locals())
l = arg.split('=')
if len(l) < 2:
- raise UsageError, _('argument "%(arg)s" not propname=value'
- )%locals()
+ raise UsageError(_('argument "%(arg)s" not propname=value'
+ )%locals())
key, value = l[0], '='.join(l[1:])
if value:
props[key] = value
return props
def usage(self, message=''):
- ''' Display a simple usage message.
- '''
+ """ Display a simple usage message.
+ """
if message:
message = _('Problem: %(message)s\n\n')%locals()
- print _('''%(message)sUsage: roundup-admin [options] [<command> <arguments>]
+ print _("""%(message)sUsage: roundup-admin [options] [<command> <arguments>]
Options:
-i instance home -- specify the issue tracker "home directory" to administer
roundup-admin help -- this help
roundup-admin help <command> -- command-specific help
roundup-admin help all -- all available help
-''')%locals()
+""")%locals()
self.help_commands()
def help_commands(self):
- ''' List the commands available with their help summary.
- '''
+ """List the commands available with their help summary.
+ """
print _('Commands:'),
commands = ['']
- for command in self.commands.values():
+ for command in self.commands.itervalues():
h = _(command.__doc__).split('\n')[0]
commands.append(' '+h[7:])
commands.sort()
print
def help_commands_html(self, indent_re=re.compile(r'^(\s+)\S+')):
- ''' Produce an HTML command list.
- '''
- commands = self.commands.values()
- def sortfun(a, b):
- return cmp(a.__name__, b.__name__)
- commands.sort(sortfun)
+ """ Produce an HTML command list.
+ """
+ commands = sorted(self.commands.itervalues(),
+ operator.attrgetter('__name__'))
for command in commands:
h = _(command.__doc__).split('\n')
name = command.__name__[3:]
usage = h[0]
- print '''
+ print """
<tr><td valign=top><strong>%(name)s</strong></td>
<td><tt>%(usage)s</tt><p>
-<pre>''' % locals()
+<pre>""" % locals()
indent = indent_re.match(h[3])
if indent: indent = len(indent.group(1))
for line in h[3:]:
print '</pre></td></tr>\n'
def help_all(self):
- print _('''
+ print _("""
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
"." means "right now"
Command help:
-''')
+""")
for name, command in self.commands.items():
print _('%s:')%name
print ' ', _(command.__doc__)
def do_help(self, args, nl_re=re.compile('[\r\n]'),
indent_re=re.compile(r'^(\s+)\S+')):
- ""'''Usage: help topic
+ ''"""Usage: help topic
Give help about topic.
commands -- list commands
<command> -- help specific to a command
initopts -- init command options
all -- all available help
- '''
+ """
if len(args)>0:
topic = args[0]
else:
# try help_ methods
- if self.help.has_key(topic):
+ if topic in self.help:
self.help[topic]()
return 0
return 0
def listTemplates(self):
- ''' List all the available templates.
+ """ List all the available templates.
Look in the following places, where the later rules take precedence:
this is for when someone unpacks a 3rd-party template
5. <current working dir>
this is for someone who "cd"s to the 3rd-party template dir
- '''
+ """
# OK, try <prefix>/share/roundup/templates
# and <egg-directory>/share/roundup/templates
# -- this module (roundup.admin) will be installed in something
def help_initopts(self):
templates = self.listTemplates()
- print _('Templates:'), ', '.join(templates.keys())
+ print _('Templates:'), ', '.join(templates)
import roundup.backends
backends = roundup.backends.list_backends()
print _('Back ends:'), ', '.join(backends)
def do_install(self, tracker_home, args):
- ""'''Usage: install [template [backend [key=val[,key=val]]]]
+ ''"""Usage: install [template [backend [key=val[,key=val]]]]
Install a new Roundup tracker.
The command will prompt for the tracker home directory
the tracker's dbinit.py module init() function.
See also initopts help.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
# make sure the tracker home can be created
tracker_home = os.path.abspath(tracker_home)
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()
+ raise UsageError(_('Instance home parent directory "%(parent)s"'
+ ' does not exist')%locals())
config_ini_file = os.path.join(tracker_home, CoreConfig.INI_FILE)
# check for both old- and new-style configs
- if filter(os.path.exists, [config_ini_file,
- os.path.join(tracker_home, 'config.py')]):
+ if list(filter(os.path.exists, [config_ini_file,
+ os.path.join(tracker_home, 'config.py')])):
ok = raw_input(_(
"""WARNING: There appears to be a tracker in "%(tracker_home)s"!
If you re-install it, you will lose all the data!
# select template
templates = self.listTemplates()
template = len(args) > 1 and args[1] or ''
- if not templates.has_key(template):
- print _('Templates:'), ', '.join(templates.keys())
- while not templates.has_key(template):
+ if template not in templates:
+ print _('Templates:'), ', '.join(templates)
+ while template not in templates:
template = raw_input(_('Select template [classic]: ')).strip()
if not template:
template = 'classic'
need_set = CoreConfig(tracker_home)._get_unset_options()
if need_set:
print _(" ... at a minimum, you must set following options:")
- for section, options in need_set.items():
- print " [%s]: %s" % (section, ", ".join(options))
+ for section in need_set:
+ print " [%s]: %s" % (section, ", ".join(need_set[section]))
# note about schema modifications
print _("""
return 0
def do_genconfig(self, args):
- ""'''Usage: genconfig <filename>
+ ''"""Usage: genconfig <filename>
Generate a new tracker config file (ini style) with default values
in <filename>.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
config = CoreConfig()
config.save(args[0])
def do_initialise(self, tracker_home, args):
- ""'''Usage: initialise [adminpw]
+ ''"""Usage: initialise [adminpw]
Initialise a new Roundup tracker.
The administrator details will be set at this step.
Execute the tracker's initialisation function dbinit.init()
- '''
+ """
# password
if len(args) > 1:
adminpw = args[1]
# 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())
try:
tracker = roundup.instance.open(tracker_home)
except roundup.instance.TrackerError:
- raise UsageError, _('Instance has not been installed')%locals()
+ raise UsageError(_('Instance has not been installed')%locals())
# is there already a database?
if tracker.exists():
tracker.nuke()
# re-write the backend select file
- init.write_select_db(tracker_home, backend)
+ init.write_select_db(tracker_home, backend, tracker.config.DATABASE)
# GO
- tracker.init(password.Password(adminpw))
+ tracker.init(password.Password(adminpw, config=tracker.config))
return 0
def do_get(self, args):
- ""'''Usage: get property designator[,designator]*
+ ''"""Usage: get property designator[,designator]*
Get the given property of one or more designator(s).
+ A designator is a classname and a nodeid concatenated,
+ eg. bug1, user10, ...
+
Retrieves the property value of the nodes specified
by the designators.
- '''
+ """
if len(args) < 2:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
propname = args[0]
designators = args[1].split(',')
l = []
try:
classname, nodeid = hyperdb.splitDesignator(designator)
except hyperdb.DesignatorError, message:
- raise UsageError, message
+ raise UsageError(message)
# get the class
cl = self.get_class(classname)
property = properties[propname]
if not (isinstance(property, hyperdb.Multilink) or
isinstance(property, hyperdb.Link)):
- raise UsageError, _('property %s is not of type Multilink or Link so -d flag does not apply.')%propname
+ raise UsageError(_('property %s is not of type'
+ ' Multilink or Link so -d flag does not '
+ 'apply.')%propname)
propclassname = self.db.getclass(property.classname).classname
id = cl.get(nodeid, propname)
for i in id:
property = properties[propname]
if not (isinstance(property, hyperdb.Multilink) or
isinstance(property, hyperdb.Link)):
- raise UsageError, _('property %s is not of type Multilink or Link so -d flag does not apply.')%propname
+ raise UsageError(_('property %s is not of type'
+ ' Multilink or Link so -d flag does not '
+ 'apply.')%propname)
propclassname = self.db.getclass(property.classname).classname
id = cl.get(nodeid, propname)
for i in id:
else:
print cl.get(nodeid, propname)
except IndexError:
- raise UsageError, _('no such %(classname)s node "%(nodeid)s"')%locals()
+ raise UsageError(_('no such %(classname)s node '
+ '"%(nodeid)s"')%locals())
except KeyError:
- raise UsageError, _('no such %(classname)s property '
- '"%(propname)s"')%locals()
+ raise UsageError(_('no such %(classname)s property '
+ '"%(propname)s"')%locals())
if self.separator:
print self.separator.join(l)
def do_set(self, args):
- ""'''Usage: set items property=value property=value ...
+ ''"""Usage: set items property=value property=value ...
Set the given properties of one or more items(s).
The items are specified as a class or as a comma-separated
list of item designators (ie "designator[,designator,...]").
+ A designator is a classname and a nodeid concatenated,
+ eg. bug1, user10, ...
+
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 the property is a multilink, you specify the linked
ids for the multilink as comma-separated numbers (ie "1,2,3").
- '''
+ """
if len(args) < 2:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
from roundup import hyperdb
designators = args[0].split(',')
try:
designators = [hyperdb.splitDesignator(x) for x in designators]
except hyperdb.DesignatorError, message:
- raise UsageError, message
+ raise UsageError(message)
# get the props from the args
props = self.props_from_args(args[1:])
props[key] = hyperdb.rawToHyperdb(self.db, cl, itemid,
key, value)
except hyperdb.HyperdbValueError, message:
- raise UsageError, message
+ raise UsageError(message)
# try the set
try:
- apply(cl.set, (itemid, ), props)
+ cl.set(itemid, **props)
except (TypeError, IndexError, ValueError), message:
import traceback; traceback.print_exc()
- raise UsageError, message
+ raise UsageError(message)
self.db_uncommitted = True
return 0
def do_find(self, args):
- ""'''Usage: find classname propname=value ...
+ ''"""Usage: find classname propname=value ...
Find the nodes of the given class with a given link property value.
Find the nodes of the given class with a given link property value.
The value may be either the nodeid of the linked node, or its key
value.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
classname = args[0]
# get the class
cl = self.get_class(classname)
props = self.props_from_args(args[1:])
# convert the user-input value to a value used for find()
- for propname, value in props.items():
+ for propname, value in props.iteritems():
if ',' in value:
values = value.split(',')
else:
designator = []
if self.separator:
if self.print_designator:
- id=apply(cl.find, (), props)
+ id = cl.find(**props)
for i in id:
designator.append(classname + i)
print self.separator.join(designator)
else:
- print self.separator.join(apply(cl.find, (), props))
+ print self.separator.join(cl.find(**props))
else:
if self.print_designator:
- id=apply(cl.find, (), props)
+ id = cl.find(**props)
for i in id:
designator.append(classname + i)
print designator
else:
- print apply(cl.find, (), props)
+ print cl.find(**props)
except KeyError:
- raise UsageError, _('%(classname)s has no property '
- '"%(propname)s"')%locals()
+ raise UsageError(_('%(classname)s has no property '
+ '"%(propname)s"')%locals())
except (ValueError, TypeError), message:
- raise UsageError, message
+ raise UsageError(message)
return 0
def do_specification(self, args):
- ""'''Usage: specification classname
+ ''"""Usage: specification classname
Show the properties for a classname.
This lists the properties for a given class.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
classname = args[0]
# get the class
cl = self.get_class(classname)
# get the key property
keyprop = cl.getkey()
- for key, value in cl.properties.items():
+ for key in cl.properties:
+ value = cl.properties[key]
if keyprop == key:
print _('%(key)s: %(value)s (key property)')%locals()
else:
print _('%(key)s: %(value)s')%locals()
def do_display(self, args):
- ""'''Usage: display designator[,designator]*
+ ''"""Usage: display designator[,designator]*
Show the property values for the given node(s).
+ A designator is a classname and a nodeid concatenated,
+ eg. bug1, user10, ...
+
This lists the properties and their associated values for the given
node.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
# decode the node designator
for designator in args[0].split(','):
try:
classname, nodeid = hyperdb.splitDesignator(designator)
except hyperdb.DesignatorError, message:
- raise UsageError, message
+ raise UsageError(message)
# get the class
cl = self.get_class(classname)
# display the values
- keys = cl.properties.keys()
- keys.sort()
+ keys = sorted(cl.properties)
for key in keys:
value = cl.get(nodeid, key)
print _('%(key)s: %(value)s')%locals()
def do_create(self, args):
- ""'''Usage: create classname property=value ...
+ ''"""Usage: create classname property=value ...
Create a new entry of a given class.
This creates a new entry of the given class using the property
name=value arguments provided on the command line after the "create"
command.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
from roundup import hyperdb
classname = args[0]
properties = cl.getprops(protected = 0)
if len(args) == 1:
# ask for the properties
- for key, value in properties.items():
+ for key in properties:
if key == 'id': continue
+ value = properties[key]
name = value.__class__.__name__
if isinstance(value , hyperdb.Password):
again = None
props = self.props_from_args(args[1:])
# convert types
- for propname, value in props.items():
+ for propname in props:
try:
props[propname] = hyperdb.rawToHyperdb(self.db, cl, None,
- propname, value)
+ propname, props[propname])
except hyperdb.HyperdbValueError, message:
- raise UsageError, message
+ raise UsageError(message)
# check for the key property
propname = cl.getkey()
- if propname and not props.has_key(propname):
- raise UsageError, _('you must provide the "%(propname)s" '
- 'property.')%locals()
+ if propname and propname not in props:
+ raise UsageError(_('you must provide the "%(propname)s" '
+ 'property.')%locals())
# do the actual create
try:
- print apply(cl.create, (), props)
+ print cl.create(**props)
except (TypeError, IndexError, ValueError), message:
- raise UsageError, message
+ raise UsageError(message)
self.db_uncommitted = True
return 0
def do_list(self, args):
- ""'''Usage: list classname [property]
+ ''"""Usage: list classname [property]
List the instances of a class.
Lists all instances of the given class. If the property is not
With -c, -S or -s print a list of item id's if no property
specified. If property specified, print list of that property
for every class instance.
- '''
+ """
if len(args) > 2:
- raise UsageError, _('Too many arguments supplied')
+ raise UsageError(_('Too many arguments supplied'))
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
classname = args[0]
-
+
# get the class
cl = self.get_class(classname)
if self.separator:
if len(args) == 2:
- # create a list of propnames since user specified propname
+ # create a list of propnames since user specified propname
proplist=[]
for nodeid in cl.list():
try:
proplist.append(cl.get(nodeid, propname))
except KeyError:
- raise UsageError, _('%(classname)s has no property '
- '"%(propname)s"')%locals()
+ raise UsageError(_('%(classname)s has no property '
+ '"%(propname)s"')%locals())
print self.separator.join(proplist)
else:
# create a list of index id's since user didn't specify
try:
value = cl.get(nodeid, propname)
except KeyError:
- raise UsageError, _('%(classname)s has no property '
- '"%(propname)s"')%locals()
+ raise UsageError(_('%(classname)s has no property '
+ '"%(propname)s"')%locals())
print _('%(nodeid)4s: %(value)s')%locals()
return 0
def do_table(self, args):
- ""'''Usage: table classname [property[,property]*]
+ ''"""Usage: table classname [property[,property]*]
List the instances of a class in tabular form.
Lists all instances of the given class. If the properties are not
4 feat
will result in a the 4 character wide "Name" column.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
classname = args[0]
# get the class
try:
propname, width = spec.split(':')
except (ValueError, TypeError):
- raise UsageError, _('"%(spec)s" not name:width')%locals()
+ raise UsageError(_('"%(spec)s" not '
+ 'name:width')%locals())
else:
propname = spec
- if not all_props.has_key(propname):
- raise UsageError, _('%(classname)s has no property '
- '"%(propname)s"')%locals()
+ if propname not in all_props:
+ raise UsageError(_('%(classname)s has no property '
+ '"%(propname)s"')%locals())
else:
- prop_names = cl.getprops().keys()
+ prop_names = cl.getprops()
# now figure column widths
props = []
return 0
def do_history(self, args):
- ""'''Usage: history designator
+ ''"""Usage: history designator
Show the history entries of a designator.
+ A designator is a classname and a nodeid concatenated,
+ eg. bug1, user10, ...
+
Lists the journal entries for the node identified by the designator.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
try:
classname, nodeid = hyperdb.splitDesignator(args[0])
except hyperdb.DesignatorError, message:
- raise UsageError, message
+ raise UsageError(message)
try:
print self.db.getclass(classname).history(nodeid)
except KeyError:
- raise UsageError, _('no such class "%(classname)s"')%locals()
+ raise UsageError(_('no such class "%(classname)s"')%locals())
except IndexError:
- raise UsageError, _('no such %(classname)s node "%(nodeid)s"')%locals()
+ raise UsageError(_('no such %(classname)s node '
+ '"%(nodeid)s"')%locals())
return 0
def do_commit(self, args):
- ""'''Usage: commit
+ ''"""Usage: commit
Commit changes made to the database during an interactive session.
The changes made during an interactive session are not
One-off commands on the command-line are automatically committed if
they are successful.
- '''
+ """
self.db.commit()
self.db_uncommitted = False
return 0
def do_rollback(self, args):
- ""'''Usage: rollback
+ ''"""Usage: rollback
Undo all changes that are pending commit to the database.
The changes made during an interactive session are not
automatically written to the database - they must be committed
manually. This command undoes all those changes, so a commit
immediately after would make no changes to the database.
- '''
+ """
self.db.rollback()
self.db_uncommitted = False
return 0
def do_retire(self, args):
- ""'''Usage: retire designator[,designator]*
+ ''"""Usage: retire designator[,designator]*
Retire the node specified by designator.
+ A designator is a classname and a nodeid concatenated,
+ eg. bug1, user10, ...
+
This action indicates that a particular node is not to be retrieved
by the list or find commands, and its key value may be re-used.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
designators = args[0].split(',')
for designator in designators:
try:
classname, nodeid = hyperdb.splitDesignator(designator)
except hyperdb.DesignatorError, message:
- raise UsageError, message
+ raise UsageError(message)
try:
self.db.getclass(classname).retire(nodeid)
except KeyError:
- raise UsageError, _('no such class "%(classname)s"')%locals()
+ raise UsageError(_('no such class "%(classname)s"')%locals())
except IndexError:
- raise UsageError, _('no such %(classname)s node "%(nodeid)s"')%locals()
+ raise UsageError(_('no such %(classname)s node '
+ '"%(nodeid)s"')%locals())
self.db_uncommitted = True
return 0
def do_restore(self, args):
- ""'''Usage: restore designator[,designator]*
+ ''"""Usage: restore designator[,designator]*
Restore the retired node specified by designator.
+ A designator is a classname and a nodeid concatenated,
+ eg. bug1, user10, ...
+
The given nodes will become available for users again.
- '''
+ """
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
designators = args[0].split(',')
for designator in designators:
try:
classname, nodeid = hyperdb.splitDesignator(designator)
except hyperdb.DesignatorError, message:
- raise UsageError, message
+ raise UsageError(message)
try:
self.db.getclass(classname).restore(nodeid)
except KeyError:
- raise UsageError, _('no such class "%(classname)s"')%locals()
+ raise UsageError(_('no such class "%(classname)s"')%locals())
except IndexError:
- raise UsageError, _('no such %(classname)s node "%(nodeid)s"')%locals()
+ raise UsageError(_('no such %(classname)s node '
+ '"%(nodeid)s"')%locals())
self.db_uncommitted = True
return 0
def do_export(self, args, export_files=True):
- ""'''Usage: export [[-]class[,class]] export_dir
+ ''"""Usage: export [[-]class[,class]] export_dir
Export the database to colon-separated-value files.
To exclude the files (e.g. for the msg or file class),
use the exporttables command.
This action exports the current data from the database into
colon-separated-value files that are placed in the nominated
destination directory.
- '''
+ """
# grab the directory to export to
if len(args) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
dir = args[-1]
# get the list of classes to export
if len(args) == 2:
if args[0].startswith('-'):
- classes = [ c for c in self.db.classes.keys()
+ classes = [ c for c in self.db.classes
if not c in args[0][1:].split(',') ]
else:
classes = args[0].split(',')
else:
- classes = self.db.classes.keys()
+ classes = self.db.classes
class colon_separated(csv.excel):
delimiter = ':'
if not os.path.exists(dir):
os.makedirs(dir)
+ # maximum csv field length exceeding configured size?
+ max_len = self.db.config.CSV_FIELD_SIZE
+
# do all the classes specified
for classname in classes:
cl = self.get_class(classname)
if self.verbose:
sys.stdout.write('\rExporting %s - %s'%(classname, nodeid))
sys.stdout.flush()
- writer.writerow(cl.export_list(propnames, nodeid))
+ node = cl.getnode(nodeid)
+ exp = cl.export_list(propnames, nodeid)
+ lensum = sum ([len (repr(node[p])) for p in propnames])
+ # for a safe upper bound of field length we add
+ # difference between CSV len and sum of all field lengths
+ d = sum ([len(x) for x in exp]) - lensum
+ assert (d > 0)
+ for p in propnames:
+ ll = len(repr(node[p])) + d
+ if ll > max_len:
+ max_len = ll
+ writer.writerow(exp)
if export_files and hasattr(cl, 'export_files'):
cl.export_files(dir, nodeid)
sys.stdout.write("\nExporting Journal for %s\n" % classname)
sys.stdout.flush()
journals = csv.writer(jf, colon_separated)
- map(journals.writerow, cl.export_journals())
+ for row in cl.export_journals():
+ journals.writerow(row)
jf.close()
+ if max_len > self.db.config.CSV_FIELD_SIZE:
+ print >> sys.stderr, \
+ "Warning: config csv_field_size should be at least %s"%max_len
return 0
def do_exporttables(self, args):
- ""'''Usage: exporttables [[-]class[,class]] export_dir
+ ''"""Usage: exporttables [[-]class[,class]] export_dir
Export the database to colon-separated-value files, excluding the
files below $TRACKER_HOME/db/files/ (which can be archived separately).
To include the files, use the export command.
This action exports the current data from the database into
colon-separated-value files that are placed in the nominated
destination directory.
- '''
+ """
return self.do_export(args, export_files=False)
def do_import(self, args):
- ""'''Usage: import import_dir
+ ''"""Usage: import import_dir
Import a database from the directory containing CSV files,
two per class to import.
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) < 1:
- raise UsageError, _('Not enough arguments supplied')
+ raise UsageError(_('Not enough arguments supplied'))
from roundup import hyperdb
+ if hasattr (csv, 'field_size_limit'):
+ csv.field_size_limit(self.db.config.CSV_FIELD_SIZE)
+
# directory to import from
dir = args[0]
if hasattr(cl, 'import_files'):
cl.import_files(dir, nodeid)
maxid = max(maxid, int(nodeid))
- print
+
+ # (print to sys.stdout here to allow tests to squash it .. ugh)
+ print >> sys.stdout
+
f.close()
# import the journals
cl.import_journals(reader)
f.close()
+ # (print to sys.stdout here to allow tests to squash it .. ugh)
+ print >> sys.stdout, 'setting', classname, maxid+1
+
# set the id counter
- print 'setting', classname, maxid+1
self.db.setid(classname, str(maxid+1))
self.db_uncommitted = True
return 0
def do_pack(self, args):
- ""'''Usage: pack period | date
+ ''"""Usage: pack period | date
Remove journal entries older than a period of time specified or
before a certain date.
Date format is "YYYY-MM-DD" eg:
2001-01-01
- '''
- if len(args) <> 1:
- raise UsageError, _('Not enough arguments supplied')
+ """
+ if len(args) != 1:
+ raise UsageError(_('Not enough arguments supplied'))
# are we dealing with a period or a date
value = args[0]
- date_re = re.compile(r'''
+ date_re = re.compile(r"""
(?P<date>\d\d\d\d-\d\d?-\d\d?)? # yyyy-mm-dd
(?P<period>(\d+y\s*)?(\d+m\s*)?(\d+d\s*)?)?
- ''', re.VERBOSE)
+ """, re.VERBOSE)
m = date_re.match(value)
if not m:
- raise ValueError, _('Invalid format')
+ raise ValueError(_('Invalid format'))
m = m.groupdict()
if m['period']:
pack_before = date.Date(". - %s"%value)
return 0
def do_reindex(self, args, desre=re.compile('([A-Za-z]+)([0-9]+)')):
- ""'''Usage: reindex [classname|designator]*
+ ''"""Usage: reindex [classname|designator]*
Re-generate a tracker's search indexes.
This will re-generate the search indexes for a tracker.
This will typically happen automatically.
- '''
+ """
if args:
for arg in args:
m = desre.match(arg)
try:
cl.index(m.group(2))
except IndexError:
- raise UsageError, _('no such item "%(designator)s"')%{
- 'designator': arg}
+ raise UsageError(_('no such item "%(designator)s"')%{
+ 'designator': arg})
else:
cl = self.get_class(arg)
self.db.reindex(arg)
return 0
def do_security(self, args):
- ""'''Usage: security [Role name]
+ ''"""Usage: security [Role name]
Display the Permissions available to one or all Roles.
- '''
+ """
if len(args) == 1:
role = args[0]
try:
print _('No such Role "%(role)s"')%locals()
return 1
else:
- roles = self.db.security.role.items()
+ roles = list(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()
def do_migrate(self, args):
- '''Usage: migrate
+ ''"""Usage: migrate
Update a tracker's database to be compatible with the Roundup
codebase.
It's safe to run this even if it's not required, so just get into
the habit.
- '''
+ """
if getattr(self.db, 'db_version_updated'):
print _('Tracker updated')
self.db_uncommitted = True
return 0
def run_command(self, args):
- '''Run a single command
- '''
+ """Run a single command
+ """
command = args[0]
# handle help now
return ret
def interactive(self):
- '''Run in an interactive mode
- '''
+ """Run in an interactive mode
+ """
print _('Roundup %s ready for input.\nType "help" for help.'
% roundup_version)
try:
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'):
+ if 'ROUNDUP_LOGIN' in os.environ:
l = os.environ['ROUNDUP_LOGIN'].split(':')
name = l[0]
if len(l) > 1: