]> git.openstreetmap.org Git - nominatim.git/blobdiff - nominatim/api/v1/server_glue.py
add HTML-formatted debug output to lookup
[nominatim.git] / nominatim / api / v1 / server_glue.py
index 7444b7aa07869ab126f45ab144d6d53a75d1fc47..35028526aba6983ed5d7ed221b593fe52caa4cda 100644 (file)
@@ -11,13 +11,16 @@ Combine with the scaffolding provided for the various Python ASGI frameworks.
 from typing import Optional, Any, Type, Callable
 import abc
 
+from nominatim.config import Configuration
 import nominatim.api as napi
+import nominatim.api.logging as loglib
 from nominatim.api.v1.format import dispatch as formatting
 
 CONTENT_TYPE = {
   'text': 'text/plain; charset=utf-8',
   'xml': 'text/xml; charset=utf-8',
-  'jsonp': 'application/javascript'
+  'jsonp': 'application/javascript',
+  'debug': 'text/html; charset=utf-8'
 }
 
 
@@ -40,9 +43,9 @@ class ASGIAdaptor(abc.ABC):
 
 
     @abc.abstractmethod
-    def error(self, msg: str) -> Exception:
+    def error(self, msg: str, status: int = 400) -> Exception:
         """ Construct an appropriate exception from the given error message.
-            The exception must result in a HTTP 400 error.
+            The exception must result in a HTTP error with the given status.
         """
 
 
@@ -59,6 +62,12 @@ class ASGIAdaptor(abc.ABC):
         """
 
 
+    @abc.abstractmethod
+    def config(self) -> Configuration:
+        """ Return the current configuration object.
+        """
+
+
     def build_response(self, output: str, media_type: str, status: int = 200) -> Any:
         """ Create a response from the given output. Wraps a JSONP function
             around the response, if necessary.
@@ -116,6 +125,26 @@ class ASGIAdaptor(abc.ABC):
         return value != '0'
 
 
+    def get_accepted_languages(self) -> str:
+        """ Return the accepted langauges.
+        """
+        return self.get('accept-language')\
+               or self.get_header('http_accept_language')\
+               or self.config().DEFAULT_LANGUAGE
+
+
+    def setup_debugging(self) -> bool:
+        """ Set up collection of debug information if requested.
+
+            Return True when debugging was requested.
+        """
+        if self.get_bool('debug', False):
+            loglib.set_log_output('html')
+            return True
+
+        return False
+
+
 def parse_format(params: ASGIAdaptor, result_type: Type[Any], default: str) -> str:
     """ Get and check the 'format' parameter and prepare the formatter.
         `fmtter` is a formatter and `default` the
@@ -143,11 +172,56 @@ async def status_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) -> A
     else:
         status_code = 200
 
-    return params.build_response(formatting.format_result(result, fmt), fmt,
+    return params.build_response(formatting.format_result(result, fmt, {}), fmt,
                                  status=status_code)
 
+
+async def details_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) -> Any:
+    """ Server glue for /details endpoint. See API docs for details.
+    """
+    place_id = params.get_int('place_id', 0)
+    place: napi.PlaceRef
+    if place_id:
+        place = napi.PlaceID(place_id)
+    else:
+        osmtype = params.get('osmtype')
+        if osmtype is None:
+            raise params.error("Missing ID parameter 'place_id' or 'osmtype'.")
+        place = napi.OsmID(osmtype, params.get_int('osmid'), params.get('class'))
+
+    debug = params.setup_debugging()
+
+    details = napi.LookupDetails(address_details=params.get_bool('addressdetails', False),
+                                 linked_places=params.get_bool('linkedplaces', False),
+                                 parented_places=params.get_bool('hierarchy', False),
+                                 keywords=params.get_bool('keywords', False))
+
+    if params.get_bool('polygon_geojson', False):
+        details.geometry_output = napi.GeometryFormat.GEOJSON
+
+    locales = napi.Locales.from_accept_languages(params.get_accepted_languages())
+
+    result = await api.lookup(place, details)
+
+    if debug:
+        return params.build_response(loglib.get_and_disable(), 'debug')
+
+    if result is None:
+        raise params.error('No place with that OSM ID found.', status=404)
+
+    output = formatting.format_result(
+                 result,
+                 'details-json',
+                 {'locales': locales,
+                  'group_hierarchy': params.get_bool('group_hierarchy', False),
+                  'icon_base_url': params.config().MAPICON_URL})
+
+    return params.build_response(output, 'json')
+
+
 EndpointFunc = Callable[[napi.NominatimAPIAsync, ASGIAdaptor], Any]
 
 ROUTES = [
-    ('status', status_endpoint)
+    ('status', status_endpoint),
+    ('details', details_endpoint)
 ]