diff --git a/app/initialize.py b/app/initialize.py index 8b06dd7..404ee6f 100644 --- a/app/initialize.py +++ b/app/initialize.py @@ -4,12 +4,13 @@ import os import sys import argparse +from typing import Tuple from datetime import datetime, timedelta import logging import xarray as xr import requests -from grib import ISOBARIC_LABEL, TEMPERATURE_LABEL +from grib import ISOBARIC_LABEL, TEMPERATURE_LABEL, get_temporal_extent dataset = xr.Dataset() logger = logging.getLogger() @@ -151,7 +152,7 @@ def download_gribfile(data_path: str, api_url: str) -> str: logger.info("Latest file is %s, already have that. Skipping download.", fname) return fname - logger.info("Downloading %s to path %s.", api_url, fname) + logger.warning("Downloading %s to path %s.", api_url, fname) with open(fname, "wb") as fd: for chunk in response.iter_content(chunk_size=524288): fd.write(chunk) @@ -163,6 +164,22 @@ def format_instance_id(timestamp: datetime) -> str: return timestamp.strftime("%Y%m%d%H%M%S") +def check_instance_exists(ds: xr.Dataset, instance_id: str) -> Tuple[bool, str]: + """Check instance id exists in dataset.""" + instance_dates = [get_temporal_extent(ds)] + for d in instance_dates: + if format_instance_id(d) == instance_id: + logger.info("instance_id %s is valid", instance_id) + return True, "" + + logger.error("instance_id %s does not exist in dataset", instance_id) + valid_dates = [format_instance_id(x) for x in instance_dates] + return ( + False, + f"instance_id {instance_id} does not exist in dataset. Valid dates are {valid_dates}.", + ) + + args = parse_args() DATAFILE = args.file BASE_URL = args.base_url diff --git a/app/routes/collections_page.py b/app/routes/collections_page.py index 432308e..70037a5 100644 --- a/app/routes/collections_page.py +++ b/app/routes/collections_page.py @@ -2,11 +2,11 @@ from functools import lru_cache from datetime import timedelta import logging -from fastapi import APIRouter +from fastapi import APIRouter, status, Response import edr_pydantic from edr_pydantic.collections import Collection -from initialize import get_dataset, BASE_URL +from initialize import get_dataset, BASE_URL, check_instance_exists from grib import ( get_vertical_extent, @@ -34,6 +34,11 @@ def create_collection(collection_id: str = "", instance_id: str = "") -> dict: vertical_levels = get_vertical_extent(dataset) collection_url = f"{BASE_URL}collections/isobaric/" if len(instance_id) > 0: + # Sanity check on instance id + instance_ok, errmsg = check_instance_exists(dataset, instance_id) + if not instance_ok: + return Response(status_code=status.HTTP_400_BAD_REQUEST, content=errmsg) + collection_url = instance_url isobaric_col = Collection( diff --git a/app/routes/position_page.py b/app/routes/position_page.py index 234730e..eb5a21d 100644 --- a/app/routes/position_page.py +++ b/app/routes/position_page.py @@ -9,7 +9,7 @@ from covjson_pydantic.coverage import Coverage from covjson_pydantic.ndarray import NdArray -from initialize import get_dataset, format_instance_id +from initialize import get_dataset, check_instance_exists from grib import ( get_vertical_extent, @@ -49,9 +49,10 @@ def create_point(coords: str = "", instance_id: str = "") -> dict: return Response(status_code=status.HTTP_400_BAD_REQUEST, content=errmsg) # Sanity check on instance id - instance_ok, errmsg = check_instance_exists(dataset, instance_id) - if not instance_ok: - return Response(status_code=status.HTTP_400_BAD_REQUEST, content=errmsg) + if len(instance_id) > 0: + instance_ok, errmsg = check_instance_exists(dataset, instance_id) + if not instance_ok: + return Response(status_code=status.HTTP_400_BAD_REQUEST, content=errmsg) # Fetch temperature temperatures = dataset[TEMPERATURE_LABEL].sel( @@ -215,22 +216,6 @@ def check_coords_within_bounds(ds: xr.Dataset, point: Point) -> Tuple[bool, str] return True, "" -def check_instance_exists(ds: xr.Dataset, instance_id: str) -> Tuple[bool, str]: - """Check instance id exists in dataset.""" - instance_dates = [get_temporal_extent(ds)] - for d in instance_dates: - if format_instance_id(d) == instance_id: - logger.info("instance_id %s is valid", instance_id) - return True, "" - - logger.error("instance_id %s does not exist in dataset", instance_id) - valid_dates = [format_instance_id(x) for x in instance_dates] - return ( - False, - f"instance_id {instance_id} does not exist in dataset. Valid dates are {valid_dates}.", - ) - - @router.get("/collections/isobaric/position") async def create_isobaric_page(coords: str) -> dict: """Position. diff --git a/app/test_app.py b/app/test_app.py index f6041d3..a23b173 100644 --- a/app/test_app.py +++ b/app/test_app.py @@ -26,40 +26,58 @@ def test_conformance(self): def test_collections(self): response = client.get("/collections") self.assertEqual(response.status_code, 200) - self.assertIn( - '{"links":[{"href":"http://localhost:5000/","hreflang":"en","rel":"self","type":"aplication/json"}],"collections":[{"id":"isobaric","title":"', - response.text, - ) + self.assertTrue(response.json()["collections"][0]["id"] == "isobaric") + # '{"links":[{"href":"http://localhost:5000/collections/","hreflang":"en","rel":"self","type":"aplication/json"}],"collections":[{"id":"isobaric","title":"IsobaricGRIB - GRIB files"... def test_point(self): - response = client.get(f"/collections/position?{sample_coords}") + response = client.get(f"/collections/isobaric/position?{sample_coords}") self.assertEqual(response.status_code, 200) - self.assertIn( - '"vertical":{"interval":[["850.0"],["100.0"]],"values":["850.0","750.0","700.0","600.0","500.0","450.0","400.0","350.0","300.0","275.0","250.0","225.0","200.0","150.0","100.0"],"vrs":"Vertical Reference System: PressureLevel"}},', - response.text, + # Test for values in range -> temperature + self.assertTrue( + len(str(response.json()["ranges"]["temperature"]["values"][0])) > 1 + ) + # Test for values in range -> uwind + self.assertTrue(len(str(response.json()["ranges"]["uwind"]["values"][0])) > 1) + # Test for values in range -> vwind + self.assertTrue(len(str(response.json()["ranges"]["vwind"]["values"][0])) > 1) + # Test for values in domain -> z -> values + self.assertTrue( + len(str(response.json()["domain"]["axes"]["z"]["values"][0])) > 1 ) def test_instances(self): + """Test a variety of URLs related to instances.""" + # Test list of instances. response = client.get("/collections/isobaric/instances") self.assertEqual(response.status_code, 200) + # Find current instance. json_response = response.json() instance_id = json_response["instances"][0]["id"] - self.assertIn( - f"http://localhost:5000/collections/isobaric/instances/{instance_id}/position", - response.text, + # Test link to current instance. + self.assertTrue( + json_response["instances"][0]["links"][0]["href"] + == f"http://localhost:5000/collections/isobaric/instances/{instance_id}/" ) - # Test instance - response = client.get("/collections/isobaric/instances/{instance_id}") + # Test asking for a specific instance. + response = client.get(f"/collections/isobaric/instances/{instance_id}") self.assertEqual(response.status_code, 200) - self.assertIn( - '"id":"isobaric","title":"', - response.text, + self.assertTrue(response.json()["id"] == "isobaric") + self.assertIn(instance_id, response.json()["links"][0]["href"]) + + # Test asking for non-existing instance. + response = client.get("/collections/isobaric/instances/1234567890/") + self.assertEqual(response.status_code, 400) + + # Test asking for data in a non-existing instance. + response = client.get( + f"/collections/isobaric/instances/1234567890/position?{sample_coords}" ) + self.assertEqual(response.status_code, 400) - # Test a point in instance + # Test asking for a sample point in current instance. response = client.get( f"/collections/isobaric/instances/{instance_id}/position?{sample_coords}" )