1616# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1717# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1818#
19- # $Id: roundup-admin,v 1.23 2001-10-09 23:36:25 richard Exp $
19+ # $Id: roundup-admin,v 1.24 2001-10-10 03:54:57 richard Exp $
2020
2121import sys
2222if int (sys .version [0 ]) < 2 :
2323 print 'Roundup requires python 2.0 or later.'
2424 sys .exit (1 )
2525
2626import string , os , getpass , getopt , re
27+ try :
28+ import csv
29+ except ImportError :
30+ csv = None
2731from roundup import date , roundupdb , init , password
2832import roundup .instance
2933
@@ -233,7 +237,7 @@ def do_spec(db, args):
233237 else :
234238 print '%s: %s' % (key , value )
235239
236- def do_create (db , args , pretty_re = re . compile ( r'<roundup\.hyperdb\.(.*)>' ) ):
240+ def do_create (db , args ):
237241 '''Usage: create classname property=value ...
238242 Create a new entry of a given class.
239243
@@ -251,12 +255,19 @@ def do_create(db, args, pretty_re=re.compile(r'<roundup\.hyperdb\.(.*)>')):
251255 # ask for the properties
252256 for key , value in properties .items ():
253257 if key == 'id' : continue
254- m = pretty_re .match (str (value ))
255- if m :
256- value = m .group (1 )
257- value = raw_input ('%s (%s): ' % (key .capitalize (), value ))
258- if value :
259- props [key ] = value
258+ name = value .__class__ .__name__
259+ if isinstance (value , hyperdb .Password ):
260+ again = None
261+ while value != again :
262+ value = getpass .getpass ('%s (Password): ' % key .capitalize ())
263+ again = getpass .getpass (' %s (Again): ' % key .capitalize ())
264+ if value != again : print 'Sorry, try again...'
265+ if value :
266+ props [key ] = value
267+ else :
268+ value = raw_input ('%s (%s): ' % (key .capitalize (), name ))
269+ if value :
270+ props [key ] = value
260271 else :
261272 # use the args
262273 for prop in args [1 :]:
@@ -270,6 +281,8 @@ def do_create(db, args, pretty_re=re.compile(r'<roundup\.hyperdb\.(.*)>')):
270281 props [key ] = date .Date (value )
271282 elif isinstance (type , hyperdb .Interval ):
272283 props [key ] = date .Interval (value )
284+ elif isinstance (type , hyperdb .Password ):
285+ props [key ] = password .Password (value )
273286 elif isinstance (type , hyperdb .Multilink ):
274287 props [key ] = value .split (',' )
275288
@@ -325,6 +338,111 @@ def do_retire(db, args):
325338 db .getclass (classname ).retire (nodeid )
326339 return 0
327340
341+ def do_export (db , args ):
342+ '''Usage: export class[,class] destination_dir
343+ Export the database to CSV files by class in the given directory.
344+
345+ This action exports the current data from the database into
346+ comma-separated files that are placed in the nominated destination
347+ directory. The journals are not exported.
348+ '''
349+ if len (args ) < 2 :
350+ print do_export .__doc__
351+ return 1
352+ classes = string .split (args [0 ], ',' )
353+ dir = args [1 ]
354+
355+ # use the csv parser if we can - it's faster
356+ if csv is not None :
357+ p = csv .parser ()
358+
359+ # do all the classes specified
360+ for classname in classes :
361+ cl = db .getclass (classname )
362+ f = open (os .path .join (dir , classname + '.csv' ), 'w' )
363+ f .write (string .join (cl .properties .keys (), ',' ) + '\n ' )
364+
365+ # all nodes for this class
366+ for nodeid in cl .list ():
367+ if csv is not None :
368+ s = p .join (map (str , cl .getnode (nodeid ).values (protected = 0 )))
369+ f .write (s + '\n ' )
370+ else :
371+ l = []
372+ # escape the individual entries to they're valid CSV
373+ for entry in map (str , cl .getnode (nodeid ).values (protected = 0 )):
374+ if '"' in entry :
375+ entry = '""' .join (entry .split ('"' ))
376+ if ',' in entry :
377+ entry = '"%s"' % entry
378+ l .append (entry )
379+ f .write (',' .join (l ) + '\n ' )
380+ return 0
381+
382+ def do_import (db , args ):
383+ '''Usage: import class file
384+ Import the contents of the CSV file as new nodes for the given class.
385+
386+ The file must define the same properties as the class (including having
387+ a "header" line with those property names.)
388+ '''
389+ if len (args ) < 2 :
390+ print do_export .__doc__
391+ return 1
392+ if csv is None :
393+ print 'Sorry, you need the csv module to use this function.'
394+ print 'Get it from: http://www.object-craft.com.au/projects/csv/'
395+ return 1
396+
397+ from roundup import hyperdb
398+
399+ # ensure that the properties and the CSV file headings match
400+ cl = db .getclass (args [0 ])
401+ f = open (args [1 ])
402+ p = csv .parser ()
403+ file_props = p .parse (f .readline ())
404+ props = cl .properties .keys ()
405+ m = file_props [:]
406+ m .sort ()
407+ props .sort ()
408+ if m != props :
409+ print do_export .__doc__
410+ print "\n \n File doesn't define the same properties"
411+ return 1
412+
413+ # loop through the file and create a node for each entry
414+ n = range (len (props ))
415+ while 1 :
416+ line = f .readline ()
417+ if not line : break
418+
419+ # parse lines until we get a complete entry
420+ while 1 :
421+ l = p .parse (line )
422+ if l : break
423+
424+ # make the new node's property map
425+ d = {}
426+ for i in n :
427+ value = l [i ]
428+ key = file_props [i ]
429+ type = cl .properties [key ]
430+ if isinstance (type , hyperdb .Date ):
431+ value = date .Date (value )
432+ elif isinstance (type , hyperdb .Interval ):
433+ value = date .Interval (value )
434+ elif isinstance (type , hyperdb .Password ):
435+ pwd = password .Password ()
436+ pwd .unpack (value )
437+ value = pwd
438+ elif isinstance (type , hyperdb .Multilink ):
439+ value = value .split (',' )
440+ d [key ] = value
441+
442+ # and create the new node
443+ apply (cl .create , (), d )
444+ return 0
445+
328446def do_freshen (db , args ):
329447 '''Usage: freshen
330448 Freshen an existing instance. **DO NOT USE**
@@ -444,6 +562,9 @@ if __name__ == '__main__':
444562
445563#
446564# $Log: not supported by cvs2svn $
565+ # Revision 1.23 2001/10/09 23:36:25 richard
566+ # Spit out command help if roundup-admin command doesn't get an argument.
567+ #
447568# Revision 1.22 2001/10/09 07:25:59 richard
448569# Added the Password property type. See "pydoc roundup.password" for
449570# implementation details. Have updated some of the documentation too.
0 commit comments