[refactor] first working stage for huggingface docker container with fastapi and loguru
Browse files- .idea/misc.xml +1 -1
- Dockerfile +38 -0
- README.md +53 -35
- dockerfiles/dockerfile-fastapi-fastsam-api +1 -0
- dockerfiles/dockerfile-lambda-fastsam-api +4 -4
- dockerfiles/{dockerfile-lambda-gdal-runner → dockerfile-samgis-base} +6 -5
- events/payload_point.json +1 -1
- events/payload_point2.json +1 -1
- events/payload_point_eolie.json +1 -1
- poetry.lock +129 -1
- pyproject.toml +11 -1
- samgis/__init__.py +31 -2
- samgis/io/lambda_helpers.py +2 -3
- samgis/utilities/type_hints.py +7 -0
- src/fastapi_wrapper.py +122 -0
- static/app.js +35 -0
- static/index.html +44 -0
.idea/misc.xml
CHANGED
@@ -3,5 +3,5 @@
|
|
3 |
<component name="Black">
|
4 |
<option name="sdkName" value="Python 3.11 (samgis)" />
|
5 |
</component>
|
6 |
-
<component name="ProjectRootManager" version="2" project-jdk-name="
|
7 |
</project>
|
|
|
3 |
<component name="Black">
|
4 |
<option name="sdkName" value="Python 3.11 (samgis)" />
|
5 |
</component>
|
6 |
+
<component name="ProjectRootManager" version="2" project-jdk-name="Poetry (samgis)" project-jdk-type="Python SDK" />
|
7 |
</project>
|
Dockerfile
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM localhost/samgis-base-fastapi:latest
|
2 |
+
|
3 |
+
# Include global arg in this stage of the build
|
4 |
+
ARG LAMBDA_TASK_ROOT="/var/task"
|
5 |
+
ARG PYTHONPATH="${LAMBDA_TASK_ROOT}:${PYTHONPATH}:/usr/local/lib/python3/dist-packages"
|
6 |
+
ENV VIRTUAL_ENV=${LAMBDA_TASK_ROOT}/.venv \
|
7 |
+
PATH="${LAMBDA_TASK_ROOT}/.venv/bin:$PATH"
|
8 |
+
|
9 |
+
# Set working directory to function root directory
|
10 |
+
WORKDIR ${LAMBDA_TASK_ROOT}
|
11 |
+
COPY ./samgis ${LAMBDA_TASK_ROOT}/samgis
|
12 |
+
COPY ./src ${LAMBDA_TASK_ROOT}/src
|
13 |
+
COPY ./static ${LAMBDA_TASK_ROOT}/static
|
14 |
+
COPY ./machine_learning_models ${LAMBDA_TASK_ROOT}/machine_learning_models
|
15 |
+
|
16 |
+
RUN ls -l /usr/bin/which
|
17 |
+
RUN /usr/bin/which python
|
18 |
+
RUN python -v
|
19 |
+
RUN echo "PYTHONPATH: ${PYTHONPATH}."
|
20 |
+
RUN echo "PATH: ${PATH}."
|
21 |
+
RUN echo "LAMBDA_TASK_ROOT: ${LAMBDA_TASK_ROOT}."
|
22 |
+
RUN ls -l ${LAMBDA_TASK_ROOT}
|
23 |
+
RUN ls -ld ${LAMBDA_TASK_ROOT}
|
24 |
+
RUN ls -l ${LAMBDA_TASK_ROOT}/machine_learning_models
|
25 |
+
RUN python -c "import sys; print(sys.path)"
|
26 |
+
RUN python -c "import cv2"
|
27 |
+
RUN python -c "import fastapi"
|
28 |
+
RUN python -c "import geopandas"
|
29 |
+
RUN python -c "import loguru"
|
30 |
+
RUN python -c "import onnxruntime"
|
31 |
+
RUN python -c "import rasterio"
|
32 |
+
RUN python -c "import uvicorn"
|
33 |
+
RUN df -h
|
34 |
+
RUN ls -l ${LAMBDA_TASK_ROOT}/samgis/
|
35 |
+
RUN ls -l ${LAMBDA_TASK_ROOT}/src/
|
36 |
+
RUN ls -l ${LAMBDA_TASK_ROOT}/static/
|
37 |
+
|
38 |
+
CMD ["uvicorn", "src.fastapi_wrapper:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
CHANGED
@@ -1,36 +1,64 @@
|
|
1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
##
|
4 |
|
5 |
-
|
6 |
-
2. resolve model paths: OK local
|
7 |
-
3. inference:
|
8 |
-
4. from mask to json (rasterio + geopandas, check for re-projection to EPSG_4326)
|
9 |
-
5. check mandatory dependencies
|
10 |
-
6. check for alternative python interpreters
|
11 |
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
```bash
|
17 |
# clean any old active containers
|
18 |
docker stop $(docker ps -a -q); docker rm $(docker ps -a -q)
|
19 |
|
20 |
# build the base docker image with the docker aws repository tag
|
21 |
-
docker build . -f dockerfiles/dockerfile-
|
22 |
-
|
23 |
-
# OPTIONAL: to build the lambda-gdal-runner image on a x86 machine use the build arg `RIE="https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie"`:
|
24 |
-
docker build . -f dockerfiles/dockerfile-lambda-gdal-runner --build-arg RIE="https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie" --tag 686901913580.dkr.ecr.eu-west-1.amazonaws.com/lambda-gdal-runner --progress=plain
|
25 |
|
26 |
# build the final docker image
|
27 |
-
docker build . -f dockerfiles/dockerfile-lambda-fastsam-api --tag
|
28 |
```
|
29 |
|
30 |
Run the container (keep it on background) and show logs
|
31 |
|
32 |
```bash
|
33 |
-
docker tag
|
34 |
```
|
35 |
|
36 |
Test it with curl:
|
@@ -43,25 +71,15 @@ curl -X 'POST' \
|
|
43 |
-d '{}'
|
44 |
```
|
45 |
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
aws --profile alessandrotrinca_hotmail_aws_console_ec2_lambda ecr get-login-password --region eu-west-1 | docker login --username AWS --password-stdin 686901913580.dkr.ecr.eu-west-1.amazonaws.com
|
50 |
-
```
|
51 |
-
2. Build and tag the docker images, then push them:
|
52 |
-
```
|
53 |
-
docker push 686901913580.dkr.ecr.eu-west-1.amazonaws.com/lambda-gdal-runner:latest
|
54 |
-
docker push 686901913580.dkr.ecr.eu-west-1.amazonaws.com/lambda-fastsam-api:latest
|
55 |
-
```
|
56 |
-
3. It's possible to publish a new aws lambda version from cmd or from lambda page
|
57 |
-
|
58 |
-
|
59 |
-
## Dependencies installation and local tests
|
60 |
-
The docker build process needs only the classic requirements.txt (here renamed to `requirements_dockerfile.txt`), instead for local development and sphinx-docs build
|
61 |
-
there is `Pipfile` (sphinx docs is hosted on Cloudflare Pages).
|
62 |
|
|
|
|
|
|
|
63 |
|
64 |
-
|
65 |
|
66 |
Tests are defined in the `tests` folder in this project. Use PIP to install the test dependencies and run tests.
|
67 |
|
@@ -69,7 +87,7 @@ Tests are defined in the `tests` folder in this project. Use PIP to install the
|
|
69 |
python -m pytest --cov=samgis --cov-report=term-missing && coverage html
|
70 |
```
|
71 |
|
72 |
-
|
73 |
|
74 |
Run the sphinx-apidoc: it's a tool for automatic generation of Sphinx sources that, using the autodoc
|
75 |
extension, document a whole package in the style of other automatic API documentation tools. See the
|
@@ -78,7 +96,7 @@ Run the command from the project root:
|
|
78 |
|
79 |
```bash
|
80 |
# missing docs folder (run from project root)
|
81 |
-
cd docs && sphinx-quickstart -p SamGIS -
|
82 |
|
83 |
# update docs folder (from project root)
|
84 |
sphinx-apidoc -f -o docs samgis
|
|
|
1 |
+
---
|
2 |
+
title: SamGIS
|
3 |
+
emoji: 📉
|
4 |
+
colorFrom: red
|
5 |
+
colorTo: blue
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
---
|
9 |
|
10 |
+
## SamGIS - HuggingFace version
|
11 |
|
12 |
+
Build the docker image this way:
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
+
```bash
|
15 |
+
# clean any old active containers
|
16 |
+
docker stop $(docker ps -a -q); docker rm $(docker ps -a -q)
|
17 |
+
|
18 |
+
# build the base docker image with the ARG DEPENDENCY_GROUP=fastapi used by poetry
|
19 |
+
docker build . -f dockerfiles/dockerfile-samgis-base --build-arg DEPENDENCY_GROUP=fastapi --tag localhost/samgis-base-fastapi --progress=plain
|
20 |
|
21 |
+
# build the image, use the tag "samgis-huggingface"
|
22 |
+
docker build . --tag samgis-huggingface --progress=plain
|
23 |
+
```
|
24 |
+
|
25 |
+
Run the container (keep it on background) and show logs
|
26 |
+
|
27 |
+
```bash
|
28 |
+
docker run -d --name samgis-huggingface -p 7860:7860 samgis-huggingface; docker logs -f samgis-huggingface
|
29 |
+
```
|
30 |
+
|
31 |
+
Test it with curl:
|
32 |
+
|
33 |
+
```bash
|
34 |
+
curl -X 'POST' \
|
35 |
+
'http://localhost:7860/infer_samgeo' \
|
36 |
+
-H 'accept: application/json' \
|
37 |
+
-d '{}'
|
38 |
+
```
|
39 |
+
|
40 |
+
or better visiting the swagger page on http://localhost:7860/docs
|
41 |
+
|
42 |
+
|
43 |
+
## SamGIS - lambda AWS version
|
44 |
+
|
45 |
+
Build the docker image this way:
|
46 |
|
47 |
```bash
|
48 |
# clean any old active containers
|
49 |
docker stop $(docker ps -a -q); docker rm $(docker ps -a -q)
|
50 |
|
51 |
# build the base docker image with the docker aws repository tag
|
52 |
+
docker build . -f dockerfiles/dockerfile-samgis-base --build-arg DEPENDENCY_GROUP=aws_lambda --tag localhost/samgis-base-aws-lambda --progress=plain
|
|
|
|
|
|
|
53 |
|
54 |
# build the final docker image
|
55 |
+
docker build . -f dockerfiles/dockerfile-lambda-fastsam-api --tag localhost/lambda-fastsam-api --progress=plain
|
56 |
```
|
57 |
|
58 |
Run the container (keep it on background) and show logs
|
59 |
|
60 |
```bash
|
61 |
+
docker tag localhost/lambda-fastsam-api:latest localhost/lambda-fastsam-api;docker run -d --name lambda-fastsam-api -p 8080:8080 lambda-fastsam-api; docker logs -f lambda-fastsam-api
|
62 |
```
|
63 |
|
64 |
Test it with curl:
|
|
|
71 |
-d '{}'
|
72 |
```
|
73 |
|
74 |
+
### Publish the aws lambda docker image
|
75 |
+
Login on aws ECR with the correct aws profile (change the example `localhost/` repository url with the one from
|
76 |
+
the [ECR push command instructions page](https://eu-west-1.console.aws.amazon.com/ecr/repositories/)).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
+
### Dependencies installation and local tests
|
79 |
+
The docker build process needs only the base dependency group plus the `aws_lambda` or `fastapi` optional one.
|
80 |
+
Install also the `test` and/or `docs` groups if needed.
|
81 |
|
82 |
+
### Tests
|
83 |
|
84 |
Tests are defined in the `tests` folder in this project. Use PIP to install the test dependencies and run tests.
|
85 |
|
|
|
87 |
python -m pytest --cov=samgis --cov-report=term-missing && coverage html
|
88 |
```
|
89 |
|
90 |
+
### How to update the static documentation with sphinx
|
91 |
|
92 |
Run the sphinx-apidoc: it's a tool for automatic generation of Sphinx sources that, using the autodoc
|
93 |
extension, document a whole package in the style of other automatic API documentation tools. See the
|
|
|
96 |
|
97 |
```bash
|
98 |
# missing docs folder (run from project root)
|
99 |
+
cd docs && sphinx-quickstart -p SamGIS -r 1.0.0 -l python --master index
|
100 |
|
101 |
# update docs folder (from project root)
|
102 |
sphinx-apidoc -f -o docs samgis
|
dockerfiles/dockerfile-fastapi-fastsam-api
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
Dockerfile
|
dockerfiles/dockerfile-lambda-fastsam-api
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
FROM
|
2 |
|
3 |
# Include global arg in this stage of the build
|
4 |
ARG LAMBDA_TASK_ROOT="/var/task"
|
@@ -22,15 +22,15 @@ RUN ls -l ${LAMBDA_TASK_ROOT}
|
|
22 |
RUN ls -ld ${LAMBDA_TASK_ROOT}
|
23 |
RUN ls -l ${LAMBDA_TASK_ROOT}/machine_learning_models
|
24 |
RUN python -c "import sys; print(sys.path)"
|
|
|
|
|
25 |
RUN python -c "import geopandas"
|
26 |
RUN python -c "import onnxruntime"
|
27 |
RUN python -c "import rasterio"
|
28 |
-
RUN python -c "import awslambdaric"
|
29 |
-
RUN python -c "import cv2"
|
30 |
RUN df -h
|
31 |
RUN ls -l /lambda-entrypoint.sh
|
32 |
RUN ls -l ${LAMBDA_TASK_ROOT}/samgis/
|
33 |
RUN ls -l ${LAMBDA_TASK_ROOT}/src/
|
34 |
|
35 |
-
|
36 |
CMD [ "src.lambda_wrapper.lambda_handler" ]
|
|
|
1 |
+
FROM localhost/samgis-base-aws-lambda:latest
|
2 |
|
3 |
# Include global arg in this stage of the build
|
4 |
ARG LAMBDA_TASK_ROOT="/var/task"
|
|
|
22 |
RUN ls -ld ${LAMBDA_TASK_ROOT}
|
23 |
RUN ls -l ${LAMBDA_TASK_ROOT}/machine_learning_models
|
24 |
RUN python -c "import sys; print(sys.path)"
|
25 |
+
RUN python -c "import awslambdaric"
|
26 |
+
RUN python -c "import cv2"
|
27 |
RUN python -c "import geopandas"
|
28 |
RUN python -c "import onnxruntime"
|
29 |
RUN python -c "import rasterio"
|
|
|
|
|
30 |
RUN df -h
|
31 |
RUN ls -l /lambda-entrypoint.sh
|
32 |
RUN ls -l ${LAMBDA_TASK_ROOT}/samgis/
|
33 |
RUN ls -l ${LAMBDA_TASK_ROOT}/src/
|
34 |
|
35 |
+
ENTRYPOINT ["/lambda-entrypoint.sh"]
|
36 |
CMD [ "src.lambda_wrapper.lambda_handler" ]
|
dockerfiles/{dockerfile-lambda-gdal-runner → dockerfile-samgis-base}
RENAMED
@@ -19,12 +19,15 @@ ARG POETRY_VIRTUALENVS_IN_PROJECT
|
|
19 |
ARG POETRY_VIRTUALENVS_CREATE
|
20 |
ARG POETRY_CACHE_DIR
|
21 |
ARG RIE
|
|
|
22 |
|
23 |
RUN echo "ARCH: $ARCH ..."
|
24 |
|
25 |
RUN echo "ARG RIE: $RIE ..."
|
26 |
RUN echo "ARG POETRY_CACHE_DIR: ${POETRY_CACHE_DIR} ..."
|
27 |
RUN echo "ARG PYTHONPATH: $PYTHONPATH ..."
|
|
|
|
|
28 |
|
29 |
# Set working directory to function root directory
|
30 |
WORKDIR ${LAMBDA_TASK_ROOT}
|
@@ -44,7 +47,7 @@ RUN python -m pip install -r ${LAMBDA_TASK_ROOT}/requirements_poetry.txt
|
|
44 |
RUN which poetry && poetry --version && poetry config --list
|
45 |
RUN poetry config virtualenvs.path ${LAMBDA_TASK_ROOT}
|
46 |
RUN echo "# poetry config --list #" && poetry config --list
|
47 |
-
RUN poetry install --with
|
48 |
|
49 |
RUN curl -Lo /usr/local/bin/aws-lambda-rie ${RIE}
|
50 |
|
@@ -62,7 +65,6 @@ ARG LAMBDA_TASK_ROOT
|
|
62 |
ENV VIRTUAL_ENV=${LAMBDA_TASK_ROOT}/.venv \
|
63 |
PATH="${LAMBDA_TASK_ROOT}/.venv/bin:$PATH"
|
64 |
|
65 |
-
COPY --from=builder /usr/local/bin/aws-lambda-rie /usr/local/bin/aws-lambda-rie
|
66 |
RUN echo "COPY --from=builder /usr/lib/${ARCH}-linux-gnu/libGL.so* /usr/lib/${ARCH}-linux-gnu/"
|
67 |
COPY --from=builder /usr/lib/${ARCH}-linux-gnu/libGL.so* /usr/lib/${ARCH}-linux-gnu/
|
68 |
COPY --from=builder ${LAMBDA_TASK_ROOT}/.venv ${LAMBDA_TASK_ROOT}/.venv
|
@@ -71,10 +73,9 @@ RUN echo "new LAMBDA_TASK_ROOT after hidden venv copy => ${LAMBDA_TASK_ROOT}"
|
|
71 |
RUN ls -ld ${LAMBDA_TASK_ROOT}/
|
72 |
RUN ls -lA ${LAMBDA_TASK_ROOT}/
|
73 |
|
|
|
|
|
74 |
RUN chmod +x /usr/local/bin/aws-lambda-rie
|
75 |
-
|
76 |
COPY ./scripts/lambda-entrypoint.sh /lambda-entrypoint.sh
|
77 |
RUN chmod +x /lambda-entrypoint.sh
|
78 |
RUN ls -l /lambda-entrypoint.sh
|
79 |
-
|
80 |
-
ENTRYPOINT ["/lambda-entrypoint.sh"]
|
|
|
19 |
ARG POETRY_VIRTUALENVS_CREATE
|
20 |
ARG POETRY_CACHE_DIR
|
21 |
ARG RIE
|
22 |
+
ARG DEPENDENCY_GROUP
|
23 |
|
24 |
RUN echo "ARCH: $ARCH ..."
|
25 |
|
26 |
RUN echo "ARG RIE: $RIE ..."
|
27 |
RUN echo "ARG POETRY_CACHE_DIR: ${POETRY_CACHE_DIR} ..."
|
28 |
RUN echo "ARG PYTHONPATH: $PYTHONPATH ..."
|
29 |
+
RUN test -n ${DEPENDENCY_GROUP:?}
|
30 |
+
RUN echo "python DEPENDENCY_GROUP: ${DEPENDENCY_GROUP} ..."
|
31 |
|
32 |
# Set working directory to function root directory
|
33 |
WORKDIR ${LAMBDA_TASK_ROOT}
|
|
|
47 |
RUN which poetry && poetry --version && poetry config --list
|
48 |
RUN poetry config virtualenvs.path ${LAMBDA_TASK_ROOT}
|
49 |
RUN echo "# poetry config --list #" && poetry config --list
|
50 |
+
RUN poetry install --with ${DEPENDENCY_GROUP} --no-root
|
51 |
|
52 |
RUN curl -Lo /usr/local/bin/aws-lambda-rie ${RIE}
|
53 |
|
|
|
65 |
ENV VIRTUAL_ENV=${LAMBDA_TASK_ROOT}/.venv \
|
66 |
PATH="${LAMBDA_TASK_ROOT}/.venv/bin:$PATH"
|
67 |
|
|
|
68 |
RUN echo "COPY --from=builder /usr/lib/${ARCH}-linux-gnu/libGL.so* /usr/lib/${ARCH}-linux-gnu/"
|
69 |
COPY --from=builder /usr/lib/${ARCH}-linux-gnu/libGL.so* /usr/lib/${ARCH}-linux-gnu/
|
70 |
COPY --from=builder ${LAMBDA_TASK_ROOT}/.venv ${LAMBDA_TASK_ROOT}/.venv
|
|
|
73 |
RUN ls -ld ${LAMBDA_TASK_ROOT}/
|
74 |
RUN ls -lA ${LAMBDA_TASK_ROOT}/
|
75 |
|
76 |
+
# only used by AWS lambda docker image
|
77 |
+
COPY --from=builder /usr/local/bin/aws-lambda-rie /usr/local/bin/aws-lambda-rie
|
78 |
RUN chmod +x /usr/local/bin/aws-lambda-rie
|
|
|
79 |
COPY ./scripts/lambda-entrypoint.sh /lambda-entrypoint.sh
|
80 |
RUN chmod +x /lambda-entrypoint.sh
|
81 |
RUN ls -l /lambda-entrypoint.sh
|
|
|
|
events/payload_point.json
CHANGED
@@ -9,6 +9,6 @@
|
|
9 |
"label": 0
|
10 |
}],
|
11 |
"zoom": 6,
|
12 |
-
"source_type": "
|
13 |
"debug": true
|
14 |
}
|
|
|
9 |
"label": 0
|
10 |
}],
|
11 |
"zoom": 6,
|
12 |
+
"source_type": "OpenStreetMap",
|
13 |
"debug": true
|
14 |
}
|
events/payload_point2.json
CHANGED
@@ -9,6 +9,6 @@
|
|
9 |
"label": 0
|
10 |
}],
|
11 |
"zoom": 10,
|
12 |
-
"source_type": "
|
13 |
"debug": true
|
14 |
}
|
|
|
9 |
"label": 0
|
10 |
}],
|
11 |
"zoom": 10,
|
12 |
+
"source_type": "OpenStreetMap",
|
13 |
"debug": true
|
14 |
}
|
events/payload_point_eolie.json
CHANGED
@@ -20,5 +20,5 @@
|
|
20 |
}
|
21 |
],
|
22 |
"zoom": 10,
|
23 |
-
"source_type": "
|
24 |
}
|
|
|
20 |
}
|
21 |
],
|
22 |
"zoom": 10,
|
23 |
+
"source_type": "OpenStreetMap"
|
24 |
}
|
poetry.lock
CHANGED
@@ -37,6 +37,26 @@ files = [
|
|
37 |
{file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
|
38 |
]
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
[[package]]
|
41 |
name = "attrs"
|
42 |
version = "23.1.0"
|
@@ -512,6 +532,25 @@ files = [
|
|
512 |
{file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
|
513 |
]
|
514 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
515 |
[[package]]
|
516 |
name = "fiona"
|
517 |
version = "1.9.5"
|
@@ -688,6 +727,17 @@ dev-test = ["coverage", "pytest (>=3.10)", "pytest-asyncio (>=0.17)", "sphinx (<
|
|
688 |
requests = ["requests (>=2.16.2)", "urllib3 (>=1.24.2)"]
|
689 |
timezone = ["pytz"]
|
690 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
691 |
[[package]]
|
692 |
name = "humanfriendly"
|
693 |
version = "10.0"
|
@@ -922,6 +972,24 @@ files = [
|
|
922 |
{file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
|
923 |
]
|
924 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
925 |
[[package]]
|
926 |
name = "markdown-it-py"
|
927 |
version = "3.0.0"
|
@@ -2143,6 +2211,17 @@ files = [
|
|
2143 |
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
2144 |
]
|
2145 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2146 |
[[package]]
|
2147 |
name = "snowballstemmer"
|
2148 |
version = "2.2.0"
|
@@ -2380,6 +2459,23 @@ Sphinx = ">=5"
|
|
2380 |
lint = ["docutils-stubs", "flake8", "mypy"]
|
2381 |
test = ["pytest"]
|
2382 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2383 |
[[package]]
|
2384 |
name = "sympy"
|
2385 |
version = "1.12"
|
@@ -2432,6 +2528,38 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
|
|
2432 |
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
2433 |
zstd = ["zstandard (>=0.18.0)"]
|
2434 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2435 |
[[package]]
|
2436 |
name = "xyzservices"
|
2437 |
version = "2023.10.1"
|
@@ -2446,4 +2574,4 @@ files = [
|
|
2446 |
[metadata]
|
2447 |
lock-version = "2.0"
|
2448 |
python-versions = "^3.11"
|
2449 |
-
content-hash = "
|
|
|
37 |
{file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
|
38 |
]
|
39 |
|
40 |
+
[[package]]
|
41 |
+
name = "anyio"
|
42 |
+
version = "4.2.0"
|
43 |
+
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
44 |
+
optional = false
|
45 |
+
python-versions = ">=3.8"
|
46 |
+
files = [
|
47 |
+
{file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"},
|
48 |
+
{file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"},
|
49 |
+
]
|
50 |
+
|
51 |
+
[package.dependencies]
|
52 |
+
idna = ">=2.8"
|
53 |
+
sniffio = ">=1.1"
|
54 |
+
|
55 |
+
[package.extras]
|
56 |
+
doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
|
57 |
+
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
|
58 |
+
trio = ["trio (>=0.23)"]
|
59 |
+
|
60 |
[[package]]
|
61 |
name = "attrs"
|
62 |
version = "23.1.0"
|
|
|
532 |
{file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
|
533 |
]
|
534 |
|
535 |
+
[[package]]
|
536 |
+
name = "fastapi"
|
537 |
+
version = "0.108.0"
|
538 |
+
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
539 |
+
optional = false
|
540 |
+
python-versions = ">=3.8"
|
541 |
+
files = [
|
542 |
+
{file = "fastapi-0.108.0-py3-none-any.whl", hash = "sha256:8c7bc6d315da963ee4cdb605557827071a9a7f95aeb8fcdd3bde48cdc8764dd7"},
|
543 |
+
{file = "fastapi-0.108.0.tar.gz", hash = "sha256:5056e504ac6395bf68493d71fcfc5352fdbd5fda6f88c21f6420d80d81163296"},
|
544 |
+
]
|
545 |
+
|
546 |
+
[package.dependencies]
|
547 |
+
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
|
548 |
+
starlette = ">=0.29.0,<0.33.0"
|
549 |
+
typing-extensions = ">=4.8.0"
|
550 |
+
|
551 |
+
[package.extras]
|
552 |
+
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
553 |
+
|
554 |
[[package]]
|
555 |
name = "fiona"
|
556 |
version = "1.9.5"
|
|
|
727 |
requests = ["requests (>=2.16.2)", "urllib3 (>=1.24.2)"]
|
728 |
timezone = ["pytz"]
|
729 |
|
730 |
+
[[package]]
|
731 |
+
name = "h11"
|
732 |
+
version = "0.14.0"
|
733 |
+
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
734 |
+
optional = false
|
735 |
+
python-versions = ">=3.7"
|
736 |
+
files = [
|
737 |
+
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
|
738 |
+
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
739 |
+
]
|
740 |
+
|
741 |
[[package]]
|
742 |
name = "humanfriendly"
|
743 |
version = "10.0"
|
|
|
972 |
{file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
|
973 |
]
|
974 |
|
975 |
+
[[package]]
|
976 |
+
name = "loguru"
|
977 |
+
version = "0.7.2"
|
978 |
+
description = "Python logging made (stupidly) simple"
|
979 |
+
optional = false
|
980 |
+
python-versions = ">=3.5"
|
981 |
+
files = [
|
982 |
+
{file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
|
983 |
+
{file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
|
984 |
+
]
|
985 |
+
|
986 |
+
[package.dependencies]
|
987 |
+
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
|
988 |
+
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
989 |
+
|
990 |
+
[package.extras]
|
991 |
+
dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
|
992 |
+
|
993 |
[[package]]
|
994 |
name = "markdown-it-py"
|
995 |
version = "3.0.0"
|
|
|
2211 |
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
2212 |
]
|
2213 |
|
2214 |
+
[[package]]
|
2215 |
+
name = "sniffio"
|
2216 |
+
version = "1.3.0"
|
2217 |
+
description = "Sniff out which async library your code is running under"
|
2218 |
+
optional = false
|
2219 |
+
python-versions = ">=3.7"
|
2220 |
+
files = [
|
2221 |
+
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
|
2222 |
+
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
2223 |
+
]
|
2224 |
+
|
2225 |
[[package]]
|
2226 |
name = "snowballstemmer"
|
2227 |
version = "2.2.0"
|
|
|
2459 |
lint = ["docutils-stubs", "flake8", "mypy"]
|
2460 |
test = ["pytest"]
|
2461 |
|
2462 |
+
[[package]]
|
2463 |
+
name = "starlette"
|
2464 |
+
version = "0.32.0.post1"
|
2465 |
+
description = "The little ASGI library that shines."
|
2466 |
+
optional = false
|
2467 |
+
python-versions = ">=3.8"
|
2468 |
+
files = [
|
2469 |
+
{file = "starlette-0.32.0.post1-py3-none-any.whl", hash = "sha256:cd0cb10ddb49313f609cedfac62c8c12e56c7314b66d89bb077ba228bada1b09"},
|
2470 |
+
{file = "starlette-0.32.0.post1.tar.gz", hash = "sha256:e54e2b7e2fb06dff9eac40133583f10dfa05913f5a85bf26f427c7a40a9a3d02"},
|
2471 |
+
]
|
2472 |
+
|
2473 |
+
[package.dependencies]
|
2474 |
+
anyio = ">=3.4.0,<5"
|
2475 |
+
|
2476 |
+
[package.extras]
|
2477 |
+
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"]
|
2478 |
+
|
2479 |
[[package]]
|
2480 |
name = "sympy"
|
2481 |
version = "1.12"
|
|
|
2528 |
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
2529 |
zstd = ["zstandard (>=0.18.0)"]
|
2530 |
|
2531 |
+
[[package]]
|
2532 |
+
name = "uvicorn"
|
2533 |
+
version = "0.25.0"
|
2534 |
+
description = "The lightning-fast ASGI server."
|
2535 |
+
optional = false
|
2536 |
+
python-versions = ">=3.8"
|
2537 |
+
files = [
|
2538 |
+
{file = "uvicorn-0.25.0-py3-none-any.whl", hash = "sha256:ce107f5d9bd02b4636001a77a4e74aab5e1e2b146868ebbad565237145af444c"},
|
2539 |
+
{file = "uvicorn-0.25.0.tar.gz", hash = "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2"},
|
2540 |
+
]
|
2541 |
+
|
2542 |
+
[package.dependencies]
|
2543 |
+
click = ">=7.0"
|
2544 |
+
h11 = ">=0.8"
|
2545 |
+
|
2546 |
+
[package.extras]
|
2547 |
+
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
2548 |
+
|
2549 |
+
[[package]]
|
2550 |
+
name = "win32-setctime"
|
2551 |
+
version = "1.1.0"
|
2552 |
+
description = "A small Python utility to set file creation time on Windows"
|
2553 |
+
optional = false
|
2554 |
+
python-versions = ">=3.5"
|
2555 |
+
files = [
|
2556 |
+
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
|
2557 |
+
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
|
2558 |
+
]
|
2559 |
+
|
2560 |
+
[package.extras]
|
2561 |
+
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
|
2562 |
+
|
2563 |
[[package]]
|
2564 |
name = "xyzservices"
|
2565 |
version = "2023.10.1"
|
|
|
2574 |
[metadata]
|
2575 |
lock-version = "2.0"
|
2576 |
python-versions = "^3.11"
|
2577 |
+
content-hash = "61953101662fbd1d752c6beb1b60c5ee803794c4ecb9f2484bdc28c884cd077e"
|
pyproject.toml
CHANGED
@@ -18,6 +18,7 @@ python-dotenv = "^1.0.0"
|
|
18 |
rasterio = "^1.3.9"
|
19 |
requests = "^2.31.0"
|
20 |
pillow = "^10.1.0"
|
|
|
21 |
|
22 |
[tool.poetry.group.aws_lambda]
|
23 |
optional = true
|
@@ -26,7 +27,7 @@ optional = true
|
|
26 |
aws-lambda-powertools = "^2.30.2"
|
27 |
awslambdaric = "^2.0.8"
|
28 |
jmespath = "^1.0.1"
|
29 |
-
pydantic = "
|
30 |
|
31 |
[tool.poetry.group.test]
|
32 |
optional = true
|
@@ -45,6 +46,15 @@ sphinx-autodoc-typehints = "^1.25.2"
|
|
45 |
sphinxcontrib-openapi = "^0.8.3"
|
46 |
myst-parser = "^2.0.0"
|
47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
[build-system]
|
49 |
requires = ["poetry-core"]
|
50 |
build-backend = "poetry.core.masonry.api"
|
|
|
18 |
rasterio = "^1.3.9"
|
19 |
requests = "^2.31.0"
|
20 |
pillow = "^10.1.0"
|
21 |
+
loguru = "^0.7.2"
|
22 |
|
23 |
[tool.poetry.group.aws_lambda]
|
24 |
optional = true
|
|
|
27 |
aws-lambda-powertools = "^2.30.2"
|
28 |
awslambdaric = "^2.0.8"
|
29 |
jmespath = "^1.0.1"
|
30 |
+
pydantic = "^2.5.3"
|
31 |
|
32 |
[tool.poetry.group.test]
|
33 |
optional = true
|
|
|
46 |
sphinxcontrib-openapi = "^0.8.3"
|
47 |
myst-parser = "^2.0.0"
|
48 |
|
49 |
+
[tool.poetry.group.fastapi]
|
50 |
+
optional = true
|
51 |
+
|
52 |
+
[tool.poetry.group.fastapi.dependencies]
|
53 |
+
fastapi = "^0.108.0"
|
54 |
+
loguru = "^0.7.2"
|
55 |
+
pydantic = "^2.5.3"
|
56 |
+
uvicorn = "^0.25.0"
|
57 |
+
|
58 |
[build-system]
|
59 |
requires = ["poetry-core"]
|
60 |
build-backend = "poetry.core.masonry.api"
|
samgis/__init__.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
"""Get machine learning predictions from geodata raster images"""
|
2 |
-
from aws_lambda_powertools import Logger
|
3 |
# not used here but contextily_tile is imported in samgis.io.tms2geotiff
|
4 |
from contextily import tile as contextily_tile
|
5 |
from pathlib import Path
|
@@ -9,4 +8,34 @@ from samgis.utilities.constants import SERVICE_NAME
|
|
9 |
|
10 |
PROJECT_ROOT_FOLDER = Path(globals().get("__file__", "./_")).absolute().parent.parent
|
11 |
MODEL_FOLDER = Path(PROJECT_ROOT_FOLDER / "machine_learning_models")
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
"""Get machine learning predictions from geodata raster images"""
|
|
|
2 |
# not used here but contextily_tile is imported in samgis.io.tms2geotiff
|
3 |
from contextily import tile as contextily_tile
|
4 |
from pathlib import Path
|
|
|
8 |
|
9 |
PROJECT_ROOT_FOLDER = Path(globals().get("__file__", "./_")).absolute().parent.parent
|
10 |
MODEL_FOLDER = Path(PROJECT_ROOT_FOLDER / "machine_learning_models")
|
11 |
+
try:
|
12 |
+
from aws_lambda_powertools import Logger
|
13 |
+
|
14 |
+
app_logger = Logger(service=SERVICE_NAME)
|
15 |
+
except ModuleNotFoundError:
|
16 |
+
import loguru
|
17 |
+
|
18 |
+
def setup_logging(debug: bool = False, formatter: str = "{time} - {level} - ({extra[request_id]}) {message} "
|
19 |
+
) -> loguru.logger:
|
20 |
+
"""
|
21 |
+
Create a logging instance with log string formatter.
|
22 |
+
|
23 |
+
Args:
|
24 |
+
debug: logging debug argument
|
25 |
+
formatter: log string formatter
|
26 |
+
|
27 |
+
Returns:
|
28 |
+
Logger
|
29 |
+
|
30 |
+
"""
|
31 |
+
import sys
|
32 |
+
|
33 |
+
logger = loguru.logger
|
34 |
+
logger.remove()
|
35 |
+
level_logger = "DEBUG" if debug else "INFO"
|
36 |
+
logger.add(sys.stdout, format=formatter, level=level_logger)
|
37 |
+
logger.info(f"type_logger:{type(logger)}, logger:{logger}.")
|
38 |
+
return logger
|
39 |
+
|
40 |
+
|
41 |
+
app_logger = setup_logging(debug=True)
|
samgis/io/lambda_helpers.py
CHANGED
@@ -1,12 +1,11 @@
|
|
1 |
"""lambda helper functions"""
|
2 |
from typing import Dict
|
3 |
from xyzservices import providers
|
4 |
-
from aws_lambda_powertools.event_handler import content_types
|
5 |
|
6 |
from samgis import app_logger
|
7 |
from samgis.io.coordinates_pixel_conversion import get_latlng_to_pixel_coordinates
|
8 |
from samgis.utilities.constants import CUSTOM_RESPONSE_MESSAGES
|
9 |
-
from samgis.utilities.type_hints import ApiRequestBody
|
10 |
from samgis.utilities.utilities import base64_decode
|
11 |
|
12 |
|
@@ -34,7 +33,7 @@ def get_response(status: int, start_time: float, request_id: str, response_body:
|
|
34 |
|
35 |
response = {
|
36 |
"statusCode": status,
|
37 |
-
"header": {"Content-Type":
|
38 |
"body": dumps(response_body),
|
39 |
"isBase64Encoded": False
|
40 |
}
|
|
|
1 |
"""lambda helper functions"""
|
2 |
from typing import Dict
|
3 |
from xyzservices import providers
|
|
|
4 |
|
5 |
from samgis import app_logger
|
6 |
from samgis.io.coordinates_pixel_conversion import get_latlng_to_pixel_coordinates
|
7 |
from samgis.utilities.constants import CUSTOM_RESPONSE_MESSAGES
|
8 |
+
from samgis.utilities.type_hints import ApiRequestBody, ContentTypes
|
9 |
from samgis.utilities.utilities import base64_decode
|
10 |
|
11 |
|
|
|
33 |
|
34 |
response = {
|
35 |
"statusCode": status,
|
36 |
+
"header": {"Content-Type": ContentTypes.APPLICATION_JSON},
|
37 |
"body": dumps(response_body),
|
38 |
"isBase64Encoded": False
|
39 |
}
|
samgis/utilities/type_hints.py
CHANGED
@@ -31,6 +31,13 @@ class LatLngDict(BaseModel):
|
|
31 |
lng: float
|
32 |
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
class PromptPointType(str, Enum):
|
35 |
"""Segment Anything: validation point prompt type"""
|
36 |
point = "point"
|
|
|
31 |
lng: float
|
32 |
|
33 |
|
34 |
+
class ContentTypes(str, Enum):
|
35 |
+
"""Segment Anything: validation point prompt type"""
|
36 |
+
APPLICATION_JSON = "application/json"
|
37 |
+
TEXT_PLAIN = "text/plain"
|
38 |
+
TEXT_HTML = "text/html"
|
39 |
+
|
40 |
+
|
41 |
class PromptPointType(str, Enum):
|
42 |
"""Segment Anything: validation point prompt type"""
|
43 |
point = "point"
|
src/fastapi_wrapper.py
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import uuid
|
3 |
+
|
4 |
+
from fastapi import FastAPI, HTTPException, Request, status
|
5 |
+
from fastapi.exceptions import RequestValidationError
|
6 |
+
from fastapi.responses import FileResponse, JSONResponse
|
7 |
+
from fastapi.staticfiles import StaticFiles
|
8 |
+
|
9 |
+
from samgis import app_logger
|
10 |
+
from samgis.io.lambda_helpers import get_parsed_bbox_points
|
11 |
+
from samgis.utilities.type_hints import ApiRequestBody
|
12 |
+
|
13 |
+
app = FastAPI()
|
14 |
+
|
15 |
+
|
16 |
+
@app.middleware("http")
|
17 |
+
async def request_middleware(request, call_next):
|
18 |
+
request_id = str(uuid.uuid4())
|
19 |
+
with app_logger.contextualize(request_id=request_id):
|
20 |
+
app_logger.info("Request started")
|
21 |
+
|
22 |
+
try:
|
23 |
+
response = await call_next(request)
|
24 |
+
|
25 |
+
except Exception as ex:
|
26 |
+
app_logger.error(f"Request failed: {ex}")
|
27 |
+
response = JSONResponse(content={"success": False}, status_code=500)
|
28 |
+
|
29 |
+
finally:
|
30 |
+
response.headers["X-Request-ID"] = request_id
|
31 |
+
app_logger.info(f"Request ended")
|
32 |
+
return response
|
33 |
+
|
34 |
+
|
35 |
+
@app.post("/post_test")
|
36 |
+
async def post_test(request_input: ApiRequestBody) -> JSONResponse:
|
37 |
+
request_body = get_parsed_bbox_points(request_input)
|
38 |
+
app_logger.info(f"request_body:{request_body}.")
|
39 |
+
return JSONResponse(
|
40 |
+
status_code=200,
|
41 |
+
content=get_parsed_bbox_points(request_input)
|
42 |
+
)
|
43 |
+
|
44 |
+
|
45 |
+
@app.get("/hello")
|
46 |
+
async def hello() -> JSONResponse:
|
47 |
+
app_logger.info(f"hello")
|
48 |
+
return JSONResponse(status_code=200, content={"msg": "hello"})
|
49 |
+
|
50 |
+
|
51 |
+
@app.post("/infer_samgis")
|
52 |
+
def samgis(request_input: ApiRequestBody):
|
53 |
+
import subprocess
|
54 |
+
|
55 |
+
from samgis.prediction_api.predictors import samexporter_predict
|
56 |
+
app_logger.info("starting inference request...")
|
57 |
+
|
58 |
+
try:
|
59 |
+
import time
|
60 |
+
|
61 |
+
time_start_run = time.time()
|
62 |
+
body_request = get_parsed_bbox_points(request_input)
|
63 |
+
app_logger.info(f"body_request:{body_request}.")
|
64 |
+
try:
|
65 |
+
output = samexporter_predict(
|
66 |
+
bbox=body_request["bbox"], prompt=body_request["prompt"], zoom=body_request["zoom"],
|
67 |
+
source=body_request["source"]
|
68 |
+
)
|
69 |
+
duration_run = time.time() - time_start_run
|
70 |
+
app_logger.info(f"duration_run:{duration_run}.")
|
71 |
+
body = {
|
72 |
+
"duration_run": duration_run,
|
73 |
+
"output": output
|
74 |
+
}
|
75 |
+
return JSONResponse(status_code=200, content={"body": json.dumps(body)})
|
76 |
+
except Exception as inference_exception:
|
77 |
+
home_content = subprocess.run(
|
78 |
+
"ls -l /var/task", shell=True, universal_newlines=True, stdout=subprocess.PIPE
|
79 |
+
)
|
80 |
+
app_logger.error(f"/home/user ls -l: {home_content.stdout}.")
|
81 |
+
app_logger.error(f"inference error:{inference_exception}.")
|
82 |
+
return HTTPException(status_code=500, detail="Internal server error on inference")
|
83 |
+
except Exception as generic_exception:
|
84 |
+
app_logger.error(f"generic error:{generic_exception}.")
|
85 |
+
return HTTPException(status_code=500, detail="Generic internal server error")
|
86 |
+
|
87 |
+
|
88 |
+
@app.exception_handler(RequestValidationError)
|
89 |
+
async def request_validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
|
90 |
+
app_logger.error(f"exception errors: {exc.errors()}.")
|
91 |
+
app_logger.error(f"exception body: {exc.body}.")
|
92 |
+
headers = request.headers.items()
|
93 |
+
app_logger.error(f'request header: {dict(headers)}.' )
|
94 |
+
params = request.query_params.items()
|
95 |
+
app_logger.error(f'request query params: {dict(params)}.')
|
96 |
+
return JSONResponse(
|
97 |
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
98 |
+
content={"msg": "Error - Unprocessable Entity"}
|
99 |
+
)
|
100 |
+
|
101 |
+
|
102 |
+
@app.exception_handler(HTTPException)
|
103 |
+
async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
|
104 |
+
app_logger.error(f"exception: {str(exc)}.")
|
105 |
+
headers = request.headers.items()
|
106 |
+
app_logger.error(f'request header: {dict(headers)}.' )
|
107 |
+
params = request.query_params.items()
|
108 |
+
app_logger.error(f'request query params: {dict(params)}.')
|
109 |
+
return JSONResponse(
|
110 |
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
111 |
+
content={"msg": "Error - Internal Server Error"}
|
112 |
+
)
|
113 |
+
|
114 |
+
|
115 |
+
# important: the index() function and the app.mount MUST be at the end
|
116 |
+
app.mount("/", StaticFiles(directory="static", html=True), name="static")
|
117 |
+
|
118 |
+
|
119 |
+
@app.get("/")
|
120 |
+
def index() -> FileResponse:
|
121 |
+
return FileResponse(path="/app/static/index.html", media_type="text/html")
|
122 |
+
|
static/app.js
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const jsonBtn = document.getElementById("getJson");
|
2 |
+
const apiBtn = document.getElementById("getApi");
|
3 |
+
const output = document.getElementById("output");
|
4 |
+
const coordsForm = document.getElementById("coords-form");
|
5 |
+
|
6 |
+
function formData2json(dataId, newObj={}) {
|
7 |
+
const formData = new FormData(dataId);
|
8 |
+
formData.forEach(function(value, key){
|
9 |
+
console.log(`formData2json:: key=${key}, value=${value}.`)
|
10 |
+
newObj[key] = value;
|
11 |
+
});
|
12 |
+
return JSON.stringify(newObj);
|
13 |
+
}
|
14 |
+
|
15 |
+
coordsForm.addEventListener('submit', event => {
|
16 |
+
event.preventDefault();
|
17 |
+
console.log("coordsForm::", coordsForm, "#")
|
18 |
+
const inputJson = formData2json(coordsForm)
|
19 |
+
console.log("inputJson", inputJson, "#");
|
20 |
+
|
21 |
+
fetch('/infer_samgis', {
|
22 |
+
method: 'POST', // or 'PUT'
|
23 |
+
body: inputJson, // a FormData will automatically set the 'Content-Type',
|
24 |
+
headers: {"Content-Type": "application/json"},
|
25 |
+
}).then(function (response) {
|
26 |
+
return response.json();
|
27 |
+
}).then(function (data) {
|
28 |
+
console.log("data:", data, "#")
|
29 |
+
output.innerHTML = JSON.stringify(data)
|
30 |
+
}).catch(function (err) {
|
31 |
+
console.log("err:", err, "#")
|
32 |
+
output.innerHTML = `err:${JSON.stringify(err)}.`;
|
33 |
+
});
|
34 |
+
event.preventDefault();
|
35 |
+
});
|
static/index.html
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
7 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" />
|
8 |
+
<title>Fetch API</title>
|
9 |
+
</head>
|
10 |
+
<body>
|
11 |
+
<div class="container">
|
12 |
+
<h1>Fetch API</h1>
|
13 |
+
<form id="coords-form">
|
14 |
+
<label>
|
15 |
+
bbox: x1 <input type="number" id="x-center-form" name="x1" value="-122.1497"/>
|
16 |
+
</label>
|
17 |
+
<label>
|
18 |
+
bbox: x2 <input type="number" id="y-center-form" name="x2" value="37.6311"/>
|
19 |
+
</label>
|
20 |
+
<label>
|
21 |
+
bbox: y1 <input type="number" id="y-center-form" name="y1" value="-122.1203"/>
|
22 |
+
</label>
|
23 |
+
<label>
|
24 |
+
bbox: y2 <input type="number" id="y-center-form" name="y2" value="37.6458"/>
|
25 |
+
</label>
|
26 |
+
<br/><br/>
|
27 |
+
<label>
|
28 |
+
x point: <input type="number" id="x-form" name="x" value="-122.1419"/>
|
29 |
+
</label>
|
30 |
+
<label>
|
31 |
+
y point: <input type="number" id="y-form" name="y" value="37.6383"/>
|
32 |
+
</label>
|
33 |
+
<label>
|
34 |
+
zoom: <input type="number" id="zoom" name="y" value="6"/>
|
35 |
+
</label>
|
36 |
+
<br/><br/>
|
37 |
+
<button type="submit" id="submit-btn">submit form</button>
|
38 |
+
</form>
|
39 |
+
<br><br>
|
40 |
+
<div id="output"></div>
|
41 |
+
</div>
|
42 |
+
<script src="app.js"></script>
|
43 |
+
</body>
|
44 |
+
</html>
|