]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/cli.py
implement warming in new cli tool
[nominatim.git] / nominatim / cli.py
1 """
2 Command-line interface to the Nominatim functions for import, update,
3 database administration and querying.
4 """
5 import sys
6 import argparse
7 import logging
8 from pathlib import Path
9
10 from .config import Configuration
11 from .admin.exec_utils import run_legacy_script
12
13 class CommandlineParser:
14     """ Wraps some of the common functions for parsing the command line
15         and setting up subcommands.
16     """
17     def __init__(self, prog, description):
18         self.parser = argparse.ArgumentParser(
19             prog=prog,
20             description=description,
21             formatter_class=argparse.RawDescriptionHelpFormatter)
22
23         self.subs = self.parser.add_subparsers(title='available commands',
24                                                dest='subcommand')
25
26         # Arguments added to every sub-command
27         self.default_args = argparse.ArgumentParser(add_help=False)
28         group = self.default_args.add_argument_group('Default arguments')
29         group.add_argument('-h', '--help', action='help',
30                            help='Show this help message and exit')
31         group.add_argument('-q', '--quiet', action='store_const', const=0,
32                            dest='verbose', default=1,
33                            help='Print only error messages')
34         group.add_argument('-v', '--verbose', action='count', default=1,
35                            help='Increase verboseness of output')
36         group.add_argument('--project-dir', metavar='DIR', default='.',
37                            help='Base directory of the Nominatim installation (default:.)')
38         group.add_argument('-j', '--threads', metavar='NUM', type=int,
39                            help='Number of parallel threads to use')
40
41
42     def add_subcommand(self, name, cmd):
43         """ Add a subcommand to the parser. The subcommand must be a class
44             with a function add_args() that adds the parameters for the
45             subcommand and a run() function that executes the command.
46         """
47         parser = self.subs.add_parser(name, parents=[self.default_args],
48                                       help=cmd.__doc__.split('\n', 1)[0],
49                                       description=cmd.__doc__,
50                                       formatter_class=argparse.RawDescriptionHelpFormatter,
51                                       add_help=False)
52         parser.set_defaults(command=cmd)
53         cmd.add_args(parser)
54
55     def run(self, **kwargs):
56         """ Parse the command line arguments of the program and execute the
57             appropriate subcommand.
58         """
59         args = self.parser.parse_args()
60
61         if args.subcommand is None:
62             return self.parser.print_help()
63
64         for arg in ('module_dir', 'osm2pgsql_path', 'phplib_dir', 'data_dir'):
65             setattr(args, arg, Path(kwargs[arg]))
66         args.project_dir = Path(args.project_dir)
67
68         logging.basicConfig(stream=sys.stderr,
69                             format='%(asctime)s %(levelname)s: %(message)s',
70                             datefmt='%Y-%m-%d %H:%M:%S',
71                             level=max(4 - args.verbose, 1) * 10)
72
73         args.config = Configuration(args.project_dir, args.data_dir / 'settings')
74
75         args.command.run(args)
76
77
78 class SetupAll:
79     """\
80     Create a new Nominatim database from an OSM file.
81     """
82
83     @staticmethod
84     def add_args(parser):
85         group_name = parser.add_argument_group('Required arguments')
86         group = group_name.add_mutually_exclusive_group(required=True)
87         group.add_argument('--osm-file',
88                            help='OSM file to be imported.')
89         group.add_argument('--continue', nargs=1,
90                            choices=['load-data', 'indexing', 'db-postprocess'],
91                            help='Continue an import that was interrupted')
92         group = parser.add_argument_group('Optional arguments')
93         group.add_argument('--osm2pgsql-cache', metavar='SIZE', type=int,
94                            help='Size of cache to be used by osm2pgsql (in MB)')
95         group.add_argument('--reverse-only', action='store_true',
96                            help='Do not create tables and indexes for searching')
97         group.add_argument('--enable-debug-statements', action='store_true',
98                            help='Include debug warning statements in SQL code')
99         group.add_argument('--no-partitions', action='store_true',
100                            help="""Do not partition search indices
101                                    (speeds up import of single country extracts)""")
102         group.add_argument('--no-updates', action='store_true',
103                            help="""Do not keep tables that are only needed for
104                                    updating the database later""")
105         group = parser.add_argument_group('Expert options')
106         group.add_argument('--ignore-errors', action='store_true',
107                            help='Continue import even when errors in SQL are present')
108         group.add_argument('--disable-token-precalc', action='store_true',
109                            help='Disable name precalculation')
110         group.add_argument('--index-noanalyse', action='store_true',
111                            help='Do not perform analyse operations during index')
112
113
114     @staticmethod
115     def run(args):
116         print("TODO: ./utils/setup.php", args)
117
118
119 class SetupFreeze:
120     """\
121     Make database read-only.
122
123     About half of data in the Nominatim database is kept only to be able to
124     keep the data up-to-date with new changes made in OpenStreetMap. This
125     command drops all this data and only keeps the part needed for geocoding
126     itself.
127
128     This command has the same effect as the `--no-updates` option for imports.
129     """
130
131     @staticmethod
132     def add_args(parser):
133         pass # No options
134
135     @staticmethod
136     def run(args):
137         print("TODO: setup drop", args)
138
139
140 class SetupSpecialPhrases:
141     """\
142     Maintain special phrases.
143     """
144
145     @staticmethod
146     def add_args(parser):
147         group = parser.add_argument_group('Input arguments')
148         group.add_argument('--from-wiki', action='store_true',
149                            help='Pull special phrases from the OSM wiki.')
150         group = parser.add_argument_group('Output arguments')
151         group.add_argument('-o', '--output', default='-',
152                            type=argparse.FileType('w', encoding='UTF-8'),
153                            help="""File to write the preprocessed phrases to.
154                                    If omitted, it will be written to stdout.""")
155
156     @staticmethod
157     def run(args):
158         print("./utils/specialphrases.php --from-wiki", args)
159
160
161 class UpdateReplication:
162     """\
163     Update the database using an online replication service.
164     """
165
166     @staticmethod
167     def add_args(parser):
168         group = parser.add_argument_group('Arguments for initialisation')
169         group.add_argument('--init', action='store_true',
170                            help='Initialise the update process')
171         group.add_argument('--no-update-functions', dest='update_functions',
172                            action='store_false',
173                            help="""Do not update the trigger function to
174                                    support differential updates.""")
175         group = parser.add_argument_group('Arguments for updates')
176         group.add_argument('--check-for-updates', action='store_true',
177                            help='Check if new updates are available and exit')
178         group.add_argument('--once', action='store_true',
179                            help="""Download and apply updates only once. When
180                                    not set, updates are continuously applied""")
181         group.add_argument('--no-index', action='store_false', dest='do_index',
182                            help="""Do not index the new data. Only applicable
183                                    together with --once""")
184
185     @staticmethod
186     def run(args):
187         if args.init:
188             print('./utils/update.php --init-updates', args)
189         else:
190             print('./utils/update.php --import-osmosis(-all)', args)
191
192
193 class UpdateAddData:
194     """\
195     Add additional data from a file or an online source.
196
197     Data is only imported, not indexed. You need to call `nominatim-update index`
198     to complete the process.
199     """
200
201     @staticmethod
202     def add_args(parser):
203         group_name = parser.add_argument_group('Source')
204         group = group_name.add_mutually_exclusive_group(required=True)
205         group.add_argument('--file', metavar='FILE',
206                            help='Import data from an OSM file')
207         group.add_argument('--diff', metavar='FILE',
208                            help='Import data from an OSM diff file')
209         group.add_argument('--node', metavar='ID', type=int,
210                            help='Import a single node from the API')
211         group.add_argument('--way', metavar='ID', type=int,
212                            help='Import a single way from the API')
213         group.add_argument('--relation', metavar='ID', type=int,
214                            help='Import a single relation from the API')
215         group.add_argument('--tiger-data', metavar='DIR',
216                            help='Add housenumbers from the US TIGER census database.')
217         group = parser.add_argument_group('Extra arguments')
218         group.add_argument('--use-main-api', action='store_true',
219                            help='Use OSM API instead of Overpass to download objects')
220
221     @staticmethod
222     def run(args):
223         print('./utils/update.php --import-*', args)
224
225
226 class UpdateIndex:
227     """\
228     Reindex all new and modified data.
229     """
230
231     @staticmethod
232     def add_args(parser):
233         pass
234
235     @staticmethod
236     def run(args):
237         print('./utils/update.php --index', args)
238
239
240 class UpdateRefresh:
241     """\
242     Recompute auxillary data used by the indexing process.
243
244     These functions must not be run in parallel with other update commands.
245     """
246
247     @staticmethod
248     def add_args(parser):
249         group = parser.add_argument_group('Data arguments')
250         group.add_argument('--postcodes', action='store_true',
251                            help='Update postcode centroid table')
252         group.add_argument('--word-counts', action='store_true',
253                            help='Compute frequency of full-word search terms')
254         group.add_argument('--address-levels', action='store_true',
255                            help='Reimport address level configuration')
256         group.add_argument('--importance', action='store_true',
257                            help='Recompute place importances')
258         group.add_argument('--functions', action='store_true',
259                            help='Update the PL/pgSQL functions in the database')
260         group.add_argument('--wiki-data',
261                            help='Update Wikipedia/data importance numbers.')
262         group.add_argument('--website', action='store_true',
263                            help='Refresh the directory that serves the scripts for the web API')
264         group = parser.add_argument_group('Arguments for function refresh')
265         group.add_argument('--no-diff-updates', action='store_false', dest='diffs',
266                            help='Do not enable code for propagating updates')
267
268     @staticmethod
269     def run(args):
270         print('./utils/update.php', args)
271
272
273 class AdminCheckDatabase:
274     """\
275     Check that the database is complete and operational.
276     """
277
278     @staticmethod
279     def add_args(parser):
280         pass # No options
281
282     @staticmethod
283     def run(args):
284         print("TODO: ./utils/check_import_finished.php", args)
285
286
287 class AdminWarm:
288     """\
289     Warm database caches for search and reverse queries.
290     """
291
292     @staticmethod
293     def add_args(parser):
294         group = parser.add_argument_group('Target arguments')
295         group.add_argument('--search-only', action='store_const', dest='target',
296                            const='search',
297                            help="Only pre-warm tables for search queries")
298         group.add_argument('--reverse-only', action='store_const', dest='target',
299                            const='reverse',
300                            help="Only pre-warm tables for reverse queries")
301
302     @staticmethod
303     def run(args):
304         params = ['warm.php']
305         if args.target == 'reverse':
306             params.append('--reverse-only')
307         if args.target == 'search':
308             params.append('--search-only')
309         return run_legacy_script(*params, nominatim_env=args)
310
311
312 class QueryExport:
313     """\
314     Export addresses as CSV file from a Nominatim database.
315     """
316
317     @staticmethod
318     def add_args(parser):
319         group = parser.add_argument_group('Output arguments')
320         group.add_argument('--output-type', default='street',
321                            choices=('continent', 'country', 'state', 'county',
322                                     'city', 'suburb', 'street', 'path'),
323                            help='Type of places to output (default: street)')
324         group.add_argument('--output-format',
325                            default='street;suburb;city;county;state;country',
326                            help="""Semicolon-separated list of address types
327                                    (see --output-type). Multiple ranks can be
328                                    merged into one column by simply using a
329                                    comma-separated list.""")
330         group.add_argument('--output-all-postcodes', action='store_true',
331                            help="""List all postcodes for address instead of
332                                    just the most likely one""")
333         group.add_argument('--language',
334                            help="""Preferred language for output
335                                    (use local name, if omitted)""")
336         group = parser.add_argument_group('Filter arguments')
337         group.add_argument('--restrict-to-country', metavar='COUNTRY_CODE',
338                            help='Export only objects within country')
339         group.add_argument('--restrict-to-osm-node', metavar='ID', type=int,
340                            help='Export only children of this OSM node')
341         group.add_argument('--restrict-to-osm-way', metavar='ID', type=int,
342                            help='Export only children of this OSM way')
343         group.add_argument('--restrict-to-osm-relation', metavar='ID', type=int,
344                            help='Export only children of this OSM relation')
345
346
347     @staticmethod
348     def run(args):
349         print("TODO: ./utils/export.php", args)
350
351
352 class QueryTodo:
353     """\
354     Todo
355     """
356     @staticmethod
357     def add_args(parser):
358         pass
359
360     def run(args):
361         print("TODO: searching")
362
363
364 def nominatim(**kwargs):
365     """\
366     Command-line tools for importing, updating, administrating and
367     querying the Nominatim database.
368     """
369     parser = CommandlineParser('nominatim', nominatim.__doc__)
370
371     parser.add_subcommand('import', SetupAll)
372     parser.add_subcommand('freeze', SetupFreeze)
373     parser.add_subcommand('replication', UpdateReplication)
374
375     parser.add_subcommand('check-database', AdminCheckDatabase)
376     parser.add_subcommand('warm', AdminWarm)
377
378     parser.add_subcommand('special-phrases', SetupSpecialPhrases)
379
380     parser.add_subcommand('add-data', UpdateAddData)
381     parser.add_subcommand('index', UpdateIndex)
382     parser.add_subcommand('refresh', UpdateRefresh)
383
384     parser.add_subcommand('export', QueryExport)
385     parser.add_subcommand('search', QueryTodo)
386     parser.add_subcommand('reverse', QueryTodo)
387     parser.add_subcommand('lookup', QueryTodo)
388     parser.add_subcommand('details', QueryTodo)
389     parser.add_subcommand('status', QueryTodo)
390
391     parser.run(**kwargs)