Add usage text for --langs
[zzz-pokedex.git] / pokedex / main.py
1 # encoding: utf8
2 from optparse import OptionParser
3 import os
4 import sys
5
6 import pokedex.db
7 import pokedex.db.load
8 import pokedex.db.tables
9 import pokedex.lookup
10 from pokedex import defaults
11
12 def main():
13 if len(sys.argv) <= 1:
14 command_help()
15
16 command = sys.argv[1]
17 args = sys.argv[2:]
18
19 # XXX there must be a better way to get Unicode argv
20 # XXX this doesn't work on Windows durp
21 enc = sys.stdin.encoding or 'utf8'
22 args = [_.decode(enc) for _ in args]
23
24 # Find the command as a function in this file
25 func = globals().get("command_%s" % command, None)
26 if func:
27 func(*args)
28 else:
29 command_help()
30
31
32 def get_parser(verbose=True):
33 """Returns an OptionParser prepopulated with the global options.
34
35 `verbose` is whether or not the options should be verbose by default.
36 """
37 parser = OptionParser()
38 parser.add_option('-e', '--engine', dest='engine_uri', default=None)
39 parser.add_option('-i', '--index', dest='index_dir', default=None)
40 parser.add_option('-q', '--quiet', dest='verbose', default=verbose, action='store_false')
41 parser.add_option('-v', '--verbose', dest='verbose', default=verbose, action='store_true')
42 return parser
43
44 def get_session(options):
45 """Given a parsed options object, connects to the database and returns a
46 session.
47 """
48
49 engine_uri = options.engine_uri
50 got_from = 'command line'
51
52 if engine_uri is None:
53 engine_uri, got_from = defaults.get_default_db_uri_with_origin()
54
55 session = pokedex.db.connect(engine_uri)
56
57 if options.verbose:
58 print "Connected to database %(engine)s (from %(got_from)s)" \
59 % dict(engine=session.bind.url, got_from=got_from)
60
61 return session
62
63 def get_lookup(options, session=None, recreate=False):
64 """Given a parsed options object, opens the whoosh index and returns a
65 PokedexLookup object.
66 """
67
68 if recreate and not session:
69 raise ValueError("get_lookup() needs an explicit session to regen the index")
70
71 index_dir = options.index_dir
72 got_from = 'command line'
73
74 if index_dir is None:
75 index_dir, got_from = defaults.get_default_index_dir_with_origin()
76
77 if options.verbose:
78 print "Opened lookup index %(index_dir)s (from %(got_from)s)" \
79 % dict(index_dir=index_dir, got_from=got_from)
80
81 lookup = pokedex.lookup.PokedexLookup(index_dir, session=session)
82
83 if recreate:
84 lookup.rebuild_index()
85
86 return lookup
87
88 def get_csv_directory(options):
89 """Prints and returns the csv directory we're about to use."""
90
91 if not options.verbose:
92 return
93
94 csvdir = options.directory
95 got_from = 'command line'
96
97 if csvdir is None:
98 csvdir, got_from = defaults.get_default_csv_dir_with_origin()
99
100 print "Using CSV directory %(csvdir)s (from %(got_from)s)" \
101 % dict(csvdir=csvdir, got_from=got_from)
102
103 return csvdir
104
105
106 ### Plumbing commands
107
108 def command_dump(*args):
109 parser = get_parser(verbose=True)
110 parser.add_option('-d', '--directory', dest='directory', default=None)
111 parser.add_option('-l', '--langs', dest='langs', default='en',
112 help="Comma-separated list of languages to dump all strings for. "
113 "Default is English ('en')")
114 options, tables = parser.parse_args(list(args))
115
116 session = get_session(options)
117 get_csv_directory(options)
118
119 langs = [l.strip() for l in options.langs.split(',')]
120
121 pokedex.db.load.dump(session, directory=options.directory,
122 tables=tables,
123 verbose=options.verbose,
124 langs=langs)
125
126 def command_load(*args):
127 parser = get_parser(verbose=True)
128 parser.add_option('-d', '--directory', dest='directory', default=None)
129 parser.add_option('-D', '--drop-tables', dest='drop_tables', default=False, action='store_true')
130 parser.add_option('-r', '--recursive', dest='recursive', default=False, action='store_true')
131 parser.add_option('-S', '--safe', dest='safe', default=False, action='store_true',
132 help="Do not use backend-specific optimalizations.")
133 parser.add_option('-l', '--langs', dest='langs', default=None,
134 help="Comma-separated list of extra languages to load, or 'none' for none. "
135 "Default is to load 'em all. Example: 'fr,de'")
136 options, tables = parser.parse_args(list(args))
137
138 if not options.engine_uri:
139 print "WARNING: You're reloading the default database, but not the lookup index. They"
140 print " might get out of sync, and pokedex commands may not work correctly!"
141 print "To fix this, run `pokedex reindex` when this command finishes. Or, just use"
142 print "`pokedex setup` to do both at once."
143 print
144
145 if options.langs == 'none':
146 langs = []
147 elif options.langs is None:
148 langs = None
149 else:
150 langs = [l.strip() for l in options.langs.split(',')]
151
152 session = get_session(options)
153 get_csv_directory(options)
154
155 pokedex.db.load.load(session, directory=options.directory,
156 drop_tables=options.drop_tables,
157 tables=tables,
158 verbose=options.verbose,
159 safe=options.safe,
160 recursive=options.recursive,
161 langs=langs)
162
163 def command_reindex(*args):
164 parser = get_parser(verbose=True)
165 options, _ = parser.parse_args(list(args))
166
167 session = get_session(options)
168 lookup = get_lookup(options, session=session, recreate=True)
169
170 print "Recreated lookup index."
171
172
173 def command_setup(*args):
174 parser = get_parser(verbose=False)
175 options, _ = parser.parse_args(list(args))
176
177 options.directory = None
178
179 session = get_session(options)
180 get_csv_directory(options)
181 pokedex.db.load.load(session, directory=None, drop_tables=True,
182 verbose=options.verbose,
183 safe=False)
184
185 lookup = get_lookup(options, session=session, recreate=True)
186
187 print "Recreated lookup index."
188
189
190 def command_status(*args):
191 parser = get_parser(verbose=True)
192 options, _ = parser.parse_args(list(args))
193 options.verbose = True
194 options.directory = None
195
196 # Database, and a lame check for whether it's been inited at least once
197 session = get_session(options)
198 print " - OK! Connected successfully."
199
200 if pokedex.db.tables.Pokemon.__table__.exists(session.bind):
201 print " - OK! Database seems to contain some data."
202 else:
203 print " - WARNING: Database appears to be empty."
204
205 # CSV; simple checks that the dir exists
206 csvdir = get_csv_directory(options)
207 if not os.path.exists(csvdir):
208 print " - ERROR: No such directory!"
209 elif not os.path.isdir(csvdir):
210 print " - ERROR: Not a directory!"
211 else:
212 print " - OK! Directory exists."
213
214 if os.access(csvdir, os.R_OK):
215 print " - OK! Can read from directory."
216 else:
217 print " - ERROR: Can't read from directory!"
218
219 if os.access(csvdir, os.W_OK):
220 print " - OK! Can write to directory."
221 else:
222 print " - WARNING: Can't write to directory! " \
223 "`dump` will not work. You may need to sudo."
224
225 # Index; the PokedexLookup constructor covers most tests and will
226 # cheerfully bomb if they fail
227 lookup = get_lookup(options, recreate=False)
228 print " - OK! Opened successfully."
229
230
231 ### User-facing commands
232
233 def command_lookup(*args):
234 parser = get_parser(verbose=False)
235 options, words = parser.parse_args(list(args))
236
237 name = u' '.join(words)
238
239 session = get_session(options)
240 lookup = get_lookup(options, session=session, recreate=False)
241
242 results = lookup.lookup(name)
243 if not results:
244 print "No matches."
245 elif results[0].exact:
246 print "Matched:"
247 else:
248 print "Fuzzy-matched:"
249
250 for result in results:
251 if hasattr(result.object, 'full_name'):
252 name = result.object.full_name
253 else:
254 name = result.object.name
255
256 print "%s: %s" % (result.object.__tablename__, name),
257 if result.language:
258 print "(%s in %s)" % (result.name, result.language)
259 else:
260 print
261
262
263 def command_help():
264 print u"""pokedex -- a command-line Pokédex interface
265 usage: pokedex {command} [options...]
266 Run `pokedex setup` first, or nothing will work!
267 See http://bugs.veekun.com/projects/pokedex/wiki/CLI for more documentation.
268
269 Commands:
270 help Displays this message.
271 lookup [thing] Look up something in the Pokédex.
272
273 System commands:
274 load Load Pokédex data into a database from CSV files.
275 dump Dump Pokédex data from a database into CSV files.
276 reindex Rebuilds the lookup index from the database.
277 setup Combines load and reindex.
278 status No effect, but prints which engine, index, and csv
279 directory would be used for other commands.
280
281 Global options:
282 -e|--engine=URI By default, all commands try to use a SQLite database
283 in the pokedex install directory. Use this option (or
284 a POKEDEX_DB_ENGINE environment variable) to specify an
285 alternate database.
286 -i|--index=DIR By default, all commands try to put the lookup index in
287 the pokedex install directory. Use this option (or a
288 POKEDEX_INDEX_DIR environment variable) to specify an
289 alternate loction.
290 -q|--quiet Don't print system output. This is the default for
291 non-system commands and setup.
292 -v|--verbose Print system output. This is the default for system
293 commands, except setup.
294
295 System options:
296 -d|--directory=DIR By default, load and dump will use the CSV files in the
297 pokedex install directory. Use this option to specify
298 a different directory.
299
300 Load options:
301 -D|--drop-tables Drop all tables before loading data.
302 -S|--safe Disable engine-specific optimizations.
303 -r|--recursive Load (and drop) all dependent tables.
304 -l|--langs Load translations for the given languages.
305 By default, all available translations are loaded.
306 Separate multiple languages by a comma (-l en,de,fr)
307
308 Dump options:
309 -l|--langs Dump unofficial texts for given languages.
310 By default, English (en) is dumped.
311 Separate multiple languages by a comma (-l en,de,fr)
312 Use 'none' to not dump any unofficial texts.
313
314 Additionally, load and dump accept a list of table names (possibly with
315 wildcards) and/or csv fileames as an argument list.
316 """.encode(sys.getdefaultencoding(), 'replace')
317
318 sys.exit(0)