Пример #1
0
def search_for_addresses(request_body, search_type):
    current_app.logger.info("Performing '%s' address search", search_type)
    search_results = g.requests.post(config.ADDRESS_API_URL +
                                     '/v1/addresses/search',
                                     data=json.dumps(request_body,
                                                     sort_keys=True),
                                     headers={
                                         "Content-Type": "application/json",
                                         "Accept": "application/json"
                                     })

    if search_results.status_code == 400:
        raise ApplicationError(search_results.json(), 400, 400)
    if search_results.status_code != 200:
        raise ApplicationError(search_results.json(), 500, 500)
    if not search_results.json():
        current_app.logger.warning("No addresses found for search")
        raise ApplicationError("No addresses found for search.", 404, 404)

    current_app.logger.info("Returning address search result")
    return Response(response=json.dumps(
        address_response_mapper.map_address_response(search_results.json(),
                                                     search_type)),
                    status=200,
                    mimetype="application/json")
Пример #2
0
def search_for_addresses(request_body):
    current_app.logger.info("Performing address search")
    search_results = g.requests.post(config.ADDRESS_API_URL +
                                     '/v2/addresses/search',
                                     data=json.dumps(request_body,
                                                     sort_keys=True),
                                     headers={
                                         "Content-Type": "application/json",
                                         "Accept": "application/json"
                                     })
    if search_results.status_code == 400:
        raise ApplicationError(search_results.json(), 400, 400)
    if search_results.status_code != 200:
        raise ApplicationError(search_results.json(), 500, 500)
    if not search_results.json():
        current_app.logger.warning("No addresses found for search")
        raise ApplicationError("No addresses found for search.", 404, 404)

    mapped_resp = address_response_mapper_v_2.map_address_response(
        search_results.json())

    # If index_map argument set to true, get index map for all the addresses
    if request.args.get('index_map') and request.args.get(
            'index_map').lower() == 'true' and config.INDEX_MAP_API_URL:
        for address in mapped_resp:
            if 'uprn' in address and address['uprn']:
                index_map = search_for_index_map(address['uprn'])
                if index_map:
                    address['index_map'] = index_map

    current_app.logger.info("Returning address search result")
    return Response(response=json.dumps(mapped_resp),
                    status=200,
                    mimetype="application/json")
Пример #3
0
def search_for_index_map(uprn):
    current_app.logger.info("Performing uprn title search")

    title_resp = g.requests.get('{}/v1/uprns/{}'.format(
        config.INDEX_MAP_API_URL, uprn),
                                headers={
                                    "Content-Type": "application/json",
                                    "Accept": "application/json"
                                })
    if title_resp.status_code == 200:
        features = []
        for title in title_resp.json():
            current_app.logger.info("Performing index map search")
            index_map_resp = g.requests.get('{}/v1/index_map/{}'.format(
                config.INDEX_MAP_API_URL, title),
                                            headers={
                                                "Content-Type":
                                                "application/json",
                                                "Accept": "application/json"
                                            })
            if index_map_resp.status_code == 200:
                index_map_json = index_map_resp.json()
                features = features + index_map_json['features']
            elif index_map_resp.status_code != 404:
                raise ApplicationError(index_map_resp.json(), 500, 500)
        if features:
            return {"type": "FeatureCollection", "features": features}
    elif title_resp.status_code != 404:
        raise ApplicationError(title_resp.json(), 500, 500)

    return None
def get_results_for_boundary(boundary, charge_filter, max_results):
    """Returns the results of land charges contained in a extent

    :param boundary: Extent to search for land charges within
    :param charge_filter: String indicating whether to filter out cancelled charges
    :param max_results: Max number of land charges to be returned.  Defaults to 1000
    :return: Json representation of the results
    """
    try:
        extent_shape = asShape(boundary)
        geo_extent_shape = shape.from_shape(unary_union(extent_shape),
                                            srid=27700)

        subquery = db.session.query(GeometryFeature.local_land_charge_id, GeometryFeature.geometry) \
            .distinct(GeometryFeature.local_land_charge_id) \
            .filter(func.ST_DWithin(GeometryFeature.geometry, geo_extent_shape, 0)) \
            .subquery()

        if charge_filter:
            charge_query = LocalLandCharge.query \
                .filter(LocalLandCharge.id == subquery.c.local_land_charge_id) \
                .filter(or_(~func.ST_Touches(subquery.c.geometry, geo_extent_shape),
                            ~LocalLandCharge.llc_item.contains(
                                {'charge-sub-category': 'Conditional planning consent'}))) \
                .filter(LocalLandCharge.cancelled.isnot(True)) \
                .order_by(LocalLandCharge.llc_item[SORT_BY_FIELD].desc())
        else:
            charge_query = LocalLandCharge.query \
                .filter(LocalLandCharge.id == subquery.c.local_land_charge_id) \
                .filter(or_(~func.ST_Touches(subquery.c.geometry, geo_extent_shape),
                            ~LocalLandCharge.llc_item.contains(
                                {'charge-sub-category': 'Conditional planning consent'}))) \
                .order_by(LocalLandCharge.llc_item[SORT_BY_FIELD].desc())

        num_results = charge_query.count()
        if num_results > max_results:
            current_app.logger.info("Search-area: {0}, "
                                    "Number-of-charges: {1}, "
                                    "Normal-limit: {2}, "
                                    "Too many charges returned".format(
                                        boundary, num_results, max_results))
            raise ApplicationError("Too many charges, search a smaller area",
                                   507, 507)

        llc_result = charge_query.all()

        if llc_result and len(llc_result) > 0:
            current_app.logger.info("Returning local land charges")
            return json.dumps(
                model_mappers.map_llc_result_to_dictionary_list(
                    llc_result)), 200, {
                        'Content-Type': 'application/json'
                    }
        else:
            raise ApplicationError("No land charges found", 404, 404)
    except (ValueError, TypeError) as err:
        raise ApplicationError("Unprocessable Entity. {}".format(err), 422,
                               422)
def get_results_for_originating_authority_charge(authority_charge_id,
                                                 migrating_authority):
    """Returns the results of land charges which match a given originating authority charge identifier

    :param originating_authority_charge_identifier: originating authority charge identifier to search by
    :return: Json representation of the results
    """

    features = LocalLandCharge.query \
        .filter(
            LocalLandCharge.llc_item["originating-authority-charge-identifier"].astext == authority_charge_id,
            LocalLandCharge.llc_item["migrating-authority"].astext == migrating_authority
        ).order_by(LocalLandCharge.llc_item[SORT_BY_FIELD].desc()) \
        .all()

    if features and len(features) > 0:
        current_app.logger.info(
            "Returning local land charges by originating authority & migrating authority"
        )

        return json.dumps(
            model_mappers.map_llc_display_result_to_dictionary_list(
                features)), 200, {
                    'Content-Type': 'application/json'
                }
    else:
        raise ApplicationError("No land charges found", 404, 404)
def get_current_timestamp():
    cur = None
    conn = None
    try:
        conn = psycopg2.connect(current_app.config['SQLALCHEMY_DATABASE_URI'])
        cur = conn.cursor()
        cur.execute("SELECT CURRENT_TIMESTAMP;")
        result = cur.fetchone()
        return result[0]
    except psycopg2.DataError as e:
        raise ApplicationError(
            'Input data error: ' + str(e), 'DB', http_code=400)
    except (psycopg2.OperationalError, psycopg2.ProgrammingError) as e:
        raise ApplicationError(
            'Database error: ' + str(e), 'DB', http_code=400)
    finally:
        if cur:
            cur.close()
        if conn:
            conn.close()
def get_local_land_charge(charge_id):
    """Get local land charge

    Returns local land charge with specified charge_id
    """
    current_app.logger.info("Get local land charge by ID '%s'", charge_id)
    charge_id_param = charge_id.upper()

    if is_valid_charge_id(charge_id_param):
        charge_id_base_10 = decode_charge_id(charge_id_param)
        llc_result = LocalLandCharge.query.get(charge_id_base_10)
    else:
        raise ApplicationError("Invalid Land Charge Number", 422, 422)

    if not llc_result:
        raise ApplicationError("No land charges found.", 404, 404)

    current_app.logger.info("Returning local land charge '%s'", charge_id)
    return json.dumps(model_mappers.map_llc_result_to_dictionary_list(llc_result)), \
        200, {'Content-Type': 'application/json'}
Пример #8
0
def get_addresses_by_uprn(uprn):
    current_app.logger.info("Get address by UPRN '%s'", uprn)
    uprn = uprn.strip()
    uprn_is_valid = re.match(uprn_regex_check, uprn)

    if uprn_is_valid is not None:
        body["search_type"] = "uprn"
        body["query_value"] = int(uprn)
        return search_for_addresses(body, "uprn")
    else:
        raise ApplicationError("Unprocessable Entity: UPRN is not valid", 422,
                               422)
Пример #9
0
def get_addresses_by_postcode(postcode):
    current_app.logger.info("Get address by postcode '%s'", postcode)
    postcode = postcode.strip()
    postcode_is_valid = re.match(postcode_regex_check, postcode)

    if postcode_is_valid is not None:
        body["search_type"] = "postcode"
        body["query_value"] = postcode
        return search_for_addresses(body, "postcode")
    else:
        raise ApplicationError("Unprocessable Entity: Postcode is not valid",
                               422, 422)
def get_local_land_charges():
    """Get a list of land charges

    Returns all if no parameter is required, otherwise it will return
    those contained in bounding box.
    """
    current_app.logger.info("Get local land charges by geometry search")
    geo_json_extent = request.args.get('boundingBox')
    if geo_json_extent:
        try:
            json_extent = json.loads(
                base64.b64decode(geo_json_extent).decode())
            extent_shape = asShape(json_extent)

            features = GeometryFeature.query.filter(
                func.ST_Intersects(
                    GeometryFeature.geometry,
                    shape.from_shape(extent_shape, srid=27700))).options(
                        db.joinedload(
                            GeometryFeature.local_land_charge)).all()
            res_set = set()
            for feature in features:
                res_set.add(feature.local_land_charge)
            llc_result = list(res_set)

        except (ValueError, TypeError) as err:
            raise ApplicationError("Unprocessable Entity. {}".format(err), 422,
                                   422)
    else:
        current_app.logger.warning(
            "No bounding box supplied - returning all local land charges")
        llc_result = LocalLandCharge.query.all()

    if not llc_result or len(llc_result) == 0:
        raise ApplicationError("No land charges found", 404, 404)

    current_app.logger.info("Returning local land charges")
    return json.dumps(model_mappers.map_llc_result_to_dictionary_list(llc_result)), \
        200, {'Content-Type': 'application/json'}
def post_local_land_charges():
    """Get a list of land charges"""
    current_app.logger.info("Get local land charges by geometry search")

    geo_json_extent = request.get_json()
    charge_filter = request.args.get('filter')
    max_results = (int(request.args.get('maxResults'))
                   if request.args.get('maxResults') else 1000)

    if geo_json_extent:
        return get_results_for_boundary(geo_json_extent, charge_filter,
                                        max_results)
    else:
        raise ApplicationError("Failed to provide a search area.", 400, 400)
def get_local_land_charge_history(charge_id):
    """Get history of land charge.

    Returns all history for local land charge with charge_id
    """
    current_app.logger.info("Get local land charge history by ID '%s'",
                            charge_id)
    charge_id_param = charge_id.upper()

    if is_valid_charge_id(charge_id_param):
        charge_id_base_10 = decode_charge_id(charge_id_param)

        llc_result = LocalLandChargeHistory.query.filter(LocalLandChargeHistory.id == charge_id_base_10)\
            .order_by(LocalLandChargeHistory.entry_timestamp).all()
    else:
        raise ApplicationError("Invalid Land Charge Number", 422, 422)

    if not llc_result:
        raise ApplicationError("No land charge history found.", 404, 404)

    current_app.logger.info("Returning local land charge history")
    return json.dumps(model_mappers.map_llc_history_result_to_dictionary_list(llc_result)), \
        200, {'Content-Type': 'application/json'}
Пример #13
0
def before_request():
    # Sets the transaction trace id into the global object if it has been provided in the HTTP header from the caller.
    # Generate a new one if it has not. We will use this in log messages.
    g.trace_id = request.headers.get('X-Trace-ID', uuid.uuid4().hex)
    # We also create a session-level requests object for the app to use with the header pre-set, so other APIs will
    # receive it. These lines can be removed if the app will not make requests to other LR APIs!
    g.requests = requests.Session()
    g.requests.headers.update({'X-Trace-ID': g.trace_id})

    if '/health' in request.path:
        return

    if 'Authorization' not in request.headers:
        raise ApplicationError("Missing Authorization header", "AUTH1", 401)

    try:
        validate(
            app.config['AUTHENTICATION_API_URL'] + '/authentication/validate',
            request.headers['Authorization'], g.requests)
    except ValidationFailure as fail:
        raise ApplicationError(fail.message, "AUTH1", 401)

    bearer_jwt = request.headers['Authorization']
    g.requests.headers.update({'Authorization': bearer_jwt})
def get_local_land_charges():
    """Get a list of land charges"""
    current_app.logger.info("Get local land charges by filter search")

    further_information_reference = request.args.get(
        'furtherInformationReference')
    authority_charge_id = request.args.get('authority_charge_id')
    migrating_authority = request.args.get('migrating_authority')

    if further_information_reference:
        return get_results_for_further_information_reference(
            further_information_reference)
    elif (authority_charge_id and migrating_authority):
        return get_results_for_originating_authority_charge(
            authority_charge_id, migrating_authority)
    else:
        raise ApplicationError("Failed to provide a filter.", 400, 400)
def encode_base_31(charge_id):
    """Encodes the given base 10 charge_id into a base 31 string.

    Throws ApplicationError if charge_id is less than ChargeId.FLOOR or greater than ChargeId.CEILING.
    """
    if charge_id < ChargeId.FLOOR or charge_id > ChargeId.CEILING:
        raise ApplicationError(
            'The given charge id ({}) is less than the allowed floor ({}), or greater than the '
            'allowed ceiling ({})'.format(charge_id, ChargeId.FLOOR,
                                          ChargeId.CEILING), 500)

    encoded = ''
    while charge_id > 0:
        charge_id, remainder = divmod(charge_id, 31)
        encoded = ChargeId.CHARACTERS[remainder] + encoded

    return encoded
def get_results_for_further_information_reference(
        further_information_reference):
    """Returns the results of land charges which match a given further information reference

    :param further_information_reference: Authority reference to search by
    :return: Json representation of the results
    """
    features = LocalLandCharge.query \
        .filter(
            func.lower(LocalLandCharge.further_information_reference) == func.lower(further_information_reference)
        ).order_by(LocalLandCharge.llc_item[SORT_BY_FIELD].desc()) \
        .all()

    if features and len(features) > 0:
        current_app.logger.info("Returning local land charges")
        return json.dumps(
            model_mappers.map_llc_result_to_dictionary_list(features)), 200, {
                'Content-Type': 'application/json'
            }
    else:
        raise ApplicationError("No land charges found", 404, 404)