1- #! /usr /bin/python
1+ #! /Users/builder /bin/python
22#
33# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
44# This module is free software, and you may redistribute it and/or modify
1616# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1717# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1818#
19- # $Id: roundup-admin,v 1.32 2001-10-17 06:57:29 richard Exp $
19+ # $Id: roundup-admin,v 1.33 2001-10-17 23:13:19 richard Exp $
2020
2121import sys
2222if int (sys .version [0 ]) < 2 :
2828 import csv
2929except ImportError :
3030 csv = None
31- from roundup import date , roundupdb , init , password
31+ from roundup import date , hyperdb , roundupdb , init , password
3232import roundup .instance
3333
3434def usage (message = '' ):
@@ -337,7 +337,7 @@ def do_list(db, args, comma_sep=0):
337337 '''Usage: list classname [property]
338338 List the instances of a class.
339339
340- Lists all instances of the given class along . If the property is not
340+ Lists all instances of the given class. If the property is not
341341 specified, the "label" property is used. The label property is tried
342342 in order: the key, "name", "title" and then the first property,
343343 alphabetically.
@@ -356,6 +356,48 @@ def do_list(db, args, comma_sep=0):
356356 print "%4s: %s" % (nodeid , value )
357357 return 0
358358
359+ def do_table (db , args , comma_sep = None ):
360+ '''Usage: table classname [property[,property]*]
361+ List the instances of a class in tabular form.
362+
363+ Lists all instances of the given class. If the properties are not
364+ specified, all properties are displayed. By default, the column widths
365+ are the width of the property names. The width may be explicitly defined
366+ by defining the property as "name:width". For example::
367+ roundup> table priority id,name:10
368+ Id Name
369+ 1 fatal-bug
370+ 2 bug
371+ 3 usability
372+ 4 feature
373+ '''
374+ classname = args [0 ]
375+ cl = db .getclass (classname )
376+ if len (args ) > 1 :
377+ prop_names = args [1 ].split (',' )
378+ else :
379+ prop_names = cl .getprops ().keys ()
380+ props = []
381+ for name in prop_names :
382+ if ':' in name :
383+ name , width = name .split (':' )
384+ props .append ((name , int (width )))
385+ else :
386+ props .append ((name , len (name )))
387+
388+ print ' ' .join ([string .capitalize (name ) for name , width in props ])
389+ for nodeid in cl .list ():
390+ l = []
391+ for name , width in props :
392+ if name != 'id' :
393+ value = str (cl .get (nodeid , name ))
394+ else :
395+ value = str (nodeid )
396+ f = '%%-%ds' % width
397+ l .append (f % value [:width ])
398+ print ' ' .join (l )
399+ return 0
400+
359401def do_history (db , args , comma_sep = 0 ):
360402 '''Usage: history designator
361403 Show the history entries of a designator.
@@ -382,11 +424,10 @@ def do_retire(db, args, comma_sep=0):
382424
383425def do_export (db , args , comma_sep = 0 ):
384426 '''Usage: export class[,class] destination_dir
385- ** EXPERIMENTAL **
386- Export the database to CSV files by class in the given directory.
427+ Export the database to tab-separated-value files.
387428
388429 This action exports the current data from the database into
389- comma -separated files that are placed in the nominated destination
430+ tab -separated-value files that are placed in the nominated destination
390431 directory. The journals are not exported.
391432 '''
392433 if len (args ) < 2 :
@@ -397,35 +438,47 @@ def do_export(db, args, comma_sep=0):
397438
398439 # use the csv parser if we can - it's faster
399440 if csv is not None :
400- p = csv .parser ()
441+ p = csv .parser (field_sep = ':' )
401442
402443 # do all the classes specified
403444 for classname in classes :
404445 cl = db .getclass (classname )
405446 f = open (os .path .join (dir , classname + '.csv' ), 'w' )
406- f .write (string .join (cl .properties .keys (), ', ' ) + '\n ' )
447+ f .write (string .join (cl .properties .keys (), ': ' ) + '\n ' )
407448
408449 # all nodes for this class
450+ properties = cl .properties .items ()
409451 for nodeid in cl .list ():
452+ l = []
453+ for prop , type in properties :
454+ value = cl .get (nodeid , prop )
455+ # convert data where needed
456+ if isinstance (type , hyperdb .Date ):
457+ value = value .get_tuple ()
458+ elif isinstance (type , hyperdb .Interval ):
459+ value = value .get_tuple ()
460+ elif isinstance (type , hyperdb .Password ):
461+ value = str (value )
462+ l .append (repr (value ))
463+
464+ # now write
410465 if csv is not None :
411- s = p .join (map (str , cl .getnode (nodeid ).values (protected = 0 )))
412- f .write (s + '\n ' )
466+ f .write (p .join (l ) + '\n ' )
413467 else :
414- l = []
415468 # escape the individual entries to they're valid CSV
416- for entry in map (str , cl .getnode (nodeid ).values (protected = 0 )):
469+ m = []
470+ for entry in l :
417471 if '"' in entry :
418472 entry = '""' .join (entry .split ('"' ))
419- if ', ' in entry :
473+ if ': ' in entry :
420474 entry = '"%s"' % entry
421- l .append (entry )
422- f .write (', ' .join (l ) + '\n ' )
475+ m .append (entry )
476+ f .write (': ' .join (m ) + '\n ' )
423477 return 0
424478
425479def do_import (db , args , comma_sep = 0 ):
426480 '''Usage: import class file
427- ** EXPERIMENTAL **
428- Import the contents of the CSV file as new nodes for the given class.
481+ Import the contents of the tab-separated-value file.
429482
430483 The file must define the same properties as the class (including having
431484 a "header" line with those property names.) The new nodes are added to
@@ -434,7 +487,7 @@ def do_import(db, args, comma_sep=0):
434487 the old data.)
435488 '''
436489 if len (args ) < 2 :
437- print do_export .__doc__
490+ print do_import .__doc__
438491 return 1
439492 if csv is None :
440493 print 'Sorry, you need the csv module to use this function.'
@@ -446,15 +499,14 @@ def do_import(db, args, comma_sep=0):
446499 # ensure that the properties and the CSV file headings match
447500 cl = db .getclass (args [0 ])
448501 f = open (args [1 ])
449- p = csv .parser ()
502+ p = csv .parser (field_sep = ':' )
450503 file_props = p .parse (f .readline ())
451504 props = cl .properties .keys ()
452505 m = file_props [:]
453506 m .sort ()
454507 props .sort ()
455508 if m != props :
456- print do_export .__doc__
457- print "\n \n File doesn't define the same properties"
509+ print 'Import file doesn\' t define the same properties as "%s".' % args [0 ]
458510 return 1
459511
460512 # loop through the file and create a node for each entry
@@ -471,9 +523,12 @@ def do_import(db, args, comma_sep=0):
471523 # make the new node's property map
472524 d = {}
473525 for i in n :
474- value = l [i ]
526+ # Use eval to reverse the repr() used to output the CSV
527+ value = eval (l [i ])
528+ # Figure the property for this column
475529 key = file_props [i ]
476530 type = cl .properties [key ]
531+ # Convert for property type
477532 if isinstance (type , hyperdb .Date ):
478533 value = date .Date (value )
479534 elif isinstance (type , hyperdb .Interval ):
@@ -482,37 +537,13 @@ def do_import(db, args, comma_sep=0):
482537 pwd = password .Password ()
483538 pwd .unpack (value )
484539 value = pwd
485- elif isinstance (type , hyperdb .Multilink ):
486- value = value .split (',' )
487- d [key ] = value
540+ if value is not None :
541+ d [key ] = value
488542
489543 # and create the new node
490544 apply (cl .create , (), d )
491545 return 0
492546
493- def do_freshen (db , args , comma_sep = 0 ):
494- '''Usage: freshen
495- Freshen an existing instance. **DO NOT USE**
496-
497- This currently kills databases!!!!
498-
499- This action should generally not be used. It reads in an instance
500- database and writes it again. In the future, is may also update
501- instance code to account for changes in templates. It's probably wise
502- not to use it anyway. Until we're sure it won't break things...
503- '''
504- # for classname, cl in db.classes.items():
505- # properties = cl.properties.items()
506- # for nodeid in cl.list():
507- # node = {}
508- # for name, type in properties:
509- # isinstance( if type, hyperdb.Multilink):
510- # node[name] = cl.get(nodeid, name, [])
511- # else:
512- # node[name] = cl.get(nodeid, name, None)
513- # db.setnode(classname, nodeid, node)
514- return 1
515-
516547def figureCommands ():
517548 d = {}
518549 for k , v in globals ().items ():
@@ -528,8 +559,9 @@ def figureHelp():
528559 return d
529560
530561class AdminTool :
531-
532562 def run_command (self , args ):
563+ '''Run a single command
564+ '''
533565 command = args [0 ]
534566
535567 # handle help now
@@ -557,7 +589,7 @@ class AdminTool:
557589
558590 # not a valid command
559591 if function is None :
560- usage ( 'Unknown command "%s"' % command )
592+ print 'Unknown command "%s" ("help commands" for a list) ' % command
561593 return 1
562594
563595 # get the instance
@@ -631,6 +663,9 @@ if __name__ == '__main__':
631663
632664#
633665# $Log: not supported by cvs2svn $
666+ # Revision 1.32 2001/10/17 06:57:29 richard
667+ # Interactive startup blurb - need to figure how to get the version in there.
668+ #
634669# Revision 1.31 2001/10/17 06:17:26 richard
635670# Now with readline support :)
636671#
0 commit comments