Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 1 addition & 27 deletions ohsome_quality_api/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
)
from fastapi.responses import JSONResponse
from fastapi_i18n import i18n
from geojson import FeatureCollection
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.staticfiles import StaticFiles

Expand All @@ -28,7 +27,6 @@
from ohsome_quality_api.api.request_models import (
AttributeCompletenessFilterRequest,
AttributeCompletenessKeyRequest,
IndicatorDataRequest,
IndicatorRequest,
LandCoverThematicAccuracyRequest,
)
Expand Down Expand Up @@ -70,10 +68,8 @@
from ohsome_quality_api.utils.exceptions import (
OhsomeApiError,
SizeRestrictionError,
TopicDataSchemaError,
)
from ohsome_quality_api.utils.helper import (
get_class_from_key,
get_project_root,
json_serialize,
)
Expand Down Expand Up @@ -182,11 +178,10 @@ async def validation_exception_handler(
)


@app.exception_handler(TopicDataSchemaError)
@app.exception_handler(OhsomeApiError)
@app.exception_handler(SizeRestrictionError)
async def custom_exception_handler(
_: Request, exception: TopicDataSchemaError | OhsomeApiError | SizeRestrictionError
_: Request, exception: OhsomeApiError | SizeRestrictionError
):
"""Exception handler for custom exceptions."""
return JSONResponse(
Expand Down Expand Up @@ -228,27 +223,6 @@ def empty_api_response() -> dict:
}


@app.post("/indicators/mapping-saturation/data", include_in_schema=False)
async def post_indicator_ms(parameters: IndicatorDataRequest) -> CustomJSONResponse:
"""Legacy support for computing the Mapping Saturation indicator for given data."""
indicators = await main.create_indicator(
key="mapping-saturation",
bpolys=parameters.bpolys,
topic=parameters.topic,
include_figure=parameters.include_figure,
)
geojson_object = FeatureCollection(
features=[i.as_feature(parameters.include_data) for i in indicators]
)
response = empty_api_response()
response["attribution"]["text"] = get_class_from_key(
class_type="indicator",
key="mapping-saturation",
).attribution()
response["result"] = [feature.properties for feature in geojson_object.features]
return CustomJSONResponse(content=response, media_type=MEDIA_TYPE_JSON)


@app.post(
"/indicators/attribute-completeness",
tags=["indicator"],
Expand Down
13 changes: 1 addition & 12 deletions ohsome_quality_api/api/request_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from ohsome_quality_api.attributes.definitions import AttributeEnum, get_attributes
from ohsome_quality_api.indicators.definitions import get_valid_indicators
from ohsome_quality_api.topics.definitions import TopicEnum, get_topic_preset
from ohsome_quality_api.topics.models import Topic, TopicData
from ohsome_quality_api.topics.models import Topic
from ohsome_quality_api.utils.helper import snake_to_lower_camel


Expand Down Expand Up @@ -231,14 +231,3 @@ def validate_indicator_topic_combination(self):
)
)
return self


class IndicatorDataRequest(BaseBpolys):
"""Model for the `/indicators/mapping-saturation/data` endpoint.

The Topic consists of name, description and data.
"""

topic: TopicData = Field(..., title="Topic", alias="topic")
include_figure: bool = True
include_data: bool = False
9 changes: 3 additions & 6 deletions ohsome_quality_api/indicators/mapping_saturation/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from ohsome_quality_api.indicators.base import BaseIndicator
from ohsome_quality_api.indicators.mapping_saturation import models
from ohsome_quality_api.ohsome import client as ohsome_client
from ohsome_quality_api.topics.models import Topic, TopicData
from ohsome_quality_api.topics.models import Topic

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -50,7 +50,7 @@ class MappingSaturation(BaseIndicator):

def __init__(
self,
topic: Topic | TopicData,
topic: Topic,
feature: Feature,
time_range: str = "2008-01-01//P1M",
) -> None:
Expand Down Expand Up @@ -190,10 +190,7 @@ def create_figure(self) -> None:
title_text=_("Date"),
ticks="outside",
)
if isinstance(self.topic, TopicData):
fig.update_yaxes(title_text=_("Value"))
else:
fig.update_yaxes(title_text=self.topic.aggregation_type.capitalize())
fig.update_yaxes(title_text=self.topic.aggregation_type.capitalize())

# plot asymptote
asymptote = self.data["best_fit"]["asymptote"]
Expand Down
4 changes: 2 additions & 2 deletions ohsome_quality_api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from geojson import Feature, FeatureCollection

from ohsome_quality_api.indicators.base import BaseIndicator as Indicator
from ohsome_quality_api.topics.models import Topic, TopicData
from ohsome_quality_api.topics.models import Topic
from ohsome_quality_api.utils.helper import get_class_from_key
from ohsome_quality_api.utils.helper_asyncio import gather_with_semaphore
from ohsome_quality_api.utils.validators import validate_area
Expand All @@ -17,7 +17,7 @@
async def create_indicator(
key: str,
bpolys: FeatureCollection,
topic: TopicData | Topic,
topic: Topic,
include_figure: bool = True,
**kwargs,
) -> list[Indicator]:
Expand Down
45 changes: 3 additions & 42 deletions ohsome_quality_api/ohsome/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import datetime
import json
from functools import singledispatch

import geojson
import httpx
Expand All @@ -15,20 +14,11 @@
from json import JSONDecodeError

from ohsome_quality_api.config import get_config_value
from ohsome_quality_api.topics.models import Topic, TopicData
from ohsome_quality_api.utils.exceptions import OhsomeApiError, TopicDataSchemaError
from ohsome_quality_api.topics.models import Topic
from ohsome_quality_api.utils.exceptions import OhsomeApiError


@singledispatch
async def query(topic) -> dict:
"""Query ohsome API."""
raise NotImplementedError(
"Cannot query ohsome API for Topic of type: " + str(type(topic))
)


@query.register
async def _(
async def query(
topic: Topic,
bpolys: Feature | FeatureCollection,
time: str | None = None,
Expand Down Expand Up @@ -66,35 +56,6 @@ async def _(
return validate_query_results(response, attribute_filter, group_by_boundary)


@query.register
async def _(
topic: TopicData,
bpolys: Feature | FeatureCollection,
attribute_filter: str | None = False,
group_by_boundary: bool | None = False,
**_kargs,
) -> dict:
"""Validate data attached to the Topic object and return data.

Data will only be validated and returned immediately.
The ohsome API will not be queried.

Args:
topic: Topic with name, description and data attached to it.
bpolys: Feature for a single bounding (multi)polygon.
FeatureCollection for "group by boundaries" queries. In this case the
argument 'group_by' needs to be set to 'True'.
group_by: Group by boundary.
"""
try:
return validate_query_results(topic.data, attribute_filter, group_by_boundary)
except SchemaError as error:
raise TopicDataSchemaError(
"Invalid Topic data input to the Mapping Saturation Indicator.",
error,
) from error


async def query_ohsome_api(url: str, data: dict) -> dict:
"""Query the ohsome API.

Expand Down
29 changes: 10 additions & 19 deletions ohsome_quality_api/topics/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@
from ohsome_quality_api.utils.helper import snake_to_lower_camel


class BaseTopic(BaseModel):
class Topic(BaseModel):
"""Includes the ohsome API endpoint and parameters needed to retrieve the data."""

key: str
name: str
description: str
endpoint: Literal["elements"]
aggregation_type: Literal["area", "count", "length", "perimeter", "area/density"]
filter: str
indicators: list[str]
projects: list[ProjectEnum]
source: str | None = None
ratio_filter: str | None = None
model_config = ConfigDict(
alias_generator=snake_to_lower_camel,
extra="forbid",
Expand All @@ -30,21 +39,3 @@ class BaseTopic(BaseModel):
@classmethod
def translate(cls, value: str) -> str:
return _(value)


class Topic(BaseTopic):
"""Includes the ohsome API endpoint and parameters needed to retrieve the data."""

endpoint: Literal["elements"]
aggregation_type: Literal["area", "count", "length", "perimeter", "area/density"]
filter: str
indicators: list[str]
projects: list[ProjectEnum]
source: str | None = None
ratio_filter: str | None = None


class TopicData(BaseTopic):
"""Includes the data associated with the topic."""

data: dict
7 changes: 0 additions & 7 deletions ohsome_quality_api/utils/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Custom exception classes."""

from fastapi_i18n import _
from schema import SchemaError


class OhsomeApiError(Exception):
Expand Down Expand Up @@ -31,9 +30,3 @@ class EmptyRecordError(DatabaseError):
def __init__(self):
self.name = "EmptyRecordError"
self.message = _("Query returned no record.")


class TopicDataSchemaError(Exception):
def __init__(self, message, schema_error: SchemaError):
self.name = "TopicDataSchemaError"
self.message = "{0}\n{1}".format(message, schema_error)

This file was deleted.

21 changes: 0 additions & 21 deletions tests/integrationtests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import pytest

from ohsome_quality_api import main
from ohsome_quality_api.topics.models import TopicData
from tests.integrationtests.utils import oqapi_vcr


Expand Down Expand Up @@ -103,26 +102,6 @@ def test_create_indicator_size_limit_bpolys_ms(bpolys, topic_building_count):
)


@mock.patch.dict("os.environ", {"OQAPI_GEOM_SIZE_LIMIT": "1"}, clear=True)
@oqapi_vcr.use_cassette
def test_create_indicator_size_limit_bpolys_data(bpolys):
# Size limit is disabled for request with custom data.
topic = TopicData(
key="key",
name="name",
description="description",
data={
"result": [
{
"value": 1.0,
"timestamp": "2020-03-20T01:30:08.180856",
}
]
},
)
asyncio.run(main.create_indicator("mapping-saturation", bpolys, topic))


@oqapi_vcr.use_cassette
def test_create_indicator_public_feature_collection_single_attribute_completeness(
bpolys, topic_building_count, attribute_key
Expand Down
Loading