Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google Floods Part 3: Adding Inundation Maps; COUNTRY=mozambique #1332

Open
wants to merge 108 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
c5c1215
Adding Google Floods Backend
Aug 8, 2024
b9722d3
Viewing the gauges on the frontend, adding error handling to backend
gislawill Aug 12, 2024
bc92d38
Update comment
gislawill Aug 12, 2024
08a4499
Updates to support tooltip content
gislawill Aug 12, 2024
0653f1e
copy update
gislawill Aug 12, 2024
7cd866d
Refactor title state management
gislawill Aug 12, 2024
94e1588
Fixing and adding server tests
gislawill Aug 12, 2024
6ac3aad
update set_env
ericboucher Aug 13, 2024
eb29b6e
Merge branch 'master' into feature/google-floods/1317
ericboucher Aug 13, 2024
6be26d7
update snapshots
ericboucher Aug 13, 2024
2fe481a
Merge branch 'master' into feature/google-floods/1317
gislawill Aug 15, 2024
7efaf78
Adding graph for google flood data
gislawill Aug 19, 2024
8e84cbb
Adding support for multi country deployments and adding google flood …
gislawill Aug 21, 2024
efdf2c9
Use interpolation for popup titles
gislawill Aug 21, 2024
8fa327c
Adding pytest-recording
gislawill Aug 21, 2024
f5ffbf6
Merge branch 'master' into feature/google-floods/1317
ericboucher Aug 21, 2024
438ee04
Update test_google_floods_api.py
ericboucher Aug 21, 2024
e369704
Merge branch 'feature/google-floods/1317' of https://github.com/WFP-V…
ericboucher Aug 21, 2024
6547f3b
Fixing river template
gislawill Aug 22, 2024
ac58dca
More selective with pytest recording
gislawill Aug 22, 2024
2e4bf07
Adding Google Floods Inundation API
Aug 22, 2024
65af7e7
Merge branch 'master' into feature/google-floods/1317
ericboucher Aug 22, 2024
0adb49d
Update API URL
ericboucher Aug 22, 2024
d358674
Updating inundations to call with same signature as gauges
Aug 23, 2024
6415863
Merge branch 'feature/google-floods/1317' into feature/google-floods-…
gislawill Aug 30, 2024
b206a6e
Merge conflict resolution and units
gislawill Aug 30, 2024
1842c4f
Updating tests
gislawill Aug 31, 2024
3a0b0fd
formatting
gislawill Aug 31, 2024
b0d1828
Using superscript
gislawill Sep 2, 2024
105b7d2
Adding Google Floods Inundation API
Aug 22, 2024
bb77ff7
Updating inundations to call with same signature as gauges
Aug 23, 2024
d397a70
Update tests, fix merge conflicts
gislawill Sep 3, 2024
c8b9be0
Speed up server, update tests
gislawill Sep 3, 2024
98cdc63
Supporting rbd
gislawill Sep 11, 2024
62d47df
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
ericboucher Sep 25, 2024
dc94f60
Merge branch 'master' into feature/google-floods/1317
ericboucher Sep 25, 2024
850cc51
Displaying inundation maps as geojson polygon layer
gislawill Oct 7, 2024
b0780fa
Merge branch 'master' into feature/google-floods/1317
gislawill Oct 7, 2024
9832dc4
Merge branch 'feature/google-floods/1317' into feature/google-floods-…
gislawill Oct 7, 2024
b3c066a
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
gislawill Oct 7, 2024
412b0b5
fixing test and ordering
gislawill Oct 7, 2024
97d15be
Merge branch 'feature/google-floods-inundation' of https://github.com…
gislawill Oct 7, 2024
7f401bb
Adding support for Cambodia
gislawill Oct 7, 2024
72d46b7
Merge branch 'feature/google-floods/1317' into feature/google-floods-…
gislawill Oct 7, 2024
e9626dd
Add support for Cambodia
gislawill Oct 7, 2024
d06b5ee
Updating url for data
gislawill Oct 7, 2024
2ce0025
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
gislawill Oct 7, 2024
35bf7a0
Clean up
gislawill Oct 7, 2024
9760ee6
Fix loading on reload
gislawill Oct 7, 2024
ff92aff
Empty commit
gislawill Oct 7, 2024
03d6ac2
http => https
gislawill Oct 7, 2024
ab5d1cc
Reduce cassettes
gislawill Oct 7, 2024
8735022
Merge branch 'feature/google-floods-inundation' of https://github.com…
wadhwamatic Oct 7, 2024
e39db90
fixing cassettes
gislawill Oct 7, 2024
dcd37e8
Merge branch 'feature/google-floods-inundation' of https://github.com…
wadhwamatic Oct 7, 2024
341a3a7
Merge branch 'master' into feature/google-floods/1317
gislawill Oct 7, 2024
ba7ee64
Merge branch 'feature/google-floods/1317' into feature/google-floods-…
gislawill Oct 7, 2024
cdf65d2
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
gislawill Oct 7, 2024
1e51479
Merge branch 'master' into feature/google-floods/1317
gislawill Oct 8, 2024
2a890cf
Adding date support
gislawill Oct 9, 2024
481c409
Merge branch 'feature/google-floods/1317' into feature/google-floods-…
gislawill Oct 9, 2024
f37026d
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
gislawill Oct 9, 2024
33c2692
api lint
gislawill Oct 9, 2024
b51d886
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
gislawill Oct 9, 2024
e950cc0
Fixing date logic on graph look up
gislawill Oct 9, 2024
2ca409d
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
gislawill Oct 9, 2024
766d9ed
Updates to chart to denote past vs future
gislawill Oct 10, 2024
efb4735
Adding translation to 'future' label
gislawill Oct 10, 2024
ff01928
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
gislawill Oct 10, 2024
bfbdb9e
Fix chart tests using mock
gislawill Oct 10, 2024
d825924
Merge branch 'feature/google-floods-forecast-chart/1317' into feature…
gislawill Oct 10, 2024
68e711b
Updating readme
gislawill Oct 10, 2024
0338024
Adding note about ip whitelist
gislawill Oct 10, 2024
a7a29b2
Merge branch 'feature/google-floods-inundation' of https://github.com…
wadhwamatic Oct 10, 2024
6d33543
Google Floods Part 2: Adding tooltip graph; COUNTRY=cambodia (#1330)
gislawill Oct 31, 2024
9204f02
Merge branch 'feature/google-floods/1317' into feature/google-floods-…
gislawill Oct 31, 2024
0963b34
Make the & conditional
ericboucher Oct 31, 2024
c7f3802
Adding inundation layer to moz
gislawill Oct 31, 2024
c8d2adc
updating server response to simpler json
gislawill Nov 7, 2024
334bda5
Layer updates, extending timeouts
gislawill Nov 7, 2024
597d6ff
Update tests and layers
gislawill Nov 7, 2024
c4155ef
Merge branch 'master' into feature/google-floods/1317
gislawill Nov 8, 2024
8c1879c
Merge branch 'feature/google-floods-inundation' of https://github.com…
wadhwamatic Nov 9, 2024
66cba35
Fixing tooltip collapsing
gislawill Nov 12, 2024
05188b3
Updating api with requested changes: test comments, abstracting utili…
gislawill Nov 12, 2024
a45de24
Adding caching
gislawill Nov 12, 2024
93f6b41
Merge branch 'feature/google-floods-inundation' of https://github.com…
wadhwamatic Nov 13, 2024
daaf86c
Update border width
gislawill Nov 15, 2024
439a542
Updates to chart line thickness and time selection
gislawill Nov 15, 2024
5a4c7b8
Merge branch 'feature/google-floods/1317' into feature/google-floods-…
gislawill Nov 22, 2024
5385bc7
lint
gislawill Nov 22, 2024
ee27d9d
requested changes
gislawill Nov 22, 2024
b1b6ec9
Update to cache_geojson
gislawill Nov 22, 2024
9f04de9
fix caching logic
gislawill Nov 22, 2024
3222328
Update cache handling
gislawill Nov 23, 2024
6d8fb11
Merge branch 'master' into feature/google-floods-inundation
gislawill Jan 28, 2025
4736583
Updating cambodia layer config
gislawill Jan 28, 2025
a3caa4b
Update snapshots
gislawill Jan 28, 2025
d4bf748
Update moz layer configuration
gislawill Jan 30, 2025
56306a0
Update snapshot
gislawill Jan 30, 2025
28deed4
Remove cors
gislawill Jan 30, 2025
efaa530
test 2
gislawill Jan 30, 2025
0c3f9c7
Don't wrap in json response
gislawill Jan 30, 2025
acbdb80
Remove double ampersand
gislawill Jan 30, 2025
6c5a00a
adding a cache test
gislawill Jan 30, 2025
9ee7907
cache test
gislawill Jan 30, 2025
7ab21e1
clean up debugging
gislawill Jan 30, 2025
449e44a
Merge branch 'master' into feature/google-floods-inundation
wadhwamatic Feb 4, 2025
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
17 changes: 12 additions & 5 deletions api/app/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def get_kobo_form_cached(form_id: str) -> Optional[dict[str, Any]]:
@timed
def cache_file(url: str, prefix: str, extension: str = "cache") -> FilePath:
"""Locally cache files fetched from a url."""
cache_filepath = _get_cached_filepath(
cache_filepath = get_cached_filepath(
prefix=prefix,
data=url,
extension=extension,
Expand Down Expand Up @@ -98,13 +98,13 @@ def cache_file(url: str, prefix: str, extension: str = "cache") -> FilePath:


@timed
def cache_geojson(geojson: GeoJSON, prefix: str) -> FilePath:
def cache_geojson(geojson: GeoJSON, prefix: str, cache_key: str) -> FilePath:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explicit the inputs here, esp. the cache_key and how it should be used.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some add comments and updated the params to be more explicit

"""Locally store geojson needed for a request."""
json_string = json.dumps(geojson)

cache_filepath = _get_cached_filepath(
cache_filepath = get_cached_filepath(
prefix=prefix,
data=json_string,
data=cache_key if cache_key else json_string,
extension="json",
)

Expand All @@ -121,7 +121,7 @@ def get_json_file(cached_filepath: FilePath) -> GeoJSON:
return json.load(f)


def _get_cached_filepath(prefix: str, data: str, extension: str = "cache") -> FilePath:
def get_cached_filepath(prefix: str, data: str, extension: str = "cache") -> FilePath:
"""Return the filepath where a cached response would live for the given inputs."""
filename = "{prefix}_{hash_string}.{extension}".format(
prefix=prefix,
Expand Down Expand Up @@ -151,3 +151,10 @@ def is_file_valid(filepath) -> bool:
return True

return False


def get_cache_age(filepath: FilePath) -> float:
"""Get the age of a cached file in seconds."""
return (
datetime.now() - datetime.fromtimestamp(os.path.getctime(filepath))
).total_seconds()
65 changes: 55 additions & 10 deletions api/app/googleflood.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
from fiona.drvsupport import supported_drivers
from pydantic import BaseModel

from .caching import (
cache_geojson,
get_cache_age,
get_cached_filepath,
get_json_file,
is_file_valid,
)

supported_drivers["LIBKML"] = "rw"

logger = logging.getLogger(__name__)
Expand All @@ -30,13 +38,31 @@ class InundationMapSet(BaseModel):
inundationMaps: List[InundationMap]


# This cache helps avoid making repeated requests to the Google Floods API
CACHE_TIMEOUT = 600 # 10 minutes
GOOGLE_FLOODS_API_KEY = os.getenv("GOOGLE_FLOODS_API_KEY", "")
if GOOGLE_FLOODS_API_KEY == "":
logger.warning("Missing backend parameter: GOOGLE_FLOODS_API_KEY")


def make_google_floods_request(url, method="get", data=None, retries=1, timeout=10):
"""Make a request with retries and error handling."""
"""Make a request with retries and error handling, caching the result for 5 minutes."""
# Create a unique cache key based on the request parameters
request_data = json.dumps(data, sort_keys=True) if data else ""
cache_key = f"{url}_{method}_{request_data}"
cache_filepath = get_cached_filepath(
prefix="google_floods", data=cache_key, extension="json"
)

# Check if the cached file is valid and not expired
if is_file_valid(cache_filepath):
cached_data = get_json_file(cache_filepath)
cache_age = get_cache_age(cache_filepath)
if cache_age < CACHE_TIMEOUT:
logger.debug("Returning cached data.")
return cached_data
ericboucher marked this conversation as resolved.
Show resolved Hide resolved

# If not cached or expired, make the request
for _ in range(retries):
try:
if method == "post":
Expand All @@ -56,6 +82,8 @@ def make_google_floods_request(url, method="get", data=None, retries=1, timeout=
status_code=500, detail="Error fetching data from Google API"
)

cache_geojson(response_data, prefix="google_floods", cache_key=cache_key)

return response_data


Expand Down Expand Up @@ -267,6 +295,21 @@ def fetch_kml(inundationMap):
def get_google_floods_inundations(
region_codes: List[str], run_sequentially: bool = False
) -> gpd.GeoDataFrame:
# Generate a cache key based on the region codes and run mode
cache_key = f"{'_'.join(region_codes)}_{run_sequentially}"
cache_filepath = get_cached_filepath(
prefix="inundations", data=cache_key, extension="json"
)

# Check if the cached GeoJSON is valid and not expired
if is_file_valid(cache_filepath):
cached_geojson = get_json_file(cache_filepath)
gislawill marked this conversation as resolved.
Show resolved Hide resolved
cache_age = get_cache_age(cache_filepath)
if cache_age < CACHE_TIMEOUT:
logger.debug("Returning cached GeoJSON data.")
return cached_geojson

# Fetch gauge data
gauge_data = get_google_floods_gauges(region_codes, as_geojson=False)

inundationMapSet = []
Expand All @@ -276,20 +319,19 @@ def get_google_floods_inundations(

level_to_kml = {}

def fetch_and_cache_kml(inundationMap):
level, kml = fetch_kml(inundationMap)
level_to_kml[level] = kml

# Fetch KML data
if run_sequentially:
for inundationMap in inundationMapSet:
level, kml = fetch_kml(inundationMap)
level_to_kml[level] = kml
fetch_and_cache_kml(inundationMap)
else:
with ThreadPoolExecutor() as executor:
future_to_inundation = {
executor.submit(fetch_kml, inundationMap): inundationMap
for inundationMap in inundationMapSet
}
for future in as_completed(future_to_inundation):
level, kml = future.result()
level_to_kml[level] = kml
executor.map(fetch_and_cache_kml, inundationMapSet)

# Process KML data into GeoDataFrames
tmp_path = os.path.join(f"/tmp/google-floods/{str(uuid.uuid4())}")
if not os.path.exists(tmp_path):
os.makedirs(tmp_path)
Expand All @@ -312,4 +354,7 @@ def get_google_floods_inundations(
# Return an empty GeoJSON FeatureCollection
geojson = {"type": "FeatureCollection", "features": []}

# Cache the final GeoJSON
cache_geojson(geojson, prefix="inundations", cache_key=cache_key)

return geojson