Skip to content

Commit

Permalink
refactor(indicators): separate templates from metadata
Browse files Browse the repository at this point in the history
separate indicators text templates for label and result description from
metadata.
  • Loading branch information
matthiasschaub committed Sep 26, 2024
1 parent 5b6135a commit 11d6efb
Show file tree
Hide file tree
Showing 24 changed files with 158 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def calculate(self) -> None:
self.result.value = None
if self.result.value is None:
return
description = Template(self.metadata.result_description).substitute(
description = Template(self.templates.result_description).substitute(
result=round(self.result.value, 2),
all=round(self.absolute_value_1, 1),
matched=round(self.absolute_value_2, 1),
Expand All @@ -79,17 +79,17 @@ def calculate(self) -> None:
if self.result.value >= self.threshold_yellow:
self.result.class_ = 5
self.result.description = (
description + self.metadata.label_description["green"]
description + self.templates.label_description["green"]
)
elif self.threshold_yellow > self.result.value >= self.threshold_red:
self.result.class_ = 3
self.result.description = (
description + self.metadata.label_description["yellow"]
description + self.templates.label_description["yellow"]
)
else:
self.result.class_ = 1
self.result.description = (
description + self.metadata.label_description["red"]
description + self.templates.label_description["red"]
)

def create_figure(self) -> None:
Expand Down
12 changes: 0 additions & 12 deletions ohsome_quality_api/indicators/attribute_completeness/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,3 @@ attribute-completeness:
Derive the ratio of OSM features compared to features which
match additional expected tags (e.g. amenity=hospital vs
amenity=hospital and wheelchair=yes).
label_description:
red: >-
Less than 25% of the features match the expected tags.
yellow: >-
Around 25-75% of the features match the expected tags.
green: >-
More than 75% of the features match the expected tags.
undefined: >-
The quality level could not be calculated for this indicator.
result_description: >-
The ratio of the features (all: $all) compared to features with
expected tags (matched: $matched) is $result.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
label_description:
red: >-
Less than 25% of the features match the expected tags.
yellow: >-
Around 25-75% of the features match the expected tags.
green: >-
More than 75% of the features match the expected tags.
undefined: >-
The quality level could not be calculated for this indicator.
result_description: >-
The ratio of the features (all: $all) compared to features with
expected tags (matched: $matched) is $result.
33 changes: 26 additions & 7 deletions ohsome_quality_api/indicators/base.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import json
import os
from abc import ABCMeta, abstractmethod

import plotly.graph_objects as go
import yaml
from geojson import Feature, Polygon

from ohsome_quality_api.definitions import get_attribution, get_metadata
from ohsome_quality_api.indicators.models import IndicatorMetadata, Result
from ohsome_quality_api.indicators.models import (
IndicatorMetadata,
IndicatorTemplates,
Result,
)
from ohsome_quality_api.topics.models import BaseTopic as Topic
from ohsome_quality_api.utils.helper import json_serialize
from ohsome_quality_api.utils.helper import (
camel_to_snake,
get_module_dir,
json_serialize,
)


class BaseIndicator(metaclass=ABCMeta):
Expand All @@ -21,10 +31,11 @@ def __init__(
self.metadata: IndicatorMetadata = get_metadata(
"indicators", type(self).__name__
)
self.templates: IndicatorTemplates = self.get_template()
self.topic: Topic = topic
self.feature: Feature = feature
self.result: Result = Result(
description=self.metadata.label_description["undefined"],
description=self.templates.label_description["undefined"],
)
self._get_default_figure()

Expand All @@ -34,10 +45,7 @@ def as_dict(self, include_data: bool = False, exclude_label: bool = False) -> di
else:
result = self.result.model_dump(by_alias=True)
raw_dict = {
"metadata": self.metadata.model_dump(
by_alias=True,
exclude={"result_description", "label_description"},
),
"metadata": self.metadata.model_dump(by_alias=True),
"topic": self.topic.model_dump(
by_alias=True,
exclude={"ratio_filter"},
Expand Down Expand Up @@ -86,6 +94,7 @@ def data(self) -> dict:
data = vars(self).copy()
data.pop("result")
data.pop("metadata")
data.pop("templates")
data.pop("topic")
data.pop("feature")
return json.loads(json.dumps(data, default=json_serialize).encode())
Expand Down Expand Up @@ -167,3 +176,13 @@ def _get_default_figure(self) -> None:
raw = fig.to_dict()
raw["layout"].pop("template") # remove boilerplate
self.result.figure = raw

def get_template(self) -> IndicatorTemplates:
"""Get template for indicator."""
indicator_key = camel_to_snake(type(self).__name__)
dir = get_module_dir(f"ohsome_quality_api.indicators.{indicator_key}")
file = os.path.join(dir, "templates.yaml")
with open(file, "r") as file:
raw = yaml.safe_load(file)
templates = IndicatorTemplates(**raw)
return templates
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def calculate(self) -> None:
edge_case = self.check_minor_edge_cases(key)
# ZeroDivisionError can not occur because of `check_major_edge_cases()`
self.ratio[key] = self.area_osm[key] / self.area_ref[key]
template = Template(self.metadata.result_description)
template = Template(self.templates.result_description)
description = template.substitute(
ratio=round(self.ratio[key] * 100, 2),
coverage=round(self.area_cov[key] * 100, 2),
Expand All @@ -130,13 +130,13 @@ def calculate(self) -> None:
self.result.class_ = 3
elif self.th_low > self.result.value >= 0:
self.result.class_ = 1
label_description = self.metadata.label_description[self.result.label]
label_description = self.templates.label_description[self.result.label]
self.result.description = " ".join((label_description, result_description))
elif major_edge_case:
label_description = self.metadata.label_description[self.result.label]
label_description = self.templates.label_description[self.result.label]
self.result.description = " ".join((label_description, result_description))
else:
label_description = self.metadata.label_description[self.result.label]
label_description = self.templates.label_description[self.result.label]
edge_case = (
"OSM has substantivly more buildings than the reference datasets. The "
"reference dataset is likely to miss many buildings."
Expand Down
11 changes: 0 additions & 11 deletions ohsome_quality_api/indicators/building_comparison/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,3 @@ building-comparison:
quality_dimension: completeness
description: >-
Comparison of OSM buildings with the buildings of reference datasets.
label_description:
red: >-
The completeness of OSM buildings in your area-of-interest is low.
yellow: >-
The completeness of OSM buildings in your area-of-interest is medium.
green: >-
The completeness of OSM buildings in your area-of-interest is high.
undefined: >-
Comparison could not be made.
result_description: >-
The completeness in comparison to $dataset is $ratio%.
12 changes: 12 additions & 0 deletions ohsome_quality_api/indicators/building_comparison/templates.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
label_description:
red: >-
The completeness of OSM buildings in your area-of-interest is low.
yellow: >-
The completeness of OSM buildings in your area-of-interest is medium.
green: >-
The completeness of OSM buildings in your area-of-interest is high.
undefined: >-
Comparison could not be made.
result_description: >-
The completeness in comparison to $dataset is $ratio%.
4 changes: 2 additions & 2 deletions ohsome_quality_api/indicators/currentness/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ def calculate(self):
else:
self.result.class_ = 1

label_description = self.metadata.label_description[self.result.label]
label_description = self.templates.label_description[self.result.label]
self.result.description += Template(
self.metadata.result_description
self.templates.result_description
).substitute(
up_to_date_contrib_rel=f"{sum(self.bin_up_to_date.contrib_rel) * 100:.0f}",
num_of_elements=int(self.contrib_sum),
Expand Down
11 changes: 0 additions & 11 deletions ohsome_quality_api/indicators/currentness/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,3 @@ currentness:
quality_dimension: currentness
description: >-
Estimate currentness of features by classifying contributions based on topic specific temporal thresholds into three groups: up-to-date, in-between and out-of-date.
label_description:
red: >-
Many features are out-of-date.
yellow: >-
Some features are up-to-date and some features are out-of-date.
green: >-
Most features are up-to-date.
undefined: >-
The quality level could not be calculated for this indicator.
result_description: >-
In the area of interest $up_to_date_contrib_rel% of the $num_of_elements features were edited (created or modified) for the last time in the period between $from_timestamp and $to_timestamp.
12 changes: 12 additions & 0 deletions ohsome_quality_api/indicators/currentness/templates.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
label_description:
red: >-
Many features are out-of-date.
yellow: >-
Some features are up-to-date and some features are out-of-date.
green: >-
Most features are up-to-date.
undefined: >-
The quality level could not be calculated for this indicator.
result_description: >-
In the area of interest $up_to_date_contrib_rel% of the $num_of_elements features were edited (created or modified) for the last time in the period between $from_timestamp and $to_timestamp.
8 changes: 4 additions & 4 deletions ohsome_quality_api/indicators/density/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,24 @@ def calculate(self) -> None:
# TODO: we need to think about how we handle this
# if there are different topics
self.result.value = self.count / self.area_sqkm # density
description = Template(self.metadata.result_description).substitute(
description = Template(self.templates.result_description).substitute(
result=f"{self.result.value:.2f}"
)
if self.result.value >= self.threshold_yellow:
self.result.class_ = 5
self.result.description = (
description + self.metadata.label_description["green"]
description + self.templates.label_description["green"]
)
else:
if self.result.value > self.threshold_red:
self.result.class_ = 3
self.result.description = (
description + self.metadata.label_description["yellow"]
description + self.templates.label_description["yellow"]
)
else:
self.result.class_ = 1
self.result.description = (
description + self.metadata.label_description["red"]
description + self.templates.label_description["red"]
)

def create_figure(self) -> None:
Expand Down
17 changes: 0 additions & 17 deletions ohsome_quality_api/indicators/density/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,3 @@ density:
The density of features.
It is calculated by the number of features
divided by the area in square-kilometers.
label_description:
red: >-
It is probably hard to orientate on OSM-based sketchmaps of this region.
There are just few orientation providing features available,
you should explore, if participants can orientate properly.
yellow: >-
It might be difficult to orientate on OSM-based sketchmaps of
this region. There are not many orientation providing features available,
you should explore, if participants can orientate properly.
green: >-
It is probably easy to orientate on OSM-based sketchmaps of this region.
undefined: >-
The quality level could not be calculated for this indicator.
result_description: >-
The density of landmarks
(points of reference, e.g. waterbodies, supermarkets, churches, bus stops)
is $result features per sqkm.
18 changes: 18 additions & 0 deletions ohsome_quality_api/indicators/density/templates.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
label_description:
red: >-
It is probably hard to orientate on OSM-based sketchmaps of this region.
There are just few orientation providing features available,
you should explore, if participants can orientate properly.
yellow: >-
It might be difficult to orientate on OSM-based sketchmaps of
this region. There are not many orientation providing features available,
you should explore, if participants can orientate properly.
green: >-
It is probably easy to orientate on OSM-based sketchmaps of this region.
undefined: >-
The quality level could not be calculated for this indicator.
result_description: >-
The density of landmarks
(points of reference, e.g. waterbodies, supermarkets, churches, bus stops)
is $result features per sqkm.
4 changes: 2 additions & 2 deletions ohsome_quality_api/indicators/mapping_saturation/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ def calculate(self) -> None: # noqa: C901
self.result.value
)
)
description = Template(self.metadata.result_description).substitute(
description = Template(self.templates.result_description).substitute(
saturation=round(self.result.value * 100, 2)
)
self.result.description = (
description + self.metadata.label_description[self.result.label]
description + self.templates.label_description[self.result.label]
)

def create_figure(self) -> None:
Expand Down
11 changes: 0 additions & 11 deletions ohsome_quality_api/indicators/mapping_saturation/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,3 @@ mapping-saturation:
description: >-
Calculate if mapping has saturated.
High saturation has been reached if the growth of the fitted curve is minimal.
label_description:
red: >-
No saturation identified (Saturation ≤ 30%).
yellow: >-
Saturation is in progress (30% < Saturation ≤ 97%).
green: >-
High saturation has been reached (97% < Saturation ≤ 100%).
undefined: >-
Saturation could not be calculated.
result_description: >-
The saturation of the last 3 years is $saturation%.
12 changes: 12 additions & 0 deletions ohsome_quality_api/indicators/mapping_saturation/templates.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
label_description:
red: >-
No saturation identified (Saturation ≤ 30%).
yellow: >-
Saturation is in progress (30% < Saturation ≤ 97%).
green: >-
High saturation has been reached (97% < Saturation ≤ 100%).
undefined: >-
Saturation could not be calculated.
result_description: >-
The saturation of the last 3 years is $saturation%.
6 changes: 4 additions & 2 deletions ohsome_quality_api/indicators/minimal/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ async def preprocess(self) -> None:
)

def calculate(self) -> None:
description = Template(self.metadata.result_description).substitute()
description = Template(self.templates.result_description).substitute()
self.result.value = 1.0
self.result.description = description + self.metadata.label_description["green"]
self.result.description = (
description + self.templates.label_description["green"]
)

def create_figure(self) -> None:
# Do nothing ...
Expand Down
11 changes: 0 additions & 11 deletions ohsome_quality_api/indicators/minimal/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,3 @@ minimal:
quality_dimension: minimal
description: >-
An minimal Indicator for testing purposes.
label_description:
red: >-
Bad data quality.
yellow: >-
Medium data quality.
green: >-
Good data quality.
undefined: >-
The quality level could not be calculated for this indicator.
result_description: >-
Some description of the result.
12 changes: 12 additions & 0 deletions ohsome_quality_api/indicators/minimal/templates.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
label_description:
red: >-
Bad data quality.
yellow: >-
Medium data quality.
green: >-
Good data quality.
undefined: >-
The quality level could not be calculated for this indicator.
result_description: >-
Some description of the result.
Loading

0 comments on commit 11d6efb

Please sign in to comment.