Code

added command-line functionality for roundup-adming (sf feature 687664)
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Wed, 26 Mar 2003 11:02:28 +0000 (11:02 +0000)
committerrichard <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
doc/user_guide.txt
roundup/admin.py

index de22291c872a330ad90e39bab7ab385ba285df18..5e24dfd83e8563cbac2b9ac4d3ff891fc92553a7 100644 (file)
@@ -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:
index 0117c550134cfe77c6126c232cb62f1db9c85554..f8395dc35f2a52d32aece4a6a75f67007fcceb07 100644 (file)
@@ -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>             -- 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]*
@@ -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.
 
 -----------------
 
index f09d82e316bd6a6f9c58f974a2dbc7de4d87eec3..ebc22c8cb553d86ab295a2d60be2c891bd2af518 100644 (file)
@@ -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] <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
@@ -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