File size: 4,820 Bytes
243f395
f2a79fa
3dbe011
7d6e00c
 
150cbc9
c6054f0
7d6e00c
f2a79fa
 
7d6e00c
e945b1f
 
da5737b
243f395
7d6e00c
 
 
 
 
 
 
28ea6eb
7d6e00c
e945b1f
7d6e00c
e945b1f
7d6e00c
 
c6054f0
 
7d6e00c
26b4f04
7d6e00c
e945b1f
 
f2a79fa
 
7d6e00c
f2a79fa
 
 
 
 
 
3dbe011
7d6e00c
f2a79fa
 
7d6e00c
f2a79fa
 
26b4f04
7d6e00c
 
 
 
150cbc9
6f1250c
7d6e00c
 
8c1934b
150cbc9
7d6e00c
150cbc9
f2a79fa
 
7d6e00c
26b4f04
 
e945b1f
26b4f04
e945b1f
7d6e00c
 
26b4f04
 
7d6e00c
26b4f04
e945b1f
 
7d6e00c
 
 
3dbe011
7d6e00c
f2a79fa
6f1250c
 
7d6e00c
6f1250c
c6054f0
7d6e00c
 
da5737b
 
 
 
7d6e00c
da5737b
 
7d6e00c
da5737b
 
6f1250c
7d6e00c
6f1250c
da5737b
7d6e00c
f2a79fa
 
26b4f04
7d6e00c
e945b1f
 
 
7d6e00c
 
 
 
 
f2a79fa
7d6e00c
26b4f04
c6054f0
7d6e00c
26b4f04
f2a79fa
7d6e00c
f2a79fa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import json
import time
from http import HTTPStatus
from typing import Dict, List

from aws_lambda_powertools.event_handler import content_types
from aws_lambda_powertools.utilities.typing import LambdaContext
from geojson_pydantic import FeatureCollection, Feature, Polygon
from pydantic import BaseModel, ValidationError

from src import app_logger
from src.prediction_api.samgeo_predictors import samgeo_fast_predict
from src.utilities.constants import CUSTOM_RESPONSE_MESSAGES, MODEL_NAME, ZOOM, SOURCE_TYPE
from src.utilities.utilities import base64_decode

PolygonFeatureCollectionModel = FeatureCollection[Feature[Polygon, Dict]]


class LatLngTupleLeaflet(BaseModel):
    lat: float
    lng: float


class RequestBody(BaseModel):
    model: str = MODEL_NAME
    ne: LatLngTupleLeaflet
    source_type: str = SOURCE_TYPE
    sw: LatLngTupleLeaflet
    zoom: float = ZOOM


class ResponseBody(BaseModel):
    duration_run: float = None
    geojson: Dict = None
    message: str = None
    request_id: str = None


def get_response(status: int, start_time: float, request_id: str, response_body: ResponseBody = None) -> str:
    """
    Return a response for frontend clients.

    Args:
        status: status response
        start_time: request start time (float)
        request_id: str
        response_body: dict we embed into our response

    Returns:
        str: json response

    """
    app_logger.info(f"response_body:{response_body}.")
    response_body.duration_run = time.time() - start_time
    response_body.message = CUSTOM_RESPONSE_MESSAGES[status]
    response_body.request_id = request_id

    response = {
        "statusCode": status,
        "header": {"Content-Type": content_types.APPLICATION_JSON},
        "body": response_body.model_dump_json(),
        "isBase64Encoded": False
    }
    app_logger.info(f"response type:{type(response)} => {response}.")
    return json.dumps(response)


def get_parsed_bbox_points(request_input: RequestBody) -> Dict:
    model_name = request_input["model"] if "model" in request_input else MODEL_NAME
    zoom = request_input["zoom"] if "zoom" in request_input else ZOOM
    source_type = request_input["source_type"] if "zoom" in request_input else SOURCE_TYPE
    app_logger.info(f"try to validate input request {request_input}...")
    request_body = RequestBody(ne=request_input["ne"], sw=request_input["sw"], model=model_name, zoom=zoom, source_type=source_type)
    return {
        "bbox": [
            request_body.ne.lat, request_body.sw.lat,
            request_body.ne.lng, request_body.sw.lng
        ],
        "model": request_body.model,
        "zoom": request_body.zoom,
        "source_type": request_body.source_type
    }


def lambda_handler(event: dict, context: LambdaContext):
    app_logger.info(f"start with aws_request_id:{context.aws_request_id}.")
    start_time = time.time()

    if "version" in event:
        app_logger.info(f"event version: {event['version']}.")

    try:
        app_logger.info(f"event:{json.dumps(event)}...")
        app_logger.info(f"context:{context}...")

        try:
            body = event["body"]
        except Exception as e_constants1:
            app_logger.error(f"e_constants1:{e_constants1}.")
            body = event

        app_logger.info(f"body: {type(body)}, {body}...")

        if isinstance(body, str):
            body_decoded_str = base64_decode(body)
            app_logger.info(f"body_decoded_str: {type(body_decoded_str)}, {body_decoded_str}...")
            body = json.loads(body_decoded_str)

        app_logger.info(f"body:{body}...")

        try:
            body_request = get_parsed_bbox_points(body)
            app_logger.info(f"validation ok - body_request:{body_request}, starting prediction...")
            output_geojson_dict = samgeo_fast_predict(
                bbox=body_request["bbox"], model_name=body_request["model"], zoom=body_request["zoom"], source_type=body_request["source_type"]
            )

            # raise ValidationError in case this is not a valid geojson by GeoJSON specification rfc7946
            PolygonFeatureCollectionModel(**output_geojson_dict)
            body_response = ResponseBody(geojson=output_geojson_dict)
            response = get_response(HTTPStatus.OK.value, start_time, context.aws_request_id, body_response)
        except ValidationError as ve:
            app_logger.error(f"validation error:{ve}.")
            response = get_response(HTTPStatus.UNPROCESSABLE_ENTITY.value, start_time, context.aws_request_id, ResponseBody())
    except Exception as e:
        app_logger.error(f"exception:{e}.")
        response = get_response(HTTPStatus.INTERNAL_SERVER_ERROR.value, start_time, context.aws_request_id, ResponseBody())

    app_logger.info(f"response_dumped:{response}...")
    return response