Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
21 changes: 17 additions & 4 deletions src/imf_reader/sdr/read_announcements.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def read_tsv(url: str) -> pd.DataFrame:
return pd.read_csv(url, delimiter="/t", engine="python")

except pd.errors.ParserError:
raise ValueError("SDR _data not available for this date")
raise ValueError("SDR data not available for this date")


def clean_df(df: pd.DataFrame) -> pd.DataFrame:
Expand Down Expand Up @@ -94,16 +94,29 @@ def get_latest_allocations_holdings_date() -> tuple[int, int]:


def fetch_allocations_holdings(date: tuple[int, int] | None = None) -> pd.DataFrame:
"""Fetch SDR holdings and allocations data for a given date
"""
Fetch SDR holdings and allocations data for a given date. If date is not specified or exceeds the latest available
date, it fetches data for the latest date

Args:
date: The year and month to get allocations and holdings data for. e.g. (2024, 11) for November 2024. If None, the latest announcements released are fetched
date: The year and month to get allocations and holdings data for. e.g. (2024, 11) for November 2024.
If None, the latest announcements released are fetched

returns:
A dataframe with the SDR allocations and holdings data
"""

latest_date = get_latest_allocations_holdings_date()
Copy link
Collaborator

Choose a reason for hiding this comment

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

A minor issue. This function logs a message saying it is retrieving the latest date. This doesn't make sense when a user requests a specific date.

Copy link
Collaborator Author

@mharoruiz mharoruiz Feb 4, 2025

Choose a reason for hiding this comment

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

I added a boolean option to suppress the logging info in get_latest_allocations_holdings_date(). Not sure if this is the best workaround though.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think this is the best approach, because it isn't very useful argument for the user to be exposed to. I suggest within the fetch_allocations_holdings function you temporarily disable logging. The functionality should be:

  • if the user does not set a date or requests the latest date, it should log the message "fetching latest date"
  • if the user specifies the date, it should not log that message, only the "fetching data" message. To do this disable the logger before the fetch_latest_date function is called, then reset the logger after it is called


if date is None:
date = get_latest_allocations_holdings_date()
date = latest_date
else:
date_obj = datetime(date[0], date[1], 1)
latest_date_obj = datetime(latest_date[0], latest_date[1], 1)
if date_obj > latest_date_obj:
logger.info(
f"SDR data unavailable for date: {format_date(date[1],date[0])}. Will fetch latest available"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure we should return data as this might lead to silent errors. It might be better to raise a value error noting data for the date is unavailable, and specify the latest available date

)
date = latest_date

return get_holdings_and_allocations_data(*date)
39 changes: 21 additions & 18 deletions tests/test_sdr/test_read_announcements.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from unittest.mock import patch, Mock
import pytest
import pandas as pd
import logging
from imf_reader import sdr
from imf_reader.sdr.read_announcements import (
read_tsv,
Expand Down Expand Up @@ -50,7 +51,7 @@ def test_read_tsv_success(self, mock_read_csv):
def test_read_tsv_failure(self, mock_read_csv):
"""Test read_tsv raises ValueError on malformed data."""
mock_read_csv.side_effect = pd.errors.ParserError
with pytest.raises(ValueError, match="SDR _data not available for this date"):
with pytest.raises(ValueError, match="SDR data not available for this date"):
read_tsv("mock_url")

def test_clean_df_correct_format(self, input_df):
Expand Down Expand Up @@ -128,7 +129,7 @@ def test_get_holdings_and_allocations_data_success(
def test_get_holdings_and_allocations_data_failure(self, mock_read_tsv):
"""Test get_holdings_and_allocations_data raises ValueError when read_tsv fails."""
with pytest.raises(ValueError, match="Data not available"):
get_holdings_and_allocations_data(2024, 2)
get_holdings_and_allocations_data(1800, 1)

@patch("imf_reader.sdr.read_announcements.make_request")
def test_get_latest_date_success(self, mock_make_request):
Expand Down Expand Up @@ -192,7 +193,7 @@ def test_fetch_allocations_holdings_default_date(
):
"""Test fetch_allocations_holdings when no date is provided."""
# Mock get_latest_allocations_holdings_date to return a specific date
mock_get_latest_date.return_value = (2, 2024)
mock_get_latest_date.return_value = (2024, 2)

# Mock read_tsv
mock_read_tsv.return_value = input_df
Expand All @@ -217,24 +218,26 @@ def test_fetch_allocations_holdings_default_date(
# Assertions
mock_get_latest_date.assert_called_once() # Ensure get_latest_allocations_holdings_date was called
mock_get_holdings_and_allocations_data.assert_called_once_with(
2, 2024
2024, 2
) # Ensure the correct call
pd.testing.assert_frame_equal(
result, cleaned_df
) # Ensure the result matches the cleaned data

@patch("imf_reader.sdr.read_announcements.get_holdings_and_allocations_data")
@patch("imf_reader.sdr.read_announcements.get_latest_allocations_holdings_date")
def test_fetch_allocations_holdings_failure(
self, mock_get_latest_date, mock_get_holdings_data
):
"""Test fetch_allocations_holdings raises ValueError when data fetching fails."""
# Mock get_latest_allocations_holdings_date to return a date
mock_get_latest_date.return_value = (2, 2024)

# Simulate a failure in get_holdings_and_allocations_data
mock_get_holdings_data.side_effect = ValueError("Data not available")

# Assertions
with pytest.raises(ValueError, match="Data not available"):
fetch_allocations_holdings()
@patch("imf_reader.sdr.read_announcements.get_holdings_and_allocations_data")
@patch(
"imf_reader.sdr.read_announcements.get_latest_allocations_holdings_date",
return_value=(2024, 1),
)
def test_fetch_allocations_holdings_future_date(
mock_get_latest_date, mock_get_holdings_and_allocations_data, caplog
):
"""Test fetch_allocations_holdings when unavailable future date is provided."""
with caplog.at_level(logging.INFO):
fetch_allocations_holdings((2025, 1))

assert (
"SDR data unavailable for date: 2025-1-31. Will fetch latest available"
in caplog.text
)