Code

Made the "init" prompting more friendly.
[roundup.git] / roundup-admin
1 #! /usr/bin/python
2 # $Id: roundup-admin,v 1.5 2001-07-30 00:04:48 richard Exp $
4 import sys
5 if int(sys.version[0]) < 2:
6     print 'Roundup requires python 2.0 or later.'
7     sys.exit(1)
9 import string, os, getpass
10 from roundup import date, roundupdb, init
12 def determineLogin(instance, argv, n = 2):
13     name = password = ''
14     if argv[2] == '-u':
15         l = argv[3].split(':')
16         name = l[0]
17         if len(l) > 1:
18             password = l[1]
19         n = 4
20     elif os.environ.has_key('ROUNDUP_LOGIN'):
21         l = os.environ['ROUNDUP_LOGIN'].split(':')
22         name = l[0]
23         if len(l) > 1:
24             password = l[1]
25     while not name:
26         name = raw_input('Login name: ')
27     while not password:
28         password = getpass.getpass('  password: ')
29     # TODO use the password...
30     return n, instance.open(name)
32 def usage(message=''):
33     if message: message = 'Problem: '+message+'\n'
34     print '''%sUsage:
36  roundup [-i instance] init [template backend]
37    -- initialise the database
38  roundup [-i instance] spec classname
39    -- show the properties for a classname
40  roundup [-i instance] create [-u login] classname propname=value ...
41    -- create a new entry of a given class
42  roundup [-i instance] list [-c] classname
43    -- list the instances of a class
44  roundup [-i instance] history [-c] designator
45    -- show the history entries of a designator
46  roundup [-i instance] get [-c] designator[,designator,...] propname
47    -- get the given property of one or more designator(s)
48  roundup [-i instance] set [-u login] designator[,designator,...] propname=value ...
49    -- set the given property of one or more designator(s)
50  roundup [-i instance] find [-c] classname propname=value ...
51    -- find the class instances with a given property
52  roundup [-i instance] retire designator[,designator,...]
53    -- "retire" a designator
54  roundup help    
55    -- this help
56  roundup morehelp
57    -- even more detailed help
58 '''%message
60 def moreusage(message=''):
61     usage(message)
62     print '''
63 All commands (except help) require an instance specifier. This is just the path
64 to the roundup instance you're working with. It may be specified in the environment
65 variable ROUNDUP_INSTANCE or on the command line as "-i instance".
67 A designator is a classname and a nodeid concatenated, eg. bug1, user10, ...
69 Property values are represented as strings in command arguments and in the
70 printed results:
71  . Strings are, well, strings.
72  . Date values are printed in the full date format in the local time zone, and
73    accepted in the full format or any of the partial formats explained below.
74  . Link values are printed as node designators. When given as an argument,
75    node designators and key strings are both accepted.
76  . Multilink values are printed as lists of node designators joined by commas.
77    When given as an argument, node designators and key strings are both
78    accepted; an empty string, a single node, or a list of nodes joined by
79    commas is accepted.
81 When multiple nodes are specified to the roundup get or roundup set
82 commands, the specified properties are retrieved or set on all the listed
83 nodes. 
85 When multiple results are returned by the roundup get or roundup find
86 commands, they are printed one per line (default) or joined by commas (with
87 the -c) option. 
89 Where the command changes data, a login name/password is required. The
90 login may be specified as either "name" or "name:password".
91  . ROUNDUP_LOGIN environment variable
92  . the -u command-line option
93 If either the name or password is not supplied, they are obtained from the
94 command-line. 
96 Date format examples:
97   "2000-04-17.03:45" means <Date 2000-04-17.08:45:00>
98   "2000-04-17" means <Date 2000-04-17.00:00:00>
99   "01-25" means <Date yyyy-01-25.00:00:00>
100   "08-13.22:13" means <Date yyyy-08-14.03:13:00>
101   "11-07.09:32:43" means <Date yyyy-11-07.14:32:43>
102   "14:25" means <Date yyyy-mm-dd.19:25:00>
103   "8:47:11" means <Date yyyy-mm-dd.13:47:11>
104   "." means "right now"
105 '''
107 def main():
108     argv = sys.argv
110     if len(argv) == 1:
111         usage('No command specified')
112         return 1
114     # handle help now
115     if argv[1] == 'help':
116         usage()
117         return 0
118     if argv[1] == 'morehelp':
119         moreusage()
120         return 0
122     # figure the instance home
123     n = 1
124     if argv[1] == '-i':
125         if len(argv) == 2:
126             usage()
127             return 1
128         instance_home = argv[2]
129         n = 3
130     else:
131         instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
133     # now figure the command
134     command = argv[n]
135     n = n + 1
137     if command == 'init':
138         adminpw = ''
139         confirm = 'x'
140         if len(argv) > n:
141             template = argv[n]
142             backend = argv[n+1]
143         else:
144             template = backend = ''
145         while not instance_home:
146             instance_home = raw_input('Enter instance home: ').strip()
148         # select template
149         import roundup.templates
150         templates = roundup.templates.listTemplates()
151         print 'Templates:', ', '.join(templates)
152         template = ''
153         while template not in templates:
154             template = raw_input('Select template [classic]: ').strip()
155             if not template:
156                 template = 'classic'
158         import roundup.backends
159         backends = roundup.backends.__all__
160         backend = ''
161         while backend not in backends:
162             backend = raw_input('Select backend [anydbm]: ').strip()
163             if not backend:
164                 backend = 'anydbm'
165         while adminpw != confirm:
166             adminpw = getpass.getpass('Admin Password: ')
167             confirm = getpass.getpass('       Confirm: ')
168         init.init(instance_home, template, backend, adminpw)
169         return 0
171     # from here on, we need an instance_home
172     if not instance_home:
173         usage('No instance home specified')
174         return 1
176     # get the instance
177     path, instance = os.path.split(instance_home)
178     sys.path.insert(0, path)
179     try:
180         instance = __import__(instance)
181     finally:
182         del sys.path[0]
184     if command == 'get':
185         db = instance.open()
186         designators = string.split(argv[n], ',')
187         propname = argv[n+1]
188         # TODO: handle the -c option
189         for designator in designators:
190             classname, nodeid = roundupdb.splitDesignator(designator)
191             print db.getclass(classname).get(nodeid, propname)
193     elif command == 'set':
194         n, db = determineLogin(instance, argv, n)
195         designators = string.split(argv[n], ',')
196         props = {}
197         for prop in argv[n+1:]:
198             key, value = prop.split('=')
199             props[key] = value
200         for designator in designators:
201             classname, nodeid = roundupdb.splitDesignator(designator)
202             cl = db.getclass(classname)
203             properties = cl.getprops()
204             for key, value in props.items():
205                 type =  properties[key]
206                 if type.isStringType:
207                     continue
208                 elif type.isDateType:
209                     props[key] = date.Date(value)
210                 elif type.isIntervalType:
211                     props[key] = date.Interval(value)
212                 elif type.isLinkType:
213                     props[key] = value
214                 elif type.isMultilinkType:
215                     props[key] = value.split(',')
216             apply(cl.set, (nodeid, ), props)
218     elif command == 'find':
219         db = instance.open()
220         classname = argv[n]
221         cl = db.getclass(classname)
223         # look up the linked-to class and get the nodeid that has the value
224         propname, value = argv[n+1:].split('=')
225         propcl = cl[propname].classname
226         nodeid = propcl.lookup(value)
228         # now do the find
229         # TODO: handle the -c option
230         print cl.find(propname, nodeid)
232     elif command == 'spec':
233         db = instance.open()
234         classname = argv[n]
235         cl = db.getclass(classname)
236         for key, value in cl.properties.items():
237             print '%s: %s'%(key, value)
239     elif command == 'create':
240         n, db = determineLogin(instance, argv, n)
241         classname = argv[n]
242         cl = db.getclass(classname)
243         props = {}
244         properties = cl.getprops()
245         for prop in argv[n+1:]:
246             key, value = prop.split('=')
247             type =  properties[key]
248             if type.isStringType:
249                 props[key] = value 
250             elif type.isDateType:
251                 props[key] = date.Date(value)
252             elif type.isIntervalType:
253                 props[key] = date.Interval(value)
254             elif type.isLinkType:
255                 props[key] = value
256             elif type.isMultilinkType:
257                 props[key] = value.split(',')
258         print apply(cl.create, (), props)
260     elif command == 'list':
261         db = instance.open()
262         classname = argv[n]
263         cl = db.getclass(classname)
264         key = cl.getkey() or cl.properties.keys()[0]
265         # TODO: handle the -c option
266         for nodeid in cl.list():
267             value = cl.get(nodeid, key)
268             print "%4s: %s"%(nodeid, value)
270     elif command == 'history':
271         db = instance.open()
272         classname, nodeid = roundupdb.splitDesignator(argv[n])
273         # TODO: handle the -c option
274         print db.getclass(classname).history(nodeid)
276     elif command == 'retire':
277         n, db = determineLogin(instance, argv, n)
278         designators = string.split(argv[n], ',')
279         for designator in designators:
280             classname, nodeid = roundupdb.splitDesignator(designator)
281             db.getclass(classname).retire(nodeid)
283     elif command == 'freshen':
284         n, db = determineLogin(instance, argv, n)
285         for classname, cl in db.classes.items():
286             properties = cl.properties.keys()
287             for nodeid in cl.list():
288                 node = {}
289                 for name in properties:
290                     node[name] = cl.get(nodeid, name)
291                 db.setnode(classname, nodeid, node)
293     else:
294         print "Unknown command '%s'"%command
295         usage()
296         return 1
298     db.close()
299     return 0
301 if __name__ == '__main__':
302     sys.exit(main())
305 # $Log: not supported by cvs2svn $
306 # Revision 1.4  2001/07/29 07:01:39  richard
307 # Added vim command to all source so that we don't get no steenkin' tabs :)
309 # Revision 1.3  2001/07/23 08:45:28  richard
310 # ok, so now "./roundup-admin init" will ask questions in an attempt to get a
311 # workable instance_home set up :)
312 # _and_ anydbm has had its first test :)
314 # Revision 1.2  2001/07/23 08:20:44  richard
315 # Moved over to using marshal in the bsddb and anydbm backends.
316 # roundup-admin now has a "freshen" command that'll load/save all nodes (not
317 #  retired - mod hyperdb.Class.list() so it lists retired nodes)
319 # Revision 1.1  2001/07/23 03:46:48  richard
320 # moving the bin files to facilitate out-of-the-boxness
322 # Revision 1.1  2001/07/22 11:15:45  richard
323 # More Grande Splite stuff
326 # vim: set filetype=python ts=4 sw=4 et si