Code

Fixed bug in unit test ;)
[roundup.git] / roundup-admin
1 #! /usr/bin/python
2 # $Id: roundup-admin,v 1.4 2001-07-29 07:01:39 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()
147         # TODO: list the templates
148         while not template:
149             template = raw_input('Select template: ').strip()
150         # TODO: list the backends
151         while not backend:
152             backend = raw_input('Select backend: ').strip()
153         while adminpw != confirm:
154             adminpw = getpass.getpass('Admin Password: ')
155             confirm = getpass.getpass('       Confirm: ')
156         init.init(instance_home, template, backend, adminpw)
157         return 0
159     # from here on, we need an instance_home
160     if not instance_home:
161         usage('No instance home specified')
162         return 1
164     # get the instance
165     path, instance = os.path.split(instance_home)
166     sys.path.insert(0, path)
167     try:
168         instance = __import__(instance)
169     finally:
170         del sys.path[0]
172     if command == 'get':
173         db = instance.open()
174         designators = string.split(argv[n], ',')
175         propname = argv[n+1]
176         # TODO: handle the -c option
177         for designator in designators:
178             classname, nodeid = roundupdb.splitDesignator(designator)
179             print db.getclass(classname).get(nodeid, propname)
181     elif command == 'set':
182         n, db = determineLogin(instance, argv, n)
183         designators = string.split(argv[n], ',')
184         props = {}
185         for prop in argv[n+1:]:
186             key, value = prop.split('=')
187             props[key] = value
188         for designator in designators:
189             classname, nodeid = roundupdb.splitDesignator(designator)
190             cl = db.getclass(classname)
191             properties = cl.getprops()
192             for key, value in props.items():
193                 type =  properties[key]
194                 if type.isStringType:
195                     continue
196                 elif type.isDateType:
197                     props[key] = date.Date(value)
198                 elif type.isIntervalType:
199                     props[key] = date.Interval(value)
200                 elif type.isLinkType:
201                     props[key] = value
202                 elif type.isMultilinkType:
203                     props[key] = value.split(',')
204             apply(cl.set, (nodeid, ), props)
206     elif command == 'find':
207         db = instance.open()
208         classname = argv[n]
209         cl = db.getclass(classname)
211         # look up the linked-to class and get the nodeid that has the value
212         propname, value = argv[n+1:].split('=')
213         propcl = cl[propname].classname
214         nodeid = propcl.lookup(value)
216         # now do the find
217         # TODO: handle the -c option
218         print cl.find(propname, nodeid)
220     elif command == 'spec':
221         db = instance.open()
222         classname = argv[n]
223         cl = db.getclass(classname)
224         for key, value in cl.properties.items():
225             print '%s: %s'%(key, value)
227     elif command == 'create':
228         n, db = determineLogin(instance, argv, n)
229         classname = argv[n]
230         cl = db.getclass(classname)
231         props = {}
232         properties = cl.getprops()
233         for prop in argv[n+1:]:
234             key, value = prop.split('=')
235             type =  properties[key]
236             if type.isStringType:
237                 props[key] = value 
238             elif type.isDateType:
239                 props[key] = date.Date(value)
240             elif type.isIntervalType:
241                 props[key] = date.Interval(value)
242             elif type.isLinkType:
243                 props[key] = value
244             elif type.isMultilinkType:
245                 props[key] = value.split(',')
246         print apply(cl.create, (), props)
248     elif command == 'list':
249         db = instance.open()
250         classname = argv[n]
251         cl = db.getclass(classname)
252         key = cl.getkey() or cl.properties.keys()[0]
253         # TODO: handle the -c option
254         for nodeid in cl.list():
255             value = cl.get(nodeid, key)
256             print "%4s: %s"%(nodeid, value)
258     elif command == 'history':
259         db = instance.open()
260         classname, nodeid = roundupdb.splitDesignator(argv[n])
261         # TODO: handle the -c option
262         print db.getclass(classname).history(nodeid)
264     elif command == 'retire':
265         n, db = determineLogin(instance, argv, n)
266         designators = string.split(argv[n], ',')
267         for designator in designators:
268             classname, nodeid = roundupdb.splitDesignator(designator)
269             db.getclass(classname).retire(nodeid)
271     elif command == 'freshen':
272         n, db = determineLogin(instance, argv, n)
273         for classname, cl in db.classes.items():
274             properties = cl.properties.keys()
275             for nodeid in cl.list():
276                 node = {}
277                 for name in properties:
278                     node[name] = cl.get(nodeid, name)
279                 db.setnode(classname, nodeid, node)
281     else:
282         print "Unknown command '%s'"%command
283         usage()
284         return 1
286     db.close()
287     return 0
289 if __name__ == '__main__':
290     sys.exit(main())
293 # $Log: not supported by cvs2svn $
294 # Revision 1.3  2001/07/23 08:45:28  richard
295 # ok, so now "./roundup-admin init" will ask questions in an attempt to get a
296 # workable instance_home set up :)
297 # _and_ anydbm has had its first test :)
299 # Revision 1.2  2001/07/23 08:20:44  richard
300 # Moved over to using marshal in the bsddb and anydbm backends.
301 # roundup-admin now has a "freshen" command that'll load/save all nodes (not
302 #  retired - mod hyperdb.Class.list() so it lists retired nodes)
304 # Revision 1.1  2001/07/23 03:46:48  richard
305 # moving the bin files to facilitate out-of-the-boxness
307 # Revision 1.1  2001/07/22 11:15:45  richard
308 # More Grande Splite stuff
311 # vim: set filetype=python ts=4 sw=4 et si