X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=roundup%2Fadmin.py;h=ff961b77eb63c50084f049ff32a7d8835e20ba46;hb=ac5f91aa489bb614476c2d49b336c342f641ddaf;hp=cfa2ca42ef065bdb09e67d469948bcea4f56ae7a;hpb=e8e0c9b06279ec296229fcbe2f1c308f4f67afb8;p=roundup.git diff --git a/roundup/admin.py b/roundup/admin.py index cfa2ca4..ff961b7 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.25 2002-09-10 00:18:20 richard Exp $ +# $Id: admin.py,v 1.32 2002-09-24 01:36:04 richard Exp $ import sys, os, getpass, getopt, re, UserDict, shlex, shutil try: @@ -61,7 +61,7 @@ class AdminTool: for k in AdminTool.__dict__.keys(): if k[:5] == 'help_': self.help[k[5:]] = getattr(self, k) - self.instance_home = '' + self.tracker_home = '' self.db = None def get_class(self, classname): @@ -81,23 +81,28 @@ class AdminTool: key, value = arg.split('=') except ValueError: raise UsageError, _('argument "%(arg)s" not propname=value')%locals() - props[key] = value + if value: + props[key] = value + else: + props[key] = None return props def usage(self, message=''): if message: message = _('Problem: %(message)s)\n\n')%locals() - print _('''%(message)sUsage: roundup-admin [-i instance home] [-u login] [-c] + 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 Help: roundup-admin -h roundup-admin help -- this help roundup-admin help -- command-specific help roundup-admin help all -- all available help -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''')%locals() +''')%locals() self.help_commands() def help_commands(self): @@ -136,12 +141,12 @@ Options: def help_all(self): print _(''' -All commands (except help) require an instance specifier. This is just the path -to the roundup instance you're working with. A roundup instance is where +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 roundup keeps the database and configuration file that defines an issue tracker. It may be thought of as the issue tracker's "home directory". It may -be specified in the environment variable ROUNDUP_INSTANCE or on the command -line as "-i instance". +be specified in the environment variable TRACKER_HOME or on the command +line as "-i tracker". A designator is a classname and a nodeid concatenated, eg. bug1, user10, ... @@ -249,27 +254,27 @@ Command help: backends = roundup.backends.__all__ print _('Back ends:'), ', '.join(backends) - def do_install(self, instance_home, args): + def do_install(self, tracker_home, args): '''Usage: install [template [backend [admin password]]] - Install a new Roundup instance. + Install a new Roundup tracker. - The command will prompt for the instance home directory (if not supplied - through INSTANCE_HOME or the -i option). The template, backend and admin + The command will prompt for the tracker home directory (if not supplied + through TRACKER_HOME or the -i option). The template, backend and admin password may be specified on the command-line as arguments, in that order. The initialise command must be called after this command in order - to initialise the instance's database. You may edit the instance's + to initialise the tracker's database. You may edit the tracker's initial database contents before running that command by editing - the instance's dbinit.py module init() function. + the tracker's dbinit.py module init() function. See also initopts help. ''' if len(args) < 1: raise UsageError, _('Not enough arguments supplied') - # make sure the instance home can be created - parent = os.path.split(instance_home)[0] + # make sure the tracker home can be created + parent = os.path.split(tracker_home)[0] if not os.path.exists(parent): raise UsageError, _('Instance home parent directory "%(parent)s"' ' does not exist')%locals() @@ -295,12 +300,13 @@ Command help: backend = raw_input(_('Select backend [anydbm]: ')).strip() if not backend: backend = 'anydbm' + # XXX perform a unit test based on the user's selections # install! - init.install(instance_home, template, backend) + init.install(tracker_home, template, backend) print _(''' - You should now edit the instance configuration file: + You should now edit the tracker configuration file: %(config_file)s ... at a minimum, you must set MAILHOST, MAIL_DOMAIN and ADMIN_EMAIL. @@ -309,19 +315,19 @@ Command help: %(database_config_file)s ... see the documentation on customizing for more information. ''')%{ - 'config_file': os.path.join(instance_home, 'config.py'), - 'database_config_file': os.path.join(instance_home, 'dbinit.py') + 'config_file': os.path.join(tracker_home, 'config.py'), + 'database_config_file': os.path.join(tracker_home, 'dbinit.py') } return 0 - def do_initialise(self, instance_home, args): + def do_initialise(self, tracker_home, args): '''Usage: initialise [adminpw] - Initialise a new Roundup instance. + Initialise a new Roundup tracker. The administrator details will be set at this step. - Execute the instance's initialisation function dbinit.init() + Execute the tracker's initialisation function dbinit.init() ''' # password if len(args) > 1: @@ -333,14 +339,14 @@ Command help: adminpw = getpass.getpass(_('Admin Password: ')) confirm = getpass.getpass(_(' Confirm: ')) - # make sure the instance home is installed - if not os.path.exists(instance_home): + # make sure the tracker home is installed + if not os.path.exists(tracker_home): raise UsageError, _('Instance home does not exist')%locals() - if not os.path.exists(os.path.join(instance_home, 'html')): + if not os.path.exists(os.path.join(tracker_home, 'html')): raise UsageError, _('Instance has not been installed')%locals() # is there already a database? - if os.path.exists(os.path.join(instance_home, 'db')): + if os.path.exists(os.path.join(tracker_home, 'db')): print _('WARNING: The database is already initialised!') print _('If you re-initialise it, you will lose all the data!') ok = raw_input(_('Erase it? Y/[N]: ')).strip() @@ -348,10 +354,10 @@ Command help: return 0 # nuke it - shutil.rmtree(os.path.join(instance_home, 'db')) + shutil.rmtree(os.path.join(tracker_home, 'db')) # GO - init.initialise(instance_home, adminpw) + init.initialise(tracker_home, adminpw) return 0 @@ -392,35 +398,53 @@ Command help: def do_set(self, args): - '''Usage: set designator[,designator]* propname=value ... - Set the given property of one or more designator(s). + '''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 + list of item designators (ie "designator[,designator,...]"). - Sets the property to the value for all designators given. + This command sets the properties to the values for all designators + given. If the value is missing (ie. "property=") then the property is + un-set. ''' if len(args) < 2: raise UsageError, _('Not enough arguments supplied') from roundup import hyperdb designators = args[0].split(',') + if len(designators) == 1: + designator = designators[0] + try: + designator = hyperdb.splitDesignator(designator) + designators = [designator] + except hyperdb.DesignatorError: + cl = self.get_class(designator) + designators = [(designator, x) for x in cl.list()] + else: + try: + designators = [hyperdb.splitDesignator(x) for x in designators] + except hyperdb.DesignatorError, message: + raise UsageError, message # get the props from the args props = self.props_from_args(args[1:]) # now do the set for all the nodes - for designator in designators: - # decode the node designator - try: - classname, nodeid = hyperdb.splitDesignator(designator) - except hyperdb.DesignatorError, message: - raise UsageError, message - - # get the class + for classname, itemid in designators: cl = self.get_class(classname) properties = cl.getprops() for key, value in props.items(): proptype = properties[key] - if isinstance(proptype, hyperdb.String): + if isinstance(proptype, hyperdb.Multilink): + if value is None: + props[key] = [] + else: + props[key] = value.split(',') + elif value is None: + continue + elif isinstance(proptype, hyperdb.String): continue elif isinstance(proptype, hyperdb.Password): props[key] = password.Password(value) @@ -436,8 +460,6 @@ Command help: raise UsageError, '"%s": %s'%(value, message) elif isinstance(proptype, hyperdb.Link): props[key] = value - elif isinstance(proptype, hyperdb.Multilink): - props[key] = value.split(',') elif isinstance(proptype, hyperdb.Boolean): props[key] = value.lower() in ('yes', 'true', 'on', '1') elif isinstance(proptype, hyperdb.Number): @@ -445,7 +467,7 @@ Command help: # try the set try: - apply(cl.set, (nodeid, ), props) + apply(cl.set, (itemid, ), props) except (TypeError, IndexError, ValueError), message: raise UsageError, message return 0 @@ -809,11 +831,11 @@ Command help: def do_export(self, args): '''Usage: export [class[,class]] export_dir - Export the database to tab-separated-value files. + Export the database to colon-separated-value files. This action exports the current data from the database into - tab-separated-value files that are placed in the nominated destination - directory. The journals are not exported. + colon-separated-value files that are placed in the nominated + destination directory. The journals are not exported. ''' # we need the CSV module if csv is None: @@ -860,9 +882,9 @@ Command help: The imported nodes will have the same nodeid as defined in the import file, thus replacing any existing content. - XXX The new nodes are added to the existing database - if you want to - XXX create a new database using the imported data, then create a new - XXX database (or, tediously, retire all the old data.) + The new nodes are added to the existing database - if you want to + create a new database using the imported data, then create a new + database (or, tediously, retire all the old data.) ''' if len(args) < 1: raise UsageError, _('Not enough arguments supplied') @@ -909,8 +931,8 @@ Command help: # do the import and figure the current highest nodeid maxid = max(maxid, int(cl.import_list(propnames, l))) - print 'setting', classname, maxid - self.db.setid(classname, str(maxid)) + print 'setting', classname, maxid+1 + self.db.setid(classname, str(maxid+1)) return 0 def do_pack(self, args): @@ -953,9 +975,9 @@ Date format is "YYYY-MM-DD" eg: def do_reindex(self, args): '''Usage: reindex - Re-generate an instance's search indexes. + Re-generate a tracker's search indexes. - This will re-generate the search indexes for an instance. This will + This will re-generate the search indexes for a tracker. This will typically happen automatically. ''' self.db.indexer.force_reindex() @@ -1030,35 +1052,35 @@ Date format is "YYYY-MM-DD" eg: return 1 command, function = functions[0] - # make sure we have an instance_home - while not self.instance_home: - self.instance_home = raw_input(_('Enter instance home: ')).strip() + # make sure we have a tracker_home + while not self.tracker_home: + self.tracker_home = raw_input(_('Enter tracker home: ')).strip() # before we open the db, we may be doing an install or init if command == 'initialise': try: - return self.do_initialise(self.instance_home, args) + return self.do_initialise(self.tracker_home, args) except UsageError, message: print _('Error: %(message)s')%locals() return 1 elif command == 'install': try: - return self.do_install(self.instance_home, args) + return self.do_install(self.tracker_home, args) except UsageError, message: print _('Error: %(message)s')%locals() return 1 - # get the instance + # get the tracker try: - instance = roundup.instance.open(self.instance_home) + tracker = roundup.instance.open(self.tracker_home) except ValueError, message: - self.instance_home = '' - print _("Error: Couldn't open instance: %(message)s")%locals() + self.tracker_home = '' + print _("Error: Couldn't open tracker: %(message)s")%locals() return 1 # only open the database once! if not self.db: - self.db = instance.open('admin') + self.db = tracker.open('admin') # do the command ret = 0 @@ -1112,7 +1134,7 @@ Date format is "YYYY-MM-DD" eg: return 1 # handle command-line args - self.instance_home = os.environ.get('ROUNDUP_INSTANCE', '') + self.tracker_home = os.environ.get('TRACKER_HOME', '') # TODO: reinstate the user/password stuff (-u arg too) name = password = '' if os.environ.has_key('ROUNDUP_LOGIN'): @@ -1126,19 +1148,23 @@ Date format is "YYYY-MM-DD" eg: self.usage() return 0 if opt == '-i': - self.instance_home = arg + self.tracker_home = arg if opt == '-c': self.comma_sep = 1 # if no command - go interactive + # wrap in a try/finally so we always close off the db ret = 0 - if not args: - self.interactive() - else: - ret = self.run_command(args) - if self.db: self.db.commit() - return ret - + try: + if not args: + self.interactive() + else: + ret = self.run_command(args) + if self.db: self.db.commit() + return ret + finally: + if self.db: + self.db.close() if __name__ == '__main__': tool = AdminTool()