diff --git a/.gitignore b/.gitignore index c6ac0ca..7fa4cbf 100644 --- a/.gitignore +++ b/.gitignore @@ -89,6 +89,7 @@ ipython_config.py # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. +Pipfile #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow diff --git a/hvpy/__init__.py b/hvpy/__init__.py index 327412a..58f3ace 100644 --- a/hvpy/__init__.py +++ b/hvpy/__init__.py @@ -1,2 +1 @@ -from .api import * # NOQA from .version import __version__ diff --git a/hvpy/api.py b/hvpy/api.py deleted file mode 100644 index 8388e3d..0000000 --- a/hvpy/api.py +++ /dev/null @@ -1,13 +0,0 @@ -__all__ = ["fake_api_call"] - - -def fake_api_call(): - """ - _summary_ - - Returns - ------- - _type_ - _description_ - """ - return {} diff --git a/hvpy/api_groups/__init__.py b/hvpy/api_groups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hvpy/api_groups/jpeg2000/__init__.py b/hvpy/api_groups/jpeg2000/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hvpy/api_groups/jpeg2000/get_jp2_image.py b/hvpy/api_groups/jpeg2000/get_jp2_image.py new file mode 100644 index 0000000..23fe191 --- /dev/null +++ b/hvpy/api_groups/jpeg2000/get_jp2_image.py @@ -0,0 +1,47 @@ +from datetime import datetime + +from pydantic import Field, validator + +from hvpy.io import HvpyParameters, OutputType + + +class getJP2ImageInputParameters(HvpyParameters): + """ + Handles the input parameters of the `getJP2Image API. + + `__. + + Attributes + ---------- + date : datetime + Desired date/time of the JP2 image. + sourceId : int + Unique image datasource identifier. + jpip : bool, optional + Returns a JPIP URI instead of the binary data of the image if set to `True`, defaults to `False`. + json : bool, optional + Returns the JSON if set to `True`, defaults to `False`. + """ + + date: datetime + sourceId: int + jpip: bool = False + Json: bool = Field(False, alias="json") + + @validator("date") + def convert_date_to_isoformat(cls, v): + """ + Converts the date from a datetime object to a string in the ISO format. + """ + return v.isoformat() + "Z" + + def get_output_type(self): + """ + Returns the output type of the API call. + """ + if self.Json and self.jpip: + return OutputType.Json + elif not self.Json and self.jpip: + return OutputType.String + else: + return OutputType.Raw diff --git a/hvpy/api_groups/jpeg2000/tests/__init__.py b/hvpy/api_groups/jpeg2000/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hvpy/api_groups/jpeg2000/tests/test_get_jp2_image.py b/hvpy/api_groups/jpeg2000/tests/test_get_jp2_image.py new file mode 100644 index 0000000..602c63e --- /dev/null +++ b/hvpy/api_groups/jpeg2000/tests/test_get_jp2_image.py @@ -0,0 +1,73 @@ +from datetime import datetime + +import pytest +from pydantic import ValidationError + +from hvpy.api_groups.jpeg2000.get_jp2_image import getJP2ImageInputParameters +from hvpy.core import execute_api_call + + +def test_str_response(): + date_obj = datetime(2022, 1, 1, 23, 59, 59) + params = {"date": date_obj, "sourceId": 14, "jpip": True, "json": False} + params = getJP2ImageInputParameters(**params) + response = execute_api_call(input_parameters=params) + assert isinstance(response, str) + assert response.startswith("jpip://") + + +def test_json_response(): + date_obj = datetime(2022, 1, 1, 23, 59, 59) + params = {"date": date_obj, "sourceId": 14, "jpip": True, "json": True} + params = getJP2ImageInputParameters(**params) + response = execute_api_call(input_parameters=params) + assert isinstance(response, dict) + assert "uri" in response + assert response["uri"].startswith("jpip://") + + +def test_raw_response(): + date_obj = datetime(2022, 1, 1, 23, 59, 59) + params = {"date": date_obj, "sourceId": 14, "jpip": False, "json": False} + params = getJP2ImageInputParameters(**params) + response = execute_api_call(input_parameters=params) + assert isinstance(response, bytes) + + +def test_raw_response_with_json(): + date_obj = datetime(2022, 1, 1, 23, 59, 59) + params = {"date": date_obj, "sourceId": 14, "jpip": False, "json": True} + params = getJP2ImageInputParameters(**params) + response = execute_api_call(input_parameters=params) + assert isinstance(response, bytes) + + +def test_default_response(): + date_obj = datetime(2022, 1, 1, 23, 59, 59) + params = {"date": date_obj, "sourceId": 14} + params = getJP2ImageInputParameters(**params) + response = execute_api_call(input_parameters=params) + assert isinstance(response, bytes) + + +def test_error_handling(): + params = {"sourceId": 14, "jpip": True, "json": True} + with pytest.raises(ValidationError, match="getJP2ImageInputParameters\ndate\n field required"): + getJP2ImageInputParameters(**params) + + +def test_unknown_parameters(): + date_obj = datetime(2022, 1, 1, 23, 59, 59) + params = {"date": date_obj, "sourceId": 14, "jpip": True, "json": True, "should_reject_this": True} + params = getJP2ImageInputParameters(**params) + response = execute_api_call(input_parameters=params) + assert isinstance(response, dict) + assert "uri" in response + assert response["uri"].startswith("jpip://") + + +def test_url_property(): + date_obj = datetime(2022, 1, 1, 23, 59, 59) + params = {"date": date_obj, "sourceId": 14, "jpip": True, "json": True} + params = getJP2ImageInputParameters(**params) + assert params.url == "https://api.helioviewer.org/v2/getJP2Image/" diff --git a/hvpy/core.py b/hvpy/core.py new file mode 100644 index 0000000..6e9a840 --- /dev/null +++ b/hvpy/core.py @@ -0,0 +1,47 @@ +import requests + +from hvpy.io import HvpyParameters, OutputType + + +def parse_response(response: requests.Response, output_parameters: OutputType): + """ + Parses the response from the API call based on the output type. + + Parameters + ---------- + response : requests.Response + The response from the API call. + output_parameters : OutputType + The output type. + + Returns + ------- + binary | str | json + The parsed response. + """ + + if output_parameters == OutputType.Raw: + return response.content + elif output_parameters == OutputType.String: + return response.content.decode("utf-8") + elif output_parameters == OutputType.Json: + return response.json() + + +def execute_api_call(input_parameters: HvpyParameters): + """ + Executes the API call and returns a parsed response. + + Parameters + ---------- + input_parameters : HvpyParameters + The input parameters. + + Returns + ------- + Parsed response from the API. + """ + + response = requests.get(input_parameters.url, params=input_parameters.dict()) + response.raise_for_status() + return parse_response(response, input_parameters.get_output_type()) diff --git a/hvpy/io.py b/hvpy/io.py new file mode 100644 index 0000000..9362e4a --- /dev/null +++ b/hvpy/io.py @@ -0,0 +1,31 @@ +from enum import Enum, auto + +from pydantic import BaseModel + +BASE_URL = "https://api.helioviewer.org/v2/" + + +class HvpyParameters(BaseModel): + def dict(self): + """ + Pydantic doesn't allow using lowercase 'json' as a field, so we + override it. + """ + d = super().dict() + if "Json" in d: + d["json"] = d["Json"] + del d["Json"] + return d + + def get_output_type(self): + return OutputType.Raw + + @property + def url(self): + return BASE_URL + self.__class__.__name__[:-15] + "/" + + +class OutputType(Enum): + Raw = auto() + String = auto() + Json = auto() diff --git a/hvpy/tests/test_fake.py b/hvpy/tests/test_fake.py deleted file mode 100644 index c815117..0000000 --- a/hvpy/tests/test_fake.py +++ /dev/null @@ -1,9 +0,0 @@ -from hvpy import fake_api_call - - -def test_test_fake(): - assert True - - -def test_fake_api_call(): - assert fake_api_call() == {} diff --git a/hvpy/tests/test_io.py b/hvpy/tests/test_io.py new file mode 100644 index 0000000..2c295e6 --- /dev/null +++ b/hvpy/tests/test_io.py @@ -0,0 +1,11 @@ +from hvpy.io import HvpyParameters, OutputType + + +def test_default_get_output_type_is_raw(): + params = HvpyParameters() + assert params.get_output_type() == OutputType.Raw + + +def test_url_property(): + params = HvpyParameters() + assert params.url == "https://api.helioviewer.org/v2//" diff --git a/setup.cfg b/setup.cfg index dabbcc0..6fe75b7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,7 @@ setup_requires = setuptools_scm install_requires = requests>=2.27.0 + pydantic>=1.9.1 [options.extras_require] tests =