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

HDX-9848 currency endpoint #115

Merged
merged 2 commits into from
May 24, 2024
Merged
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
2 changes: 2 additions & 0 deletions hdx_hapi/config/doc_snippets.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@
DOC_SEE_DATASET = 'See the <a href="/docs#/HDX%20Metadata/get_datasets_api_v1_dataset_get" target="_blank">dataset endpoint</a> for details.'
DOC_SEE_LOC = 'See the <a href="/docs#/Locations%20and%20Administrative%20Divisions/get_locations_api_v1_location_get" target="_blank">location endpoint</a> for details.'
DOC_SEE_ORG_TYPE = 'See the <a href="/docs#/Humanitarian%20Organizations%20and%20Sectors/get_org_types_api_v1_org_type_get" target="_blank">org type endpoint</a> for details.'

DOC_CURRENCY_CODE = 'Filter the response by the currency code.'
33 changes: 33 additions & 0 deletions hdx_hapi/db/dao/currency_view_dao.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import logging
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select

from hdx_hapi.db.models.views.all_views import CurrencyView
from hdx_hapi.db.dao.util.util import apply_pagination, case_insensitive_filter
from hdx_hapi.endpoints.util.util import PaginationParams

logger = logging.getLogger(__name__)


async def currencies_view_list(
pagination_parameters: PaginationParams,
db: AsyncSession,
code: Optional[str] = None,
):
logger.info(f'currency_view_list called with params: code={code}')

query = select(CurrencyView)
if code:
query = case_insensitive_filter(query, CurrencyView.code, code)

query = apply_pagination(query, pagination_parameters)

logger.debug(f'Executing SQL query: {query}')

result = await db.execute(query)
currencies = result.scalars().all()

logger.info(f'Retrieved {len(currencies)} rows from the database')

return currencies
49 changes: 49 additions & 0 deletions hdx_hapi/endpoints/get_currency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from typing import Annotated
from fastapi import Depends, Query, APIRouter


from sqlalchemy.ext.asyncio import AsyncSession
from hdx_hapi.config.doc_snippets import DOC_CURRENCY_CODE

from hdx_hapi.endpoints.models.base import HapiGenericResponse
from hdx_hapi.endpoints.models.currency import CurrencyResponse
from hdx_hapi.endpoints.util.util import (
CommonEndpointParams,
OutputFormat,
common_endpoint_parameters,
)
from hdx_hapi.services.csv_transform_logic import transform_result_to_csv_stream_if_requested

from hdx_hapi.services.currency_logic import get_currencies_srv
from hdx_hapi.services.sql_alchemy_session import get_db

router = APIRouter(
tags=['Metadata'],
)


@router.get(
'/api/metadata/currency',
response_model=HapiGenericResponse[CurrencyResponse],
summary='Get information about how currencies are classified',
include_in_schema=False,
)
@router.get(
'/api/v1/metadata/currency',
response_model=HapiGenericResponse[CurrencyResponse],
summary='Get information about how currencies are classified',
)
async def get_currencies(
common_parameters: Annotated[CommonEndpointParams, Depends(common_endpoint_parameters)],
db: AsyncSession = Depends(get_db),
code: Annotated[
str, Query(max_length=32, description=f'{DOC_CURRENCY_CODE}', openapi_examples={'usd': {'value': 'usd'}})
] = None,
output_format: OutputFormat = OutputFormat.JSON,
):
result = await get_currencies_srv(
pagination_parameters=common_parameters,
db=db,
code=code,
)
return transform_result_to_csv_stream_if_requested(result, output_format, CurrencyResponse)
9 changes: 9 additions & 0 deletions hdx_hapi/endpoints/models/currency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pydantic import ConfigDict, Field
from hdx_hapi.endpoints.models.base import HapiBaseModel


class CurrencyResponse(HapiBaseModel):
code: str = Field(max_length=32)
name: str = Field(max_length=512)

model_config = ConfigDict(from_attributes=True)
17 changes: 17 additions & 0 deletions hdx_hapi/services/currency_logic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession

from hdx_hapi.db.dao.currency_view_dao import currencies_view_list
from hdx_hapi.endpoints.util.util import PaginationParams


async def get_currencies_srv(
pagination_parameters: PaginationParams,
db: AsyncSession,
code: Optional[str] = None,
):
return await currencies_view_list(
pagination_parameters=pagination_parameters,
db=db,
code=code,
)
2 changes: 2 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from hdx_hapi.endpoints.get_national_risk import router as national_risk_router # noqa
from hdx_hapi.endpoints.get_wfp_commodity import router as wfp_commodity_router # noqa
from hdx_hapi.endpoints.get_food_security import router as food_security_router # noqa
from hdx_hapi.endpoints.get_currency import router as currency_router # noqa


# from hdx_hapi.endpoints.delete_example import delete_dataset
Expand Down Expand Up @@ -59,6 +60,7 @@
app.include_router(dataset_router)
app.include_router(wfp_commodity_router)
app.include_router(food_security_router)
app.include_router(currency_router)


# add middleware
Expand Down
3 changes: 3 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from hapi_schema.db_conflict_event import view_params_conflict_event
from hapi_schema.db_poverty_rate import view_params_poverty_rate
from hapi_schema.db_wfp_commodity import view_params_wfp_commodity
from hapi_schema.db_currency import view_params_currency

from hdx_hapi.config.config import get_config
from hdx_hapi.db.models.base import Base
Expand All @@ -48,6 +49,7 @@
'tests/sample_data/poverty_rate.sql',
'tests/sample_data/food_security.sql',
'tests/sample_data/wfp_commodity.sql',
'tests/sample_data/currency.sql',
]

VIEW_LIST = [
Expand All @@ -69,6 +71,7 @@
view_params_conflict_event,
view_params_poverty_rate,
view_params_wfp_commodity,
view_params_currency,
]


Expand Down
6 changes: 6 additions & 0 deletions tests/sample_data/currency.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- dummy data
INSERT INTO currency (code, name)
VALUES
('USD', 'United states dollar'),
('RON', 'Romanian leu'),
('EUR', 'Euro');
6 changes: 6 additions & 0 deletions tests/test_endpoints/endpoint_data.py
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are you attempting to check for case invariance here or is "usD" a typo?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

initially I did it with sensitive, but i checked other endpoints and switch to insensitive. we can easy change if DS team give us feedback

Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,12 @@
},
'expected_fields': ['code', 'name'],
},
'/api/v1/metadata/currency': {
'query_parameters': {
'code': 'usD',
},
'expected_fields': ['code', 'name'],
},
'/api/v1/metadata/wfp_commodity': {
'query_parameters': {
'code': '001',
Expand Down
60 changes: 60 additions & 0 deletions tests/test_endpoints/test_currency_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import pytest
import logging

from httpx import AsyncClient
from main import app
from tests.test_endpoints.endpoint_data import endpoint_data

log = logging.getLogger(__name__)

ENDPOINT_ROUTER = '/api/v1/metadata/currency'
endpoint_data = endpoint_data[ENDPOINT_ROUTER]
query_parameters = endpoint_data['query_parameters']
expected_fields = endpoint_data['expected_fields']


@pytest.mark.asyncio
async def test_get_currencies(event_loop, refresh_db):
log.info('started test_get_currencies')
async with AsyncClient(app=app, base_url='http://test') as ac:
response = await ac.get(ENDPOINT_ROUTER)
assert response.status_code == 200
assert len(response.json()['data']) > 0, 'There should be at least one currency in the database'


@pytest.mark.asyncio
async def test_get_currency_params(event_loop, refresh_db):
log.info('started test_get_currency_params')

for param_name, param_value in query_parameters.items():
async with AsyncClient(app=app, base_url='http://test', params={param_name: param_value}) as ac:
response = await ac.get(ENDPOINT_ROUTER)

assert response.status_code == 200
assert len(response.json()['data']) > 0, (
f'There should be at least one currency entry for parameter "{param_name}" with value "{param_value}" '
'in the database'
)

async with AsyncClient(app=app, base_url='http://test', params=query_parameters) as ac:
response = await ac.get(ENDPOINT_ROUTER)

assert response.status_code == 200
assert (
len(response.json()['data']) > 0
), 'There should be at least one currency entry for all parameters in the database'


@pytest.mark.asyncio
async def test_get_currency_result(event_loop, refresh_db):
log.info('started test_get_currency_result')

async with AsyncClient(app=app, base_url='http://test', params=query_parameters) as ac:
response = await ac.get(ENDPOINT_ROUTER)

for field in expected_fields:
assert field in response.json()['data'][0], f'Field "{field}" not found in the response'

assert len(response.json()['data'][0]) == len(
expected_fields
), 'Response has a different number of fields than expected'
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
'/api/v1/coordination-context/funding',
'/api/v1/coordination-context/conflict-event',
'/api/v1/food/food-security',
'/api/v1/metadata/currency',
]


Expand Down
Loading