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())
304 #
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 :)
308 #
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 :)
313 #
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)
318 #
319 # Revision 1.1 2001/07/23 03:46:48 richard
320 # moving the bin files to facilitate out-of-the-boxness
321 #
322 # Revision 1.1 2001/07/22 11:15:45 richard
323 # More Grande Splite stuff
324 #
325 #
326 # vim: set filetype=python ts=4 sw=4 et si