From c2e72d4de9e9f273265a66bb8b7fddfb1f7317a9 Mon Sep 17 00:00:00 2001 From: richard Date: Wed, 26 Mar 2003 11:02:28 +0000 Subject: [PATCH] added command-line functionality for roundup-adming (sf feature 687664) git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1638 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 1 + doc/user_guide.txt | 114 +++++++++++++++++++++++++++++---- roundup/admin.py | 156 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 227 insertions(+), 44 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index de22291..5e24dfd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -55,6 +55,7 @@ Feature: - 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 0117c55..f8395dc 100644 --- a/doc/user_guide.txt +++ b/doc/user_guide.txt @@ -2,7 +2,7 @@ User Guide ========== -:Version: $Revision: 1.15 $ +:Version: $Revision: 1.16 $ .. contents:: @@ -402,21 +402,30 @@ Command Line Tool The basic usage is:: - Help: - roundup-admin -h - roundup-admin help -- this help - roundup-admin help -- command-specific help - roundup-admin help all -- all available help + Usage: roundup-admin [options] [ ] 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 -- 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-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]* @@ -431,12 +440,12 @@ The basic usage is:: 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 @@ -446,8 +455,8 @@ It may be specified in the environment variable ``TRACKER_HOME`` or on 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. @@ -487,7 +496,84 @@ be specified as either "``name``" or "``name:password``". 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 f09d82e..ebc22c8 100644 --- a/roundup/admin.py +++ b/roundup/admin.py @@ -16,7 +16,7 @@ # 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. ''' @@ -110,13 +110,20 @@ class AdminTool: ''' Display a simple usage message. ''' if message: - message = _('Problem: %(message)s)\n\n')%locals() - print _('''%(message)sUsage: roundup-admin [options] + message = _('Problem: %(message)s\n\n')%locals() + print _('''%(message)sUsage: roundup-admin [options] [ ] 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 -- 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 @@ -422,17 +429,55 @@ Command help: # 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 @@ -440,7 +485,7 @@ Command help: '''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 @@ -566,10 +611,25 @@ Command help: # 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() @@ -598,8 +658,8 @@ Command help: 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. @@ -608,18 +668,19 @@ Command help: 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 ... @@ -721,7 +782,13 @@ Command help: 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] @@ -735,8 +802,21 @@ Command help: 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: @@ -1243,7 +1323,7 @@ Date format is "YYYY-MM-DD" eg: 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 @@ -1257,7 +1337,8 @@ Date format is "YYYY-MM-DD" eg: 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() @@ -1265,7 +1346,22 @@ Date format is "YYYY-MM-DD" eg: 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 -- 2.39.5