summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 0cb44cf)
raw | patch | inline | side by side (parent: 0cb44cf)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Wed, 26 Mar 2003 11:02:28 +0000 (11:02 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Wed, 26 Mar 2003 11:02:28 +0000 (11:02 +0000) |
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1638 57a73879-2fb5-44c3-a270-3262357dd7e2
CHANGES.txt | patch | blob | history | |
doc/user_guide.txt | patch | blob | history | |
roundup/admin.py | patch | blob | history |
diff --git a/CHANGES.txt b/CHANGES.txt
index de22291c872a330ad90e39bab7ab385ba285df18..5e24dfd83e8563cbac2b9ac4d3ff891fc92553a7 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
- implemented ability to search for multilink properties with no value
- Class.find() may now find unset Links (sf bug 700620)
- more flexibility in classhelp link labelling (sf feature 608204)
+- added command-line functionality for roundup-adming (sf feature 687664)
Fixed:
diff --git a/doc/user_guide.txt b/doc/user_guide.txt
index 0117c550134cfe77c6126c232cb62f1db9c85554..f8395dc35f2a52d32aece4a6a75f67007fcceb07 100644 (file)
--- a/doc/user_guide.txt
+++ b/doc/user_guide.txt
User Guide
==========
-:Version: $Revision: 1.15 $
+:Version: $Revision: 1.16 $
.. contents::
The basic usage is::
- Help:
- roundup-admin -h
- roundup-admin help -- this help
- roundup-admin help <command> -- command-specific help
- roundup-admin help all -- all available help
+ Usage: 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
+ -d -- print full designators not just class id numbers
+ -c -- when outputting lists of data, comma-separate them.
+ Same as '-S ","'.
+ -S <string> -- when outputting lists of data, string-separate them
+ -s -- when outputting lists of data, space-separate them.
+ Same as '-S " "'.
+
+ Only one of -s, -c or -S can be specified.
+
+ Help:
+ roundup-admin -h
+ roundup-admin help -- this help
+ roundup-admin help <command> -- command-specific help
+ roundup-admin help all -- all available help
- Commands:
+ Commands:
commit
create classname property=value ...
- display designator
+ display designator[,designator]*
export [class[,class]] export_dir
find classname propname=value ...
get property designator[,designator]*
retire designator[,designator]*
rollback
security [Role name]
- set designator[,designator]* propname=value ...
+ set [items] property=value property=value ...
specification classname
table classname [property[,property]*]
+ Commands may be abbreviated as long as the abbreviation matches only one
+ command, e.g. l == li == lis == list.
-Commands may be abbreviated as long as the abbreviation matches only one
-command, e.g. l == li == lis == list.
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
the command line as "``-i tracker``".
A designator is a classname and an itemid concatenated, eg. bug1, user10, ...
-Property values are represented as strings in command arguments and in the printed
-results:
+Property values are represented as strings in command arguments and in the
+printed results:
- Strings are, well, strings.
- Password values will display as their encoded value.
If either the name or password is not supplied, they are obtained from the
command-line.
+Using with the shell
+~~~~~~~~~~~~~~~~~~~~
+
+With version 0.6.0 or newer of roundup which supports: multiple
+designators to display and the -d, -S and -s flags.
+
+To find all messages regarding chatting issues that
+contain the word "spam", for example, you could execute the
+following command from the directory where the database
+dumps its files::
+
+ shell% for issue in `roundup-admin -ds find issue status=chatting`; do
+ > grep -l spam `roundup-admin -ds ' ' get messages $issue`
+ > done
+ msg23
+ msg49
+ msg50
+ msg61
+ shell%
+
+Or, using the -dc option, this can be written as a single command::
+
+ shell% grep -l spam `roundup get messages \
+ \`roundup -dc find issue status=chatting\``
+ msg23
+ msg49
+ msg50
+ msg61
+ shell%
+
+You can also display issue contents::
+
+ shell% roundup-admin display `roundup-admin -dc get messages \
+ issue3,issue1`
+ files: []
+ inreplyto: None
+ recipients: []
+ author: 1
+ date: 2003-02-16.21:23:03
+ messageid: None
+ summary: jkdskldjf
+ files: []
+ inreplyto: None
+ recipients: []
+ author: 1
+ date: 2003-02-15.01:59:11
+ messageid: None
+ summary: jlkfjadsf
+
+or status::
+
+ shell% roundup-admin get name `/tools/roundup/bin/roundup-admin \
+ -dc -i /var/roundup/sysadmin get status issue3,issue1`
+ unread
+ deferred
+
+or status on a single line::
+
+ shell% echo `roundup-admin get name \`/tools/roundup/bin/roundup-admin \
+ -dc -i /var/roundup/sysadmin get status issue3,issue1\``
+ unread deferred
+
+which is the same as::
+
+ shell% roundup-admin -s get name `/tools/roundup/bin/roundup-admin \
+ -dc -i /var/roundup/sysadmin get status issue3,issue1`
+ unread deferred
+
+Also the tautological::
+
+ shell% roundup-admin get name \
+ `roundup-admin -dc get status \`roundup-admin -dc find issue \
+ status=chatting\``
+ chatting
+ chatting
+Remember the roundup commands that accept multiple designators accept
+them ',' separated so using '-dc' is almost always required.
-----------------
diff --git a/roundup/admin.py b/roundup/admin.py
index f09d82e316bd6a6f9c58f974a2dbc7de4d87eec3..ebc22c8cb553d86ab295a2d60be2c891bd2af518 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.48 2003-03-26 10:43:58 richard Exp $
+# $Id: admin.py,v 1.49 2003-03-26 11:02:28 richard Exp $
'''Administration commands for maintaining Roundup trackers.
'''
''' Display a simple usage message.
'''
if message:
- message = _('Problem: %(message)s)\n\n')%locals()
- print _('''%(message)sUsage: roundup-admin [options] <command> <arguments>
+ message = _('Problem: %(message)s\n\n')%locals()
+ 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
+ -d -- print full designators not just class id numbers
+ -c -- when outputting lists of data, comma-separate them.
+ Same as '-S ","'.
+ -S <string> -- when outputting lists of data, string-separate them
+ -s -- when outputting lists of data, space-separate them.
+ Same as '-S " "'.
+
+ Only one of -s, -c or -S can be specified.
Help:
roundup-admin -h
# get the class
cl = self.get_class(classname)
try:
- if self.comma_sep:
- l.append(cl.get(nodeid, propname))
+ id=[]
+ if self.separator:
+ if self.print_designator:
+ # see if property is a link or multilink for
+ # which getting a desginator make sense.
+ # Algorithm: Get the properties of the
+ # current designator's class. (cl.getprops)
+ # get the property object for the property the
+ # user requested (properties[propname])
+ # verify its type (isinstance...)
+ # raise error if not link/multilink
+ # get class name for link/multilink property
+ # do the get on the designators
+ # append the new designators
+ # print
+ properties = cl.getprops()
+ 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
+ propclassname = self.db.getclass(property.classname).classname
+ id = cl.get(nodeid, propname)
+ for i in id:
+ l.append(propclassname + i)
+ else:
+ id = cl.get(nodeid, propname)
+ for i in id:
+ l.append(i)
else:
- print cl.get(nodeid, propname)
+ if self.print_designator:
+ properties = cl.getprops()
+ 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
+ propclassname = self.db.getclass(property.classname).classname
+ id = cl.get(nodeid, propname)
+ for i in id:
+ print propclassname + i
+ else:
+ print cl.get(nodeid, propname)
except IndexError:
raise UsageError, _('no such %(classname)s node "%(nodeid)s"')%locals()
except KeyError:
raise UsageError, _('no such %(classname)s property '
'"%(propname)s"')%locals()
- if self.comma_sep:
- print ','.join(l)
+ if self.separator:
+ print self.separator.join(l)
+
return 0
'''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
+ The items may be specified as a class or as a comma-separated
list of item designators (ie "designator[,designator,...]").
This command sets the properties to the values for all designators
# now do the find
try:
- if self.comma_sep:
- print ','.join(apply(cl.find, (), props))
+ id = []
+ designator = []
+ if self.separator:
+ if self.print_designator:
+ id=apply(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))
+
else:
- print apply(cl.find, (), props)
+ if self.print_designator:
+ id=apply(cl.find, (), props)
+ for i in id:
+ designator.append(classname + i)
+ print designator
+ else:
+ print apply(cl.find, (), props)
except KeyError:
raise UsageError, _('%(classname)s has no property '
'"%(propname)s"')%locals()
print _('%(key)s: %(value)s')%locals()
def do_display(self, args):
- '''Usage: display designator
- Show the property values for the given node.
+ '''Usage: display designator[,designator]*
+ Show the property values for the given node(s).
This lists the properties and their associated values for the given
node.
raise UsageError, _('Not enough arguments supplied')
# decode the node designator
- try:
- classname, nodeid = hyperdb.splitDesignator(args[0])
- except hyperdb.DesignatorError, message:
- raise UsageError, message
+ for designator in args[0].split(','):
+ try:
+ classname, nodeid = hyperdb.splitDesignator(designator)
+ except hyperdb.DesignatorError, message:
+ raise UsageError, message
- # get the class
- cl = self.get_class(classname)
+ # get the class
+ cl = self.get_class(classname)
- # display the values
- for key in cl.properties.keys():
- value = cl.get(nodeid, key)
- print _('%(key)s: %(value)s')%locals()
+ # display the values
+ for key in cl.properties.keys():
+ value = cl.get(nodeid, key)
+ print _('%(key)s: %(value)s')%locals()
def do_create(self, args, pwre = re.compile(r'{(\w+)}(.+)')):
'''Usage: create classname property=value ...
specified, the "label" property is used. The label property is tried
in order: the key, "name", "title" and then the first property,
alphabetically.
+
+ 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')
if len(args) < 1:
raise UsageError, _('Not enough arguments supplied')
classname = args[0]
else:
propname = cl.labelprop()
- if self.comma_sep:
- print ','.join(cl.list())
+ if self.separator:
+ if len(args) == 2:
+ # 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()
+ print self.separator.join(proplist)
+ else:
+ # create a list of index id's since user didn't specify
+ # otherwise
+ print self.separator.join(cl.list())
else:
for nodeid in cl.list():
try:
def main(self):
try:
- opts, args = getopt.getopt(sys.argv[1:], 'i:u:hc')
+ opts, args = getopt.getopt(sys.argv[1:], 'i:u:hcdsS:')
except getopt.GetoptError, e:
self.usage(str(e))
return 1
name = l[0]
if len(l) > 1:
password = l[1]
- self.comma_sep = 0
+ self.separator = None
+ self.print_designator = 0
for opt, arg in opts:
if opt == '-h':
self.usage()
if opt == '-i':
self.tracker_home = arg
if opt == '-c':
- self.comma_sep = 1
+ if self.separator != None:
+ self.usage('Only one of -c, -S and -s may be specified')
+ return 1
+ self.separator = ','
+ if opt == '-S':
+ if self.separator != None:
+ self.usage('Only one of -c, -S and -s may be specified')
+ return 1
+ self.separator = arg
+ if opt == '-s':
+ if self.separator != None:
+ self.usage('Only one of -c, -S and -s may be specified')
+ return 1
+ self.separator = ' '
+ if opt == '-d':
+ self.print_designator = 1
# if no command - go interactive
# wrap in a try/finally so we always close off the db