def get_place(id: str, request: Request, lang: str = None, type=None, verbosity=DEFAULT_VERBOSITY) -> Place: """Main handler that returns the requested place""" es = get_elasticsearch() verbosity = validate_verbosity(verbosity) lang = validate_lang(lang) # Handle place from "pages jaunes" if id.startswith(pj_source.PLACE_ID_PREFIX): pj_place = pj_source.get_place(id) log_place_request(pj_place, request.headers) return pj_place.load_place(lang, verbosity) # Otherwise handle places from the ES db es_place = fetch_es_place(id, es, INDICES, type) places = { "admin": Admin, "street": Street, "addr": Address, "poi": POI, } loader = places.get(es_place.get("_type")) if loader is None: prometheus.exception("FoundPlaceWithWrongType") raise Exception("Place with id '{}' has a wrong type: '{}'".format( id, es_place[0].get("_type"))) place = loader(es_place["_source"]) log_place_request(place, request.headers) return place.load_place(lang, verbosity)
def get_status(): """Returns the status of the elastic cluster """ es = get_elasticsearch() try: cluster_health = es.cluster.health() except ConnectionError as err: logging.getLogger(__name__).error("Failed to connect to ES: %s", err) cluster_health = {} es_reachable = False else: es_reachable = True es_cluster_health = cluster_health.get("status") in ES_RUNNING_STATUS # the 'ready' is used as the readyness probe. # for the moment idunn is ready if ES is reachable ready = es_cluster_health return { "es": { "reachable": es_reachable, "running": es_cluster_health }, "ready": ready }
def get_poi(id: str, lang: str = None) -> POI: """Handler that returns points-of-interest""" es = get_elasticsearch() if not lang: lang = settings["DEFAULT_LANGUAGE"] lang = lang.lower() es_poi = fetch_es_poi(id, es) return POI(es_poi).load_place(lang, verbosity=DEFAULT_VERBOSITY)
def fetch_es_pois(filters: [MimirPoiFilter], bbox, max_size) -> list: es = get_elasticsearch() left, bot, right, top = bbox[0], bbox[1], bbox[2], bbox[3] should_terms = [] for f in filters: terms = f.get_terms_filters() if terms == []: should_terms.append({"match_all": {}}) else: should_terms.append({ "bool": { "must": [{ "term": { "poi_type.name": term } } for term in terms] } }) # pylint: disable = unexpected-keyword-arg bbox_places = es.search( index=INDICES["poi"], body={ "query": { "bool": { "should": should_terms, "minimum_should_match": 1, "filter": { "geo_bounding_box": { "coord": { "top_left": { "lat": top, "lon": left }, "bottom_right": { "lat": bot, "lon": right }, } } }, } }, "sort": { "weight": "desc" }, }, size=max_size, timeout="3s", ignore_unavailable=True, ) bbox_places = bbox_places.get("hits", {}).get("hits", []) return bbox_places
def get_places_bbox( bbox: Any, category: Optional[List[str]] = Query(None), raw_filter: Optional[List[str]] = Query(None), source: Optional[str] = Query(None), q: Optional[str] = Query(None), size: Optional[int] = Query(None), lang: Optional[str] = Query(None), verbosity: Optional[str] = Query(None), ): es = get_elasticsearch() raw_params = get_raw_params(bbox, category, raw_filter, source, q, size, lang, verbosity) try: params = PlacesQueryParam(**raw_params) except ValidationError as e: logger.info(f"Validation Error: {e.json()}") raise HTTPException(status_code=400, detail=e.errors()) source = params.source if source is None: if params.q: # PJ is currently the only source that accepts arbitrary queries source = SOURCE_PAGESJAUNES elif ( params.category and all(c.get("pj_filters") for c in params.category) and pj_source.bbox_is_covered(params.bbox) ): source = SOURCE_PAGESJAUNES else: source = SOURCE_OSM if source == SOURCE_PAGESJAUNES: all_categories = [pj_category for c in params.category for pj_category in c["pj_filters"]] places_list = pj_source.get_places_bbox( all_categories, params.bbox, size=params.size, query=params.q ) else: # Default source (OSM) if params.raw_filter: raw_filters = params.raw_filter else: raw_filters = [f for c in params.category for f in c["raw_filters"]] bbox_places = fetch_bbox_places( es, INDICES, raw_filters=raw_filters, bbox=params.bbox, max_size=params.size ) places_list = [POI(p["_source"]) for p in bbox_places] return { "places": [p.load_place(params.lang, params.verbosity) for p in places_list], "source": source, }
def place_from_id(id: str, type=None, follow_redirect=False): """ :param id: place id :param type: Optional type to restrict query in Elasticsearch :param follow_redirect: if false, RedirectToPlaceId may be raised :return: Place """ try: namespace, suffix = id.split(":", 1) except ValueError as exc: raise InvalidPlaceId(id) from exc # Handle place from "pages jaunes" if namespace == pj_source.PLACE_ID_NAMESPACE: return pj_source.get_place(id) # Simple latlon place id if namespace == Latlon.PLACE_ID_NAMESPACE: return Latlon.from_id(id) # Otherwise handle places from the ES db es = get_elasticsearch() try: es_place = fetch_es_place(id, es, type) except PlaceNotFound as exc: if namespace == "addr": # A Latlon place can be used as a substitute for a "addr:<lon>;<lat>" id # that is not present in the database anymore try: lon, lat = suffix.split(":", 1)[0].split(";") latlon_id = Latlon(lat=lat, lon=lon).get_id() except ValueError: pass else: if not follow_redirect: raise RedirectToPlaceId(latlon_id) from exc return place_from_id(latlon_id, follow_redirect=False) raise places = { "admin": Admin, "street": Street, "addr": Address, "poi": POI, } loader = places.get(es_place.get("_type")) if loader is None: prometheus.exception("FoundPlaceWithWrongType") raise Exception( f"Place with id '{id}' has a wrong type: '{es_place[0].get('_type')}'" ) return loader(es_place["_source"])
def get_place_latlon(lat: float, lon: float, lang: str = None, verbosity=DEFAULT_VERBOSITY) -> Place: es = get_elasticsearch() verbosity = validate_verbosity(verbosity) lang = validate_lang(lang) try: closest_place = get_closest_place(lat, lon, es) except HTTPException: closest_place = None place = Latlon(lat, lon, closest_address=closest_place) return place.load_place(lang, verbosity)
def closest_address(lat: float, lon: float, lang=None, verbosity=DEFAULT_VERBOSITY) -> Address: if verbosity not in ALL_VERBOSITY_LEVELS: raise HTTPException( status_code=400, detail=f"Unknown verbosity '{verbosity}'. Accepted values are {ALL_VERBOSITY_LEVELS}", ) es = get_elasticsearch() if not lang: lang = settings["DEFAULT_LANGUAGE"] lang = lang.lower() place = get_closest_place(lat, lon, es) return place.load_place(lang, verbosity)
def closest_address( lat: confloat(ge=-90, le=90), lon: confloat(ge=-180, le=180), lang=None, verbosity: Verbosity = Verbosity.default(), ) -> Place: """Find the closest address to a point.""" es = get_elasticsearch() if not lang: lang = settings["DEFAULT_LANGUAGE"] lang = lang.lower() place = get_closest_place(lat, lon, es) return place.load_place(lang, verbosity)
def get_place_latlon( lat: confloat(ge=-90, le=90), lon: confloat(ge=-180, le=180), lang: str = None, verbosity: Verbosity = Verbosity.default(), ) -> Place: """Find the closest place to a point.""" es = get_elasticsearch() lang = validate_lang(lang) try: closest_place = get_closest_place(lat, lon, es) except HTTPException: closest_place = None place = Latlon(lat, lon, closest_address=closest_place) return place.load_place(lang, verbosity)
def get_closest_place(lat: float, lon: float, es=None): if es is None: es = get_elasticsearch() es_addr = fetch_closest(lat, lon, es=es, max_distance=MAX_DISTANCE_IN_METERS) places = { "addr": Address, "street": Street, } loader = places.get(es_addr.get("_type")) if loader is None: logger.warning("Found a place with the wrong type") prometheus.exception("FoundPlaceWithWrongType") raise HTTPException( status_code=404, detail=f"Closest address to '{lat}:{lon}' has a wrong type: '{es_addr.get('_type')}'", ) return loader(es_addr["_source"])