diff --git a/space2stats_api/src/space2stats/api/app.py b/space2stats_api/src/space2stats/api/app.py
index 1556543..06da597 100644
--- a/space2stats_api/src/space2stats/api/app.py
+++ b/space2stats_api/src/space2stats/api/app.py
@@ -1,4 +1,5 @@
from contextlib import asynccontextmanager
+from textwrap import dedent
from typing import Any, Dict, List, Optional
import boto3
@@ -6,10 +7,11 @@
from asgi_s3_response_middleware import S3ResponseMiddleware
from fastapi import Depends, FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
-from fastapi.responses import ORJSONResponse
+from fastapi.responses import ORJSONResponse, RedirectResponse
from starlette.requests import Request
from starlette_cramjam.middleware import CompressionMiddleware
+from .. import __version__
from ..lib import StatsTable
from .db import close_db_connection, connect_to_db
from .errors import add_exception_handlers
@@ -31,6 +33,29 @@ async def lifespan(app: FastAPI):
app = FastAPI(
default_response_class=ORJSONResponse,
lifespan=lifespan,
+ title="World Bank Space2Stats API",
+ version=__version__,
+ summary="API for Space2Stats",
+ description=dedent(
+ """
+ The Space2Stats program is designed to provide academics, statisticians, and data
+ scientists with easier access to regularly requested geospatial aggregate data.
+
+ Geographic variables were generated at the hexagon (h3) level 6 and this API enables
+ users to query the data by area of interest and generate aggregate statistics efficiently.
+
+ For more information on the datasets available and usage examples, see the [Space2Stats docs](https://worldbank.github.io/DECAT_Space2Stats/readme.html).
+ """
+ ),
+ contact={
+ "name": "Benjamin Stewart (Task Leader), Development Data Group (DECDG), Worldbank",
+ "url": "https://data.worldbank.org",
+ "email": "bstewart@worldbankgroup.org",
+ },
+ license_info={
+ "name": "MIT",
+ "url": "https://opensource.org/licenses/MIT",
+ },
)
app.add_middleware(
@@ -55,6 +80,60 @@ def stats_table(request: Request):
@app.post("/summary", response_model=List[Dict[str, Any]])
def get_summary(body: SummaryRequest, table: StatsTable = Depends(stats_table)):
+ """Retrieve Statistics from a GeoJSON feature.
+
+ Parameters
+ ----------
+
+
+ - aoi
+ -
+
+ `GeoJSON Feature`
+
+ The Area of Interest, either as a `Feature` or an instance of `AoiModel`
+
+
+ - spatial_join_method
+ -
+
+ `["touches", "centroid", "within"]`
+
+ The method to use for performing the spatial join between the AOI and H3 cells
+
+ - `touches`: Includes H3 cells that touch the AOI
+ - `centroid`: Includes H3 cells where the centroid falls within the AOI
+ - `within`: Includes H3 cells entirely within the AOI
+
+
+
+ - fields
+ -
+
+ `List[str]`
+
+ A list of field names to retrieve from the statistics table.
+
+
+ - geometry
+ -
+
+ `Optional["polygon", "point"]`
+
+ Specifies if the H3 geometries should be included in the response. It can be either "polygon" or "point". If None, geometries are not included
+
+
+
+ Returns
+ -------
+ `List[Dict]`
+
+ A list of dictionaries containing statistical summaries for each H3 cell. Each dictionary contains:
+
+ - `hex_id`: The H3 cell identifier
+ - `geometry` (optional): The geometry of the H3 cell, if geometry is specified.
+ - Other fields from the statistics table, based on the specified `fields`
+ """
try:
return table.summaries(
body.aoi,
@@ -67,6 +146,54 @@ def get_summary(body: SummaryRequest, table: StatsTable = Depends(stats_table)):
@app.post("/aggregate", response_model=Dict[str, float])
def get_aggregate(body: AggregateRequest, table: StatsTable = Depends(stats_table)):
+ """Aggregate Statistics from a GeoJSON feature.
+
+ Parameters
+ ----------
+
+
+ - aoi
+ -
+
+ `GeoJSON Feature`
+
+ The Area of Interest, either as a `Feature` or an instance of `AoiModel`
+
+
+ - spatial_join_method
+ -
+
+ `["touches", "centroid", "within"]`
+
+ The method to use for performing the spatial join between the AOI and H3 cells
+
+ - `touches`: Includes H3 cells that touch the AOI
+ - `centroid`: Includes H3 cells where the centroid falls within the AOI
+ - `within`: Includes H3 cells entirely within the AOI
+
+
+
+ - fields
+ -
+
+ `List[str]`
+
+ A list of field names to retrieve from the statistics table.
+
+
+ - aggregation_type
+ -
+
+ `["sum", "avg", "count", "max", "min"]`
+
+ The manner in which to aggregate the statistics.
+
+
+
+ Returns
+ -------
+ `Dict[str, float]`
+ """
try:
return table.aggregate(
aoi=body.aoi,
@@ -79,11 +206,20 @@ def get_aggregate(body: AggregateRequest, table: StatsTable = Depends(stats_tabl
@app.get("/fields", response_model=List[str])
def fields(table: StatsTable = Depends(stats_table)):
+ """Fields available in the statistics table"""
return table.fields()
+ @app.get("/metadata")
+ def metadata_redirect():
+ """Redirect to project STAC Browser."""
+ return RedirectResponse(
+ "https://radiantearth.github.io/stac-browser/#/external/raw.githubusercontent.com/worldbank/DECAT_Space2Stats/refs/heads/main/space2stats_api/src/space2stats_ingest/METADATA/stac/catalog.json"
+ )
+
@app.get("/")
- def read_root():
- return {"message": "Welcome to Space2Stats!"}
+ def docs_redirect():
+ """Redirect to project documentation."""
+ return RedirectResponse("https://worldbank.github.io/DECAT_Space2Stats")
@app.get("/health")
def health():
diff --git a/space2stats_api/src/tests/test_api.py b/space2stats_api/src/tests/test_api.py
index f57abf3..ffc804e 100644
--- a/space2stats_api/src/tests/test_api.py
+++ b/space2stats_api/src/tests/test_api.py
@@ -20,9 +20,27 @@
def test_read_root(client):
- response = client.get("/")
- assert response.status_code == 200
- assert response.json() == {"message": "Welcome to Space2Stats!"}
+ response = client.get("/", follow_redirects=False)
+ assert response.status_code in [
+ 302,
+ 307,
+ ], f"Unexpected status code: {response.status_code}"
+ assert (
+ response.headers["Location"] == "https://worldbank.github.io/DECAT_Space2Stats"
+ )
+
+
+def test_metadata_redirect(client):
+ response = client.get("/metadata", follow_redirects=False)
+ assert response.status_code in [
+ 302,
+ 307,
+ ], f"Unexpected status code: {response.status_code}"
+ assert response.headers["Location"] == (
+ "https://radiantearth.github.io/stac-browser/#/external/raw.githubusercontent.com/"
+ "worldbank/DECAT_Space2Stats/refs/heads/main/space2stats_api/src/space2stats_ingest/"
+ "METADATA/stac/catalog.json"
+ )
def test_get_summary(client):