Files
OpenBB/openbb_platform/providers/fred/openbb_fred/models/search.py
2023-12-13 13:08:08 +00:00

139 lines
4.5 KiB
Python

"""FRED Search Model."""
from typing import Any, Dict, List, Literal, Optional, Union
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.fred_search import (
SearchData,
SearchQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
from openbb_core.provider.utils.helpers import (
amake_request,
get_querystring,
)
from pydantic import Field, NonNegativeInt
class FredSearchQueryParams(SearchQueryParams):
"""FRED Search Query Params."""
__alias_dict__ = {
"query": "search_text",
}
is_release: Optional[bool] = Field(
default=False,
description="Is release? If True, other search filter variables are ignored."
+ " If no query text or release_id is supplied, this defaults to True.",
)
release_id: Optional[Union[str, int]] = Field(
default=None,
description="A specific release ID to target.",
)
limit: Optional[int] = Field(
default=None,
description=QUERY_DESCRIPTIONS.get("limit", "") + " (1-1000)",
)
offset: Optional[NonNegativeInt] = Field(
default=0,
description="Offset the results in conjunction with limit.",
)
filter_variable: Literal[None, "frequency", "units", "seasonal_adjustment"] = Field(
default=None, description="Filter by an attribute."
)
filter_value: Optional[str] = Field(
default=None,
description="String value to filter the variable by. Used in conjunction with filter_variable.",
)
tag_names: Optional[str] = Field(
default=None,
description="A semicolon delimited list of tag names that series match all of. Example: 'japan;imports'",
)
exclude_tag_names: Optional[str] = Field(
default=None,
description="A semicolon delimited list of tag names that series match none of. Example: 'imports;services'."
+ " Requires that variable tag_names also be set to limit the number of matching series.",
)
class FredSearchData(SearchData):
"""FRED Search Data."""
__alias_dict__ = {"url": "link"}
popularity: Optional[int] = Field(
default=None,
description="Popularity of the series",
)
group_popularity: Optional[int] = Field(
default=None,
description="Group popularity of the release",
)
class FredSearchFetcher(
Fetcher[
FredSearchQueryParams,
List[FredSearchData],
]
):
"""FRED Search Fetcher."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> FredSearchQueryParams:
"""Transform query."""
transformed_params = FredSearchQueryParams(**params)
if transformed_params.query is None and transformed_params.release_id is None:
transformed_params.is_release = True
return transformed_params
@staticmethod
async def aextract_data(
query: FredSearchQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
"""Extract the raw data."""
api_key = credentials.get("fred_api_key") if credentials else ""
if query.is_release is True:
url = f"https://api.stlouisfed.org/fred/releases?api_key={api_key}&file_type=json"
response = await amake_request(url, timeout=5, **kwargs)
return response.get("releases") # type: ignore[return-value, union-attr]
url = (
"https://api.stlouisfed.org/fred/release/series?"
if query.release_id is not None
else "https://api.stlouisfed.org/fred/series/search?"
)
exclude = (
["is_release", "search_text"]
if query.release_id is not None
else ["is_release"]
)
querystring = get_querystring(query.model_dump(), exclude).replace(" ", "+")
url = url + querystring + f"&file_type=json&api_key={api_key}"
response = await amake_request(url, timeout=5, **kwargs)
return response.get("seriess") # type: ignore[return-value, union-attr]
@staticmethod
def transform_data(
query: FredSearchQueryParams, data: List[Dict], **kwargs: Any
) -> List[FredSearchData]:
"""Transform data."""
for observation in data:
id_column_name = "release_id" if query.is_release is True else "series_id"
observation[id_column_name] = observation.pop("id")
observation.pop("realtime_start", None)
observation.pop("realtime_end", None)
return [FredSearchData.model_validate(d) for d in data]