Add OECD unemployment (+more) + data cache idea (#5917)

* Add OECD unemployment + data cache idea

* Docstring edits

* Tests

* Add composite leading indicator

* i truested ruff --fix too much

* Add short + long term interest rates

* pylint

* pylint2

* was not committing.  my b

* add csv cache + make it default.  remove pyarrow from poetry

* Clean up

* Didnt run on this file.  My bad

* Why was this ignored /shrug

* pylint

---------

Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
This commit is contained in:
James Maslek
2024-01-30 19:34:09 -05:00
committed by GitHub
parent 7a07970fc8
commit bbf5da0e58
24 changed files with 124791 additions and 324 deletions

View File

@@ -0,0 +1,37 @@
"""Composite Leading Indicator Standard Model."""
from datetime import date as dateType
from typing import Optional
from pydantic import Field
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
class CLIQueryParams(QueryParams):
"""Composite Leading Indicator Query."""
start_date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("start_date")
)
end_date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("end_date")
)
class CLIData(Data):
"""Composite Leading Indicator Data."""
date: Optional[dateType] = Field(
default=None, description=DATA_DESCRIPTIONS.get("date")
)
value: Optional[float] = Field(default=None, description="CLI value")
country: Optional[str] = Field(
default=None,
description="Country for which CLI is given",
)

View File

@@ -0,0 +1,40 @@
"""Long Term Interest Rates Standard Model."""
from datetime import date as dateType
from typing import Optional
from pydantic import Field
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
class LTIRQueryParams(QueryParams):
"""Long Term Interest Rates Query."""
start_date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("start_date")
)
end_date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("end_date")
)
class LTIRData(Data):
"""Long Term Interest Rates Data."""
date: Optional[dateType] = Field(
default=None, description=DATA_DESCRIPTIONS.get("date")
)
value: Optional[float] = Field(
default=None,
description="Interest rate (given as a whole number, i.e 10=10%)",
)
country: Optional[str] = Field(
default=None,
description="Country for which interest rate is given",
)

View File

@@ -0,0 +1,40 @@
"""Short Term Interest Rates Standard Model."""
from datetime import date as dateType
from typing import Optional
from pydantic import Field
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
class STIRQueryParams(QueryParams):
"""Short Term Interest Rates Query."""
start_date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("start_date")
)
end_date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("end_date")
)
class STIRData(Data):
"""Short Term Interest Rates Data."""
date: Optional[dateType] = Field(
default=None, description=DATA_DESCRIPTIONS.get("date")
)
value: Optional[float] = Field(
default=None,
description="Interest rate (given as a whole number, i.e 10=10%)",
)
country: Optional[str] = Field(
default=None,
description="Country for which interest rate is given",
)

View File

@@ -0,0 +1,40 @@
"""Unemployment Standard Model."""
from datetime import date as dateType
from typing import Optional
from pydantic import Field
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
class UnemploymentQueryParams(QueryParams):
"""Unemployment Query."""
start_date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("start_date")
)
end_date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("end_date")
)
class UnemploymentData(Data):
"""Unemployment Data."""
date: Optional[dateType] = Field(
default=None, description=DATA_DESCRIPTIONS.get("date")
)
value: Optional[float] = Field(
default=None,
description="Unemployment rate (given as a whole number, i.e 10=10%)",
)
country: Optional[str] = Field(
default=None,
description="Country for which unemployment rate is given",
)

View File

@@ -372,3 +372,109 @@ def test_economy_money_measures(params, headers):
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
@parametrize(
"params",
[
({"start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"sex": "total",
"frequency": "monthly",
"age": "total",
"seasonal_adjustment": True,
"provider": "oecd",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
],
)
@pytest.mark.integration
def test_economy_unemployment(params, headers):
params = {p: v for p, v in params.items() if v}
query_str = get_querystring(params, [])
url = f"http://0.0.0.0:8000/api/v1/economy/unemployment?{query_str}"
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
@parametrize(
"params",
[
({"start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"provider": "oecd",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
],
)
@pytest.mark.integration
def test_economy_composite_leading_indicator(params, headers):
params = {p: v for p, v in params.items() if v}
query_str = get_querystring(params, [])
url = f"http://0.0.0.0:8000/api/v1/economy/composite_leading_indicator?{query_str}"
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
@parametrize(
"params",
[
({"start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"frequency": "monthly",
"provider": "oecd",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
],
)
@pytest.mark.integration
def test_economy_short_term_interest_rate(params, headers):
params = {p: v for p, v in params.items() if v}
query_str = get_querystring(params, [])
url = f"http://0.0.0.0:8000/api/v1/economy/short_term_interest_rate?{query_str}"
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
@parametrize(
"params",
[
({"start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"frequency": "monthly",
"provider": "oecd",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
],
)
@pytest.mark.integration
def test_economy_long_term_interest_rate(params, headers):
params = {p: v for p, v in params.items() if v}
query_str = get_querystring(params, [])
url = f"http://0.0.0.0:8000/api/v1/economy/long_term_interest_rate?{query_str}"
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200

View File

@@ -328,3 +328,105 @@ def test_economy_money_measures(params, obb):
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@parametrize(
"params",
[
({"start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"sex": "total",
"frequency": "monthly",
"age": "total",
"seasonal_adjustment": True,
"provider": "oecd",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
],
)
@pytest.mark.integration
def test_economy_unemployment(params, obb):
params = {p: v for p, v in params.items() if v}
result = obb.economy.unemployment(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@parametrize(
"params",
[
({"start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"provider": "oecd",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
],
)
@pytest.mark.integration
def test_economy_composite_leading_indicator(params, obb):
params = {p: v for p, v in params.items() if v}
result = obb.economy.composite_leading_indicator(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@parametrize(
"params",
[
({"start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"frequency": "monthly",
"provider": "oecd",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
],
)
@pytest.mark.integration
def test_economy_short_term_interest_rate(params, obb):
params = {p: v for p, v in params.items() if v}
result = obb.economy.short_term_interest_rate(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@parametrize(
"params",
[
({"start_date": "2023-01-01", "end_date": "2023-06-06"}),
(
{
"country": "united_states",
"frequency": "monthly",
"provider": "oecd",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
}
),
],
)
@pytest.mark.integration
def test_economy_long_term_interest_rate(params, obb):
params = {p: v for p, v in params.items() if v}
result = obb.economy.long_term_interest_rate(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0

View File

@@ -96,5 +96,67 @@ async def money_measures(
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject[BaseModel]:
"""Money Measures."""
"""Money Measures (M1/M2 and components)."""
return await OBBject.from_query(Query(**locals()))
@router.command(model="Unemployment")
async def unemployment(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject[BaseModel]:
"""Global unemployment data."""
return await OBBject.from_query(Query(**locals()))
@router.command(model="CLI")
async def composite_leading_indicator(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject[BaseModel]:
"""The composite leading indicator (CLI) is designed to provide early signals of turning points
in business cycles showing fluctuation of the economic activity around its long term potential level.
CLIs show short-term economic movements in qualitative rather than quantitative terms.
"""
return await OBBject.from_query(Query(**locals()))
@router.command(model="STIR")
async def short_term_interest_rate(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject[BaseModel]:
"""
Short-term interest rates are the rates at which short-term borrowings are effected between
financial institutions or the rate at which short-term government paper is issued or traded in the market.
Short-term interest rates are generally averages of daily rates, measured as a percentage.
Short-term interest rates are based on three-month money market rates where available.
Typical standardised names are "money market rate" and "treasury bill rate".
"""
return await OBBject.from_query(Query(**locals()))
@router.command(model="STIR")
async def long_term_interest_rate(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject[BaseModel]:
"""
Long-term interest rates refer to government bonds maturing in ten years.
Rates are mainly determined by the price charged by the lender, the risk from the borrower and the
fall in the capital value. Long-term interest rates are generally averages of daily rates,
measured as a percentage. These interest rates are implied by the prices at which the government bonds are
traded on financial markets, not the interest rates at which the loans were issued.
In all cases, they refer to bonds whose capital repayment is guaranteed by governments.
Long-term interest rates are one of the determinants of business investment.
Low long-term interest rates encourage investment in new equipment and high interest rates discourage it.
Investment is, in turn, a major source of economic growth."""
return await OBBject.from_query(Query(**locals()))

View File

@@ -20,6 +20,7 @@ class ROUTER_economy(Container):
/gdp
money_measures
risk_premium
unemployment
"""
def __repr__(self) -> str:
@@ -571,7 +572,7 @@ class ROUTER_economy(Container):
provider: Optional[Literal["federal_reserve"]] = None,
**kwargs
) -> OBBject:
"""Money Measures.
"""Money Measures (M1/M2 and components.
Parameters
----------
@@ -694,3 +695,87 @@ class ROUTER_economy(Container):
extra_params=kwargs,
)
)
@validate
def unemployment(
self,
start_date: Annotated[
Union[datetime.date, None, str],
OpenBBCustomParameter(
description="Start date of the data, in YYYY-MM-DD format."
),
] = None,
end_date: Annotated[
Union[datetime.date, None, str],
OpenBBCustomParameter(
description="End date of the data, in YYYY-MM-DD format."
),
] = None,
provider: Optional[Literal["oecd"]] = None,
**kwargs
) -> OBBject:
"""Global unemployment data.
Parameters
----------
start_date : Optional[datetime.date]
Start date of the data, in YYYY-MM-DD format.
end_date : Optional[datetime.date]
End date of the data, in YYYY-MM-DD format.
provider : Optional[Literal['oecd']]
The provider to use for the query, by default None.
If None, the provider specified in defaults is selected or 'oecd' if there is
no default.
country : Literal['colombia', 'new_zealand', 'united_kingdom', 'italy', 'luxembourg', 'euro_area19', 'sweden', 'oecd', 'south_africa', 'denmark', 'canada', 'switzerland', 'slovakia', 'hungary', 'portugal', 'spain', 'france', 'czech_republic', 'costa_rica', 'japan', 'slovenia', 'russia', 'austria', 'latvia', 'netherlands', 'israel', 'iceland', 'united_states', 'ireland', 'mexico', 'germany', 'greece', 'turkey', 'australia', 'poland', 'south_korea', 'chile', 'finland', 'european_union27_2020', 'norway', 'lithuania', 'euro_area20', 'estonia', 'belgium', 'brazil', 'indonesia', 'all']
Country to get GDP for. (provider: oecd)
sex : Literal['total', 'male', 'female']
Sex to get unemployment for. (provider: oecd)
frequency : Literal['monthly', 'quarterly', 'annual']
Frequency to get unemployment for. (provider: oecd)
age : Literal['total', '15-24', '15-64', '25-54', '55-64']
Age group to get unemployment for. Total indicates 15 years or over (provider: oecd)
seasonal_adjustment : bool
Whether to get seasonally adjusted unemployment. Defaults to False. (provider: oecd)
Returns
-------
OBBject
results : List[Unemployment]
Serializable results.
provider : Optional[Literal['oecd']]
Provider name.
warnings : Optional[List[Warning_]]
List of warnings.
chart : Optional[Chart]
Chart object.
extra: Dict[str, Any]
Extra info.
Unemployment
------------
date : Optional[date]
The date of the data.
value : Optional[float]
Unemployment rate (given as a whole number, i.e 10=10%)
country : Optional[str]
Country for which unemployment rate is given
Example
-------
>>> from openbb import obb
>>> obb.economy.unemployment()
""" # noqa: E501
return self._run(
"/economy/unemployment",
**filter_inputs(
provider_choices={
"provider": provider,
},
standard_params={
"start_date": start_date,
"end_date": end_date,
},
extra_params=kwargs,
)
)

View File

@@ -1,9 +1,13 @@
"""FRED provider module."""
"""OECD provider module."""
from openbb_core.provider.abstract.provider import Provider
from openbb_oecd.models.composite_leading_indicator import OECDCLIFetcher
from openbb_oecd.models.gdp_forecast import OECDGdpForecastFetcher
from openbb_oecd.models.gdp_nominal import OECDGdpNominalFetcher
from openbb_oecd.models.gdp_real import OECDGdpRealFetcher
from openbb_oecd.models.long_term_interest_rate import OECDLTIRFetcher
from openbb_oecd.models.short_term_interest_rate import OECDSTIRFetcher
from openbb_oecd.models.unemployment import OECDUnemploymentFetcher
oecd_provider = Provider(
name="oecd",
@@ -13,5 +17,9 @@ oecd_provider = Provider(
"GdpNominal": OECDGdpNominalFetcher,
"GdpReal": OECDGdpRealFetcher,
"GdpForecast": OECDGdpForecastFetcher,
"Unemployment": OECDUnemploymentFetcher,
"CLI": OECDCLIFetcher,
"STIR": OECDSTIRFetcher,
"LTIR": OECDLTIRFetcher,
},
)

View File

@@ -0,0 +1,130 @@
"""OECD CLI Data."""
import re
from datetime import date, timedelta
from typing import Any, Dict, List, Literal, Optional, Union
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.composite_leading_indicator import (
CLIData,
CLIQueryParams,
)
from openbb_oecd.utils import helpers
from pydantic import Field, field_validator
cli_mapping = {
"USA": "united_states",
"GBR": "united_kingdom",
"JPN": "japan",
"MEX": "mexico",
"IDN": "indonesia",
"AUS": "australia",
"BRA": "brazil",
"CAN": "canada",
"ITA": "italy",
"DEU": "germany",
"TUR": "turkey",
"FRA": "france",
"ZAF": "south_africa",
"KOR": "south_korea",
"ESP": "spain",
"IND": "india",
"CHN": "china",
"G7": "g7",
"G20": "g20",
}
countries = tuple(cli_mapping.values()) + ("all",)
CountriesLiteral = Literal[countries] # type: ignore
country_to_code = {v: k for k, v in cli_mapping.items()}
class OECDCLIQueryParams(CLIQueryParams):
"""OECD CLI Query."""
country: CountriesLiteral = Field(
description="Country to get GDP for.", default="united_states"
)
class OECDCLIData(CLIData):
"""OECD CLI Data."""
@field_validator("date", mode="before")
@classmethod
def date_validate(cls, in_date: Union[date, str]): # pylint: disable=E0213
"""Validate value."""
if isinstance(in_date, str):
# i.e 2022-Q1
if re.match(r"\d{4}-Q[1-4]$", in_date):
year, quarter = in_date.split("-")
_year = int(year)
if quarter == "Q1":
return date(_year, 3, 31)
if quarter == "Q2":
return date(_year, 6, 30)
if quarter == "Q3":
return date(_year, 9, 30)
if quarter == "Q4":
return date(_year, 12, 31)
# Now match if it is monthly, i.e 2022-01
elif re.match(r"\d{4}-\d{2}$", in_date):
year, month = map(int, in_date.split("-"))
if month == 12:
return date(year, month, 31)
next_month = date(year, month + 1, 1)
return date(next_month.year, next_month.month, 1) - timedelta(days=1)
# Now match if it is yearly, i.e 2022
elif re.match(r"\d{4}$", in_date):
return date(int(in_date), 12, 31)
# If the input date is a year
if isinstance(in_date, int):
return date(in_date, 12, 31)
return in_date
class OECDCLIFetcher(Fetcher[OECDCLIQueryParams, List[OECDCLIData]]):
"""Transform the query, extract and transform the data from the OECD endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> OECDCLIQueryParams:
"""Transform the query."""
transformed_params = params.copy()
if transformed_params["start_date"] is None:
transformed_params["start_date"] = date(1950, 1, 1)
if transformed_params["end_date"] is None:
transformed_params["end_date"] = date(date.today().year, 12, 31)
return OECDCLIQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: OECDCLIQueryParams, # pylint: disable=W0613
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
"""Return the raw data from the OECD endpoint."""
url = "https://sdmx.oecd.org/public/rest/data/OECD.SDD.STES,DSD_KEI@DF_KEI,4.0/..LI...."
data = helpers.get_possibly_cached_data(
url, function="economy_composite_leading_indicator"
)
if query.country != "all":
data = data.query(f"REF_AREA == '{country_to_code[query.country]}'")
# Filter down
data = data.reset_index(drop=True)[["REF_AREA", "TIME_PERIOD", "VALUE"]].rename(
columns={"REF_AREA": "country", "TIME_PERIOD": "date", "VALUE": "value"}
)
data["country"] = data["country"].map(cli_mapping)
return data.to_dict(orient="records")
@staticmethod
def transform_data(
query: OECDCLIQueryParams, data: Dict, **kwargs: Any
) -> List[OECDCLIData]:
"""Transform the data from the OECD endpoint."""
return [OECDCLIData.model_validate(d) for d in data]

View File

@@ -0,0 +1,163 @@
"""OECD Long Term Interest Rate Rate Data."""
import re
from datetime import date, timedelta
from typing import Any, Dict, List, Literal, Optional, Union
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.long_term_interest_rate import (
LTIRData,
LTIRQueryParams,
)
from openbb_oecd.utils import helpers
from pydantic import Field, field_validator
ltir_mapping = {
"BEL": "belgium",
"IRL": "ireland",
"MEX": "mexico",
"IDN": "indonesia",
"NZL": "new_zealand",
"JPN": "japan",
"GBR": "united_kingdom",
"FRA": "france",
"CHL": "chile",
"CAN": "canada",
"NLD": "netherlands",
"USA": "united_states",
"KOR": "south_korea",
"NOR": "norway",
"AUT": "austria",
"ZAF": "south_africa",
"DNK": "denmark",
"CHE": "switzerland",
"HUN": "hungary",
"LUX": "luxembourg",
"AUS": "australia",
"DEU": "germany",
"SWE": "sweden",
"ISL": "iceland",
"TUR": "turkey",
"GRC": "greece",
"ISR": "israel",
"CZE": "czech_republic",
"LVA": "latvia",
"SVN": "slovenia",
"POL": "poland",
"EST": "estonia",
"LTU": "lithuania",
"PRT": "portugal",
"CRI": "costa_rica",
"SVK": "slovakia",
"FIN": "finland",
"ESP": "spain",
"RUS": "russia",
"EA19": "euro_area19",
"COL": "colombia",
"ITA": "italy",
"IND": "india",
"CHN": "china",
"HRV": "croatia",
}
countries = tuple(ltir_mapping.values()) + ("all",)
CountriesLiteral = Literal[countries] # type: ignore
country_to_code = {v: k for k, v in ltir_mapping.items()}
class OECDLTIRQueryParams(LTIRQueryParams):
"""OECD Short Term Interest Rate Query."""
country: CountriesLiteral = Field(
description="Country to get GDP for.", default="united_states"
)
frequency: Literal["monthly", "quarterly", "annual"] = Field(
description="Frequency to get interest rate for for.", default="monthly"
)
class OECDLTIRData(LTIRData):
"""OECD Long Term Interest Rate Data."""
@field_validator("date", mode="before")
@classmethod
def date_validate(cls, in_date: Union[date, str]): # pylint: disable=E0213
"""Validate value."""
if isinstance(in_date, str):
# i.e 2022-Q1
if re.match(r"\d{4}-Q[1-4]$", in_date):
year, quarter = in_date.split("-")
_year = int(year)
if quarter == "Q1":
return date(_year, 3, 31)
if quarter == "Q2":
return date(_year, 6, 30)
if quarter == "Q3":
return date(_year, 9, 30)
if quarter == "Q4":
return date(_year, 12, 31)
# Now match if it is monthly, i.e 2022-01
elif re.match(r"\d{4}-\d{2}$", in_date):
year, month = map(int, in_date.split("-"))
if month == 12:
return date(year, month, 31)
next_month = date(year, month + 1, 1)
return date(next_month.year, next_month.month, 1) - timedelta(days=1)
# Now match if it is yearly, i.e 2022
elif re.match(r"\d{4}$", in_date):
return date(int(in_date), 12, 31)
# If the input date is a year
if isinstance(in_date, int):
return date(in_date, 12, 31)
return in_date
class OECDLTIRFetcher(Fetcher[OECDLTIRQueryParams, List[OECDLTIRData]]):
"""Transform the query, extract and transform the data from the OECD endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> OECDLTIRQueryParams:
"""Transform the query."""
transformed_params = params.copy()
if transformed_params["start_date"] is None:
transformed_params["start_date"] = date(1950, 1, 1)
if transformed_params["end_date"] is None:
transformed_params["end_date"] = date(date.today().year, 12, 31)
return OECDLTIRQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: OECDLTIRQueryParams, # pylint: disable=W0613
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
"""Return the raw data from the OECD endpoint."""
frequency = query.frequency[0].upper()
country = "" if query.country == "all" else country_to_code[query.country]
url = "https://sdmx.oecd.org/public/rest/data/OECD.SDD.STES,DSD_KEI@DF_KEI,4.0/..IRLT...."
data = helpers.get_possibly_cached_data(
url, function="economy_long_term_interest_rate"
)
query = f"FREQ=='{frequency}'"
query = query + f" & REF_AREA=='{country}'" if country else query
# Filter down
data = (
data.query(query)
.reset_index(drop=True)[["REF_AREA", "TIME_PERIOD", "VALUE"]]
.rename(
columns={"REF_AREA": "country", "TIME_PERIOD": "date", "VALUE": "value"}
)
)
data["country"] = data["country"].map(ltir_mapping)
return data.to_dict(orient="records")
@staticmethod
def transform_data(
query: OECDLTIRQueryParams, data: Dict, **kwargs: Any
) -> List[OECDLTIRData]:
"""Transform the data from the OECD endpoint."""
return [OECDLTIRData.model_validate(d) for d in data]

View File

@@ -0,0 +1,163 @@
"""OECD Short Term Interest Rate Rate Data."""
import re
from datetime import date, timedelta
from typing import Any, Dict, List, Literal, Optional, Union
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.short_term_interest_rate import (
STIRData,
STIRQueryParams,
)
from openbb_oecd.utils import helpers
from pydantic import Field, field_validator
stir_mapping = {
"BEL": "belgium",
"IRL": "ireland",
"MEX": "mexico",
"IDN": "indonesia",
"NZL": "new_zealand",
"JPN": "japan",
"GBR": "united_kingdom",
"FRA": "france",
"CHL": "chile",
"CAN": "canada",
"NLD": "netherlands",
"USA": "united_states",
"KOR": "south_korea",
"NOR": "norway",
"AUT": "austria",
"ZAF": "south_africa",
"DNK": "denmark",
"CHE": "switzerland",
"HUN": "hungary",
"LUX": "luxembourg",
"AUS": "australia",
"DEU": "germany",
"SWE": "sweden",
"ISL": "iceland",
"TUR": "turkey",
"GRC": "greece",
"ISR": "israel",
"CZE": "czech_republic",
"LVA": "latvia",
"SVN": "slovenia",
"POL": "poland",
"EST": "estonia",
"LTU": "lithuania",
"PRT": "portugal",
"CRI": "costa_rica",
"SVK": "slovakia",
"FIN": "finland",
"ESP": "spain",
"RUS": "russia",
"EA19": "euro_area19",
"COL": "colombia",
"ITA": "italy",
"IND": "india",
"CHN": "china",
"HRV": "croatia",
}
countries = tuple(stir_mapping.values()) + ("all",)
CountriesLiteral = Literal[countries] # type: ignore
country_to_code = {v: k for k, v in stir_mapping.items()}
class OECDSTIRQueryParams(STIRQueryParams):
"""OECD Short Term Interest Rate Query."""
country: CountriesLiteral = Field(
description="Country to get GDP for.", default="united_states"
)
frequency: Literal["monthly", "quarterly", "annual"] = Field(
description="Frequency to get interest rate for for.", default="monthly"
)
class OECDSTIRData(STIRData):
"""OECD Short Term Interest Rate Data."""
@field_validator("date", mode="before")
@classmethod
def date_validate(cls, in_date: Union[date, str]): # pylint: disable=E0213
"""Validate value."""
if isinstance(in_date, str):
# i.e 2022-Q1
if re.match(r"\d{4}-Q[1-4]$", in_date):
year, quarter = in_date.split("-")
_year = int(year)
if quarter == "Q1":
return date(_year, 3, 31)
if quarter == "Q2":
return date(_year, 6, 30)
if quarter == "Q3":
return date(_year, 9, 30)
if quarter == "Q4":
return date(_year, 12, 31)
# Now match if it is monthly, i.e 2022-01
elif re.match(r"\d{4}-\d{2}$", in_date):
year, month = map(int, in_date.split("-"))
if month == 12:
return date(year, month, 31)
next_month = date(year, month + 1, 1)
return date(next_month.year, next_month.month, 1) - timedelta(days=1)
# Now match if it is yearly, i.e 2022
elif re.match(r"\d{4}$", in_date):
return date(int(in_date), 12, 31)
# If the input date is a year
if isinstance(in_date, int):
return date(in_date, 12, 31)
return in_date
class OECDSTIRFetcher(Fetcher[OECDSTIRQueryParams, List[OECDSTIRData]]):
"""Transform the query, extract and transform the data from the OECD endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> OECDSTIRQueryParams:
"""Transform the query."""
transformed_params = params.copy()
if transformed_params["start_date"] is None:
transformed_params["start_date"] = date(1950, 1, 1)
if transformed_params["end_date"] is None:
transformed_params["end_date"] = date(date.today().year, 12, 31)
return OECDSTIRQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: OECDSTIRQueryParams, # pylint: disable=W0613
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
"""Return the raw data from the OECD endpoint."""
frequency = query.frequency[0].upper()
country = "" if query.country == "all" else country_to_code[query.country]
url = "https://sdmx.oecd.org/public/rest/data/OECD.SDD.STES,DSD_KEI@DF_KEI,4.0/..IR3TIB...."
data = helpers.get_possibly_cached_data(
url, function="economy_short_term_interest_rate"
)
query = f"FREQ=='{frequency}'"
query = query + f" & REF_AREA=='{country}'" if country else query
# Filter down
data = (
data.query(query)
.reset_index(drop=True)[["REF_AREA", "TIME_PERIOD", "VALUE"]]
.rename(
columns={"REF_AREA": "country", "TIME_PERIOD": "date", "VALUE": "value"}
)
)
data["country"] = data["country"].map(stir_mapping)
return data.to_dict(orient="records")
@staticmethod
def transform_data(
query: OECDSTIRQueryParams, data: Dict, **kwargs: Any
) -> List[OECDSTIRData]:
"""Transform the data from the OECD endpoint."""
return [OECDSTIRData.model_validate(d) for d in data]

View File

@@ -0,0 +1,182 @@
"""OECD Unemployment Data."""
import re
from datetime import date, timedelta
from typing import Any, Dict, List, Literal, Optional, Union
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.unemployment import (
UnemploymentData,
UnemploymentQueryParams,
)
from openbb_oecd.utils import helpers
from pydantic import Field, field_validator
country_mapping = {
"COL": "colombia",
"NZL": "new_zealand",
"GBR": "united_kingdom",
"ITA": "italy",
"LUX": "luxembourg",
"EA19": "euro_area19",
"SWE": "sweden",
"OECD": "oecd",
"ZAF": "south_africa",
"DNK": "denmark",
"CAN": "canada",
"CHE": "switzerland",
"SVK": "slovakia",
"HUN": "hungary",
"PRT": "portugal",
"ESP": "spain",
"FRA": "france",
"CZE": "czech_republic",
"CRI": "costa_rica",
"JPN": "japan",
"SVN": "slovenia",
"RUS": "russia",
"AUT": "austria",
"LVA": "latvia",
"NLD": "netherlands",
"ISR": "israel",
"ISL": "iceland",
"USA": "united_states",
"IRL": "ireland",
"MEX": "mexico",
"DEU": "germany",
"GRC": "greece",
"TUR": "turkey",
"AUS": "australia",
"POL": "poland",
"KOR": "south_korea",
"CHL": "chile",
"FIN": "finland",
"EU27_2020": "european_union27_2020",
"NOR": "norway",
"LTU": "lithuania",
"EA20": "euro_area20",
"EST": "estonia",
"BEL": "belgium",
"BRA": "brazil",
"IDN": "indonesia",
}
countries = tuple(country_mapping.values()) + ("all",)
CountriesLiteral = Literal[countries] # type: ignore
country_to_code = {v: k for k, v in country_mapping.items()}
class OECDUnemploymentQueryParams(UnemploymentQueryParams):
"""OECD Unemployment Query."""
country: CountriesLiteral = Field(
description="Country to get GDP for.", default="united_states"
)
sex: Literal["total", "male", "female"] = Field(
description="Sex to get unemployment for.", default="total"
)
frequency: Literal["monthly", "quarterly", "annual"] = Field(
description="Frequency to get unemployment for.", default="monthly"
)
age: Literal["total", "15-24", "15-64", "25-54", "55-64"] = Field(
description="Age group to get unemployment for. Total indicates 15 years or over",
default="total",
)
seasonal_adjustment: bool = Field(
description="Whether to get seasonally adjusted unemployment. Defaults to False.",
default=False,
)
class OECDUnemploymentData(UnemploymentData):
"""OECD Unemployment Data."""
@field_validator("date", mode="before")
@classmethod
def date_validate(cls, in_date: Union[date, str]): # pylint: disable=E0213
"""Validate value."""
if isinstance(in_date, str):
# i.e 2022-Q1
if re.match(r"\d{4}-Q[1-4]$", in_date):
year, quarter = in_date.split("-")
_year = int(year)
if quarter == "Q1":
return date(_year, 3, 31)
if quarter == "Q2":
return date(_year, 6, 30)
if quarter == "Q3":
return date(_year, 9, 30)
if quarter == "Q4":
return date(_year, 12, 31)
# Now match if it is monthly, i.e 2022-01
elif re.match(r"\d{4}-\d{2}$", in_date):
year, month = map(int, in_date.split("-"))
if month == 12:
return date(year, month, 31)
next_month = date(year, month + 1, 1)
return date(next_month.year, next_month.month, 1) - timedelta(days=1)
# Now match if it is yearly, i.e 2022
elif re.match(r"\d{4}$", in_date):
return date(int(in_date), 12, 31)
# If the input date is a year
if isinstance(in_date, int):
return date(in_date, 12, 31)
return in_date
class OECDUnemploymentFetcher(
Fetcher[OECDUnemploymentQueryParams, List[OECDUnemploymentData]]
):
"""Transform the query, extract and transform the data from the OECD endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> OECDUnemploymentQueryParams:
"""Transform the query."""
transformed_params = params.copy()
if transformed_params["start_date"] is None:
transformed_params["start_date"] = date(1950, 1, 1)
if transformed_params["end_date"] is None:
transformed_params["end_date"] = date(date.today().year, 12, 31)
return OECDUnemploymentQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: OECDUnemploymentQueryParams, # pylint: disable=W0613
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
"""Return the raw data from the OECD endpoint."""
sex = {"total": "_T", "male": "M", "female": "F"}[query.sex]
frequency = query.frequency[0].upper()
age = {
"total": "Y_GE15",
"15-24": "Y15T24",
"15-64": "Y15T64",
"25-54": "Y25T54",
"55-64": "Y55T64",
}[query.age]
seasonal_adjustment = "Y" if query.seasonal_adjustment else "N"
country = "" if query.country == "all" else country_to_code[query.country]
url = "https://sdmx.oecd.org/public/rest/data/OECD.SDD.TPS,DSD_LFS@DF_IALFS_INDIC,1.0/.UNE_LF........"
data = helpers.get_possibly_cached_data(url, function="economy_unemployment")
query = f"AGE=='{age}' & SEX=='{sex}' & FREQ=='{frequency}' & ADJUSTMENT=='{seasonal_adjustment}'"
query = query + f" & REF_AREA=='{country}'" if country else query
# Filter down
data = (
data.query(query)
.reset_index(drop=True)[["REF_AREA", "TIME_PERIOD", "VALUE"]]
.rename(
columns={"REF_AREA": "country", "TIME_PERIOD": "date", "VALUE": "value"}
)
)
data["country"] = data["country"].map(country_mapping)
return data.to_dict(orient="records")
@staticmethod
def transform_data(
query: OECDUnemploymentQueryParams, data: Dict, **kwargs: Any
) -> List[OECDUnemploymentData]:
"""Transform the data from the OECD endpoint."""
return [OECDUnemploymentData.model_validate(d) for d in data]

View File

@@ -1,11 +1,19 @@
import ssl
from datetime import date
from io import StringIO
from pathlib import Path
from typing import Any, Dict, Optional
import requests
import urllib3
from defusedxml.ElementTree import fromstring
from openbb_core.app.utils import get_user_cache_directory
from openbb_core.provider import helpers
from pandas import DataFrame, read_csv
from pandas import DataFrame, read_csv, read_parquet
cache = get_user_cache_directory() + "/oecd"
# Create the cache directory if it does not exist
Path(cache).mkdir(parents=True, exist_ok=True)
# OECD does not play well with newer python. This code block from stackoverflow helps
# to create a custom session:
@@ -18,8 +26,9 @@ class CustomHttpAdapter(requests.adapters.HTTPAdapter):
self.ssl_context = ssl_context
super().__init__(**kwargs)
# pylint: disable=arguments-differ
def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = urllib3.poolmanager.PoolManager(
self.poolmanager = urllib3.poolmanager.PoolManager( # pylint: disable=attribute-defined-outside-init
num_pools=connections,
maxsize=maxsize,
block=block,
@@ -27,6 +36,9 @@ class CustomHttpAdapter(requests.adapters.HTTPAdapter):
)
# pylint: enable=arguments-differ
def get_legacy_session():
"""Stackoverflow code to create a custom session."""
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
@@ -46,3 +58,174 @@ def fetch_data(url: str, csv_kwargs: Optional[Dict] = None, **kwargs: Any) -> Da
# or a delimiter.
data = read_csv(StringIO(response.text), **csv_kwargs)
return data
### The functions below are for using the new oecd data-explorer instead of the stats.oecd
def oecd_xml_to_df(xml_string: str) -> DataFrame:
"""Helper function to parse the OECD XML and return a dataframe.
Parameters
----------
xml_string : str
A string containing the OECD XML data.
Returns
-------
DataFrame
A Pandas DataFrame containing the parsed data from the XML string.
"""
root = fromstring(xml_string)
namespaces = {
"message": "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message",
"generic": "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic",
}
# Prepare a list to hold your extracted data
data = []
# Iterate through each 'Series' in the XML
for series in root.findall(".//generic:Series", namespaces=namespaces):
series_data = {}
# Extract series key values
for value in series.findall(".//generic:Value", namespaces=namespaces):
series_data[value.get("id")] = value.get("value")
# Extract observation values
for obs in series.findall("./generic:Obs", namespaces=namespaces):
obs_data = series_data.copy()
obs_data["TIME_PERIOD"] = obs.find(
"./generic:ObsDimension", namespaces=namespaces
).get("value")
obs_data["VALUE"] = obs.find(
"./generic:ObsValue", namespaces=namespaces
).get("value")
data.append(obs_data)
# Create a DataFrame
return DataFrame(data)
def parse_url(url: str) -> DataFrame:
"""Helper function to parse the SDMX url and return a dataframe.
Parameters
----------
url:str
URL to parse
Returns
-------
DataFrame
Pandas dataframe containing URL data
"""
response = helpers.make_request(url)
response.raise_for_status()
return oecd_xml_to_df(response.text)
def check_cache_exists_and_valid(function: str, cache_method: str = "csv") -> bool:
"""Check if the cache exists and is valid.
Parameters
----------
function : str
The name of the function for which the cache is being checked.
cache_method : str, optional
The method used for caching (default is 'csv').
Returns
-------
bool
True if the cache exists and is valid for the current day, False otherwise.
"""
# TODO: add setting to disable cache for tests
if cache_method not in ["csv", "parquet"]:
raise NotImplementedError("Currently only working with parquet or csv")
# First check that the cache exists. This will be a parquet/csv and a timestamp
cache_path = f"{cache}/{function}.{cache_method}"
time_cache_path = f"{cache}/{function}.timestamp"
if Path(cache_path).exists() and Path(time_cache_path).exists():
# Now check that the cache is valid. I am going to check that we write to a file the date the cache was made
# Read the timestamp
with open(time_cache_path) as f:
cached_date = f.read().strip()
# TODO: More robust caching logic
if cached_date == str(date.today()):
return True
return False
return False
def write_to_cache(function: str, data: DataFrame, cache_method: str) -> None:
"""Write data to the cache.
Parameters
----------
function : str
The name of the function for which data is being cached.
data : DataFrame
The DataFrame to be cached.
cache_method : str
The method used for caching the data.
Raises
------
NotImplementedError
If the cache_method is not 'parquet'.
"""
if cache_method == "parquet":
cache_path = f"{cache}/{function}.parquet"
data.to_parquet(cache_path, engine="pyarrow")
# Write the current date to a file called cache/function.timestamp
with open(f"{cache}/{function}.timestamp", "w") as f:
f.write(str(date.today()))
elif cache_method == "csv":
cache_path = f"{cache}/{function}.csv"
data.to_csv(cache_path)
# Write the current date to a file called cache/function.timestamp
with open(f"{cache}/{function}.timestamp", "w") as f:
f.write(str(date.today()))
else:
raise NotImplementedError
def get_possibly_cached_data(
url: str, function: Optional[str] = None, cache_method: str = "csv"
) -> DataFrame:
"""
Retrieve data from a given URL or from the cache if available and valid.
Parameters
----------
url : str
The URL from which to fetch the data if it's not available in the cache.
function : Optional[str], optional
The name of the function for which data is being fetched or cached.
cache_method : str, optional
The method used for caching the data (default is 'csv').
Returns
-------
DataFrame
A Pandas DataFrame containing the fetched or cached data.
"""
if cache_method == "parquet":
cache_path = f"{cache}/{function}.parquet"
elif cache_method == "csv":
cache_path = f"{cache}/{function}.csv"
use_cache = check_cache_exists_and_valid(
function=function, cache_method=cache_method
)
if use_cache:
if cache_method == "csv":
data = read_csv(cache_path)
elif cache_method == "parquet":
data = read_parquet(cache_path, engine="pyarrow")
else:
data = parse_url(url)
write_to_cache(function=function, data=data, cache_method=cache_method)
return data

View File

@@ -320,6 +320,17 @@ files = [
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "defusedxml"
version = "0.8.0rc2"
description = "XML bomb protection for Python stdlib modules"
optional = false
python-versions = ">=3.6"
files = [
{file = "defusedxml-0.8.0rc2-py2.py3-none-any.whl", hash = "sha256:1c812964311154c3bf4aaf3bc1443b31ee13530b7f255eaaa062c0553c76103d"},
{file = "defusedxml-0.8.0rc2.tar.gz", hash = "sha256:138c7d540a78775182206c7c97fe65b246a2f40b29471e1a2f1b0da76e7a3942"},
]
[[package]]
name = "ecdsa"
version = "0.18.0"
@@ -635,40 +646,6 @@ files = [
{file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"},
]
[[package]]
name = "numpy"
version = "1.25.2"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"},
{file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"},
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"},
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"},
{file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"},
{file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"},
{file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"},
{file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"},
{file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"},
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"},
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"},
{file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"},
{file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"},
{file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"},
{file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"},
{file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"},
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"},
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"},
{file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"},
{file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"},
{file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"},
{file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"},
]
[[package]]
name = "openbb-core"
version = "1.1.1"

View File

@@ -10,6 +10,7 @@ packages = [{ include = "openbb_oecd" }]
python = "^3.8"
openbb-core = "^1.1.1"
urllib3 = ">1.26.16"
defusedxml = "^0.8.0rc2"
[build-system]
requires = ["poetry-core"]

View File

@@ -17,61 +17,60 @@ interactions:
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
gNB4KB9Ia+P0Hy0YkmoY/cbJ/wPKKADtNCwAAA==
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
49kjMSTKks6mvBMlfke/cfL/AI5BaGhoKwAA
headers:
Access-Control-Allow-Headers:
- Content-Type
@@ -82,13 +81,13 @@ interactions:
Cache-Control:
- private
Content-Disposition:
- attachment;filename=DP_LIVE_10112023143409920.csv
- attachment;filename=DP_LIVE_26012024223440840.csv
Content-Encoding:
- gzip
Content-Type:
- text/csv
Date:
- Fri, 10 Nov 2023 13:34:09 GMT
- Fri, 26 Jan 2024 21:34:39 GMT
Transfer-Encoding:
- chunked
Vary:

View File

@@ -17,53 +17,54 @@ interactions:
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
XTSgJAAA
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
lBzB/wMA2DKSMiUAAA==
headers:
Access-Control-Allow-Headers:
- Content-Type
@@ -74,13 +75,13 @@ interactions:
Cache-Control:
- private
Content-Disposition:
- attachment;filename=DP_LIVE_10112023143407092.csv
- attachment;filename=DP_LIVE_26012024223438996.csv
Content-Encoding:
- gzip
Content-Type:
- text/csv
Date:
- Fri, 10 Nov 2023 13:34:06 GMT
- Fri, 26 Jan 2024 21:34:38 GMT
Transfer-Encoding:
- chunked
Vary:

View File

@@ -20,183 +20,181 @@ interactions:
7Z/Y/Wi0vTu+v3fw6ae7uw8eUqv3eXsPb++M7z+4d3/34f0H+9TsfV6/R6/vje8f3Lu3d/Bg/33f
3ue3d3d3Huw/3Nu5R61u/fauGfinuwf7Bw8fPNijVu/zNg2cxv3g4e7+/f0Hu/ep1fu8TePeG+/v
7D3c3bv3KbV5n3dp1EB7/2BvZ//+ew16jwd9b/zw/qeffrrz6d57jZlexpjH9+/f3z24d//gvYZM
L9OQd8d7+/cf7N17+Ol7zTS9TGPeGR/s7T/89N7+wXsR7B6PeXtnvPfpwb2HD3cODqgV3iZBoxY3
vE1MRm/vjfeIv0fv8RJRantvd/xwb3d39+b33HtEpO398cG9g5336o7IszveIZZ4eOu3iJFkZA/p
eZ+3aGh7JHSf7u0fvM9rNLKD8cOD/fd5h4a1P76/v0Mzftu3iFdoWPfGn97bu/2o6CUa1e698e7O
/f336otGdX98f3dv7/a0oLd4XJ/eO/j09pMsbPwpMeLBfe7rzVewEDe8RaxBb+2P7z3Y3SfJe7j3
Pm8SSbYPxgcPHlKXn95/8D6vKllIPd17INx12zeJNPfHn+4/fLh3cG+Hp+JWbxKz0EAfjD/dIa0I
KX+fN2mgxNFkhPYfPvj009sPlF6lgT4c7+7vfbqzs/8pS/pt36SBPhzvkdHc37l/e2yJeXic93Z2
Dx7ee59x0ps0zk9JBB98+nDv/sHtkaU3aZj3xg8O9u7t3XsgTHvbN2mY98b3yEQe7O2Rjb3tm8Lu
98Y7O/fvPTj49N49vHn2FA7SDW8SD9Gbe+MD8M/9vf33epMotH1/fO8hWZr9+0Lc275KJNq+N97f
P9h9sPdQ9fAtXyUakRuzt/uA+OjgUybSrV4lLqKhkmkj8XxICnlPlOstX6WxPiDWfUCctCMq5bZv
0lDBDQ93Pn3w8N57oUsjJcX38ID+t6sG9TZvEh/RQOnNB8S8+/sPmXdv+yaN8/547+Dg4R45ebfH
lt6kcX5K3uXuvYe7xkbe8k0a5/3x7n1SnPf3b/GivihMvz8mlbC3e29nhzXR8588pm9v6JKYiN7c
Ge/v7Ty4/2CXZ/O2LxJ9th+Md+7dv7f7cEds+m1fJQLBgybOfXjv3j3mvtu+ShQixj3Yffjpwf7+
zu1fJSaikdKrD3fv06TsiHt121dprOSEPCCvjAwiT8xt36Sh7o/39nY+3d3ZpyDpPd6kke4TeR+S
yiW/+dZvEhfRQO+TviZLCD56nzdpnKSLdnbJWT7Yvz2F6E0aJ6aFlPWO6uvbvknjJP+eQpKHpMVu
/aLw/A4ZtAc7B+SzMYFOfgph7Q1vEhPRm8R/+w/uER/tyzhv+ypRCHHrwQ7FvULb275JFCIbsbf7
KQWd+xTIvMerRCLy7in4ooGScbKv3vQqcZEM9eEu2cK9e7vsGN2qV3qVhko+Cjl/D+/RvL7PmzTU
exR17TzcufdAApLbvkkjJQbchVb49FMWl1u9SWxEAyVFT0xPBBIn5bZv0jjhajwkvXAgvtht36Rx
7tKbpHGp0/fClsa5M97d26FBEz/c+k3hetJi+xQ5HBDv8oQ+eXULgSE2klfJ+dslOyqydttXiUZg
+52D/b3d+/dE7972XaISuSmkx8jT3X0/jIlMhPG9vfsPiMTiptzqVeIkGiw5c/fJ7u9qHHjbN2ms
u3vje58SF5KL816v0lBJTokHH+w/EIfstm/SSMmVI7aHOWWRucWbzEo80HvkpHxKrierwdu+SQO9
h/CFArx9YcLbvknjpFCZlCdZJgnNb/smj5OC5QPych7enrbC+KQa7j24x1Ee3nz55XP69oY3iYv4
TbKk+0TeHRbw275JFCIfZ+/eAfW4K+r+tq8SiUjxfvqQMgP39vZ4Rm/7KtGIBGb3wf2dhxpW3upN
4iIaKcnLpzsPSLWQi/4+r9JQiet392hKHx4c3J6+9CoNlSJLEu1PCeHbj5TepJHCxBzskgoVH+dW
bxIf0UhJH+3uU05wd08c19u+SiMl9+gB+Z4PVNRu+yYN9D6Flp9SQLD/8L36pIGS5iU/+SFFlsz2
t3pT2B5TSoK2f2+f8sX06henvzd9/ROfP930KvERXiWlsn9Atu1Tm/G97dtEpu098nQePNi/v0uO
BzV7n9eJVpQFIoxJznfv7VKr93mb6EVR8YNP71FQvEOm9T3eJraSgVNqZe8eGcoH1Op93qaBU1qH
zBwx18Mdk+u+7ds0borEiKF3d+4dvG/XNGzCm+SBECf35z1eJh6jUZOZ3btP+WokPt7vZRr0PYpT
aDnkPnEpNXqfl2nMFLZSZLR3QAsT1Oh9XqYxU6Jxd+8BRSzkHbzHyyIdxCa7uw8fkH9BbfDuyfEt
gm3iMXqXJGv/U2Kxew+o5/d4lai1TdqS4sGHyL2wtrztu0QscuY/pQwexXWSHr3tq0Qq4mvClhIE
FIiObvsqcRYNlsb6YHdnj7SXuKq3fJPGSkMlZ4hWX3bF8N72VRoqTS1lJMgx0Tjglm/SSCmCINVO
Jvve7WlE7EQDpXw8UeghBbFs7G/7Jg10n3jpIflulLl5nzdpnIQtGc59UrRsxm77Jo0TAr+/Q7ZT
Egu3elM4H29SdEa+G9P27PUtEqTERfQmWU5ybUmrk2J+jzeJQuQOPaDcACEsrHvbV4lEJGzkxu9R
mPWAhe22rxKNyHaSX0IrEaRRb/smcRGNlMQFzEDZOObc275JI939lFgXynRHc8G3fJVG+pBsD80M
8cJ7dUoDJZ+GojrSSTCZm1/9aPSRvkp8RCOlV3f2qVtajHivV2moNKcU65C1FO/4tm/SSMmRIlNF
LoI4Urd9k0a6BxWIXJHkX271prA9ZcXIoSYVuMfzcvLtWwTOxEb0JrE9rQNj6V1E7ZZvEoWI7Sll
eP8hKU/mwNu+SiSiKICC13skbOIy3vZVohG9SrnV3Qe0RsTkvdWrxEg81PsP7hMnUbT/Pm/SUImP
SNDuU1R4n8X0tq/SUGntjrQ1xVlki9/jTRopLZ7AVaM1ytu/SXxEAyVni+IdUvfv9yYNlPwdLPaQ
Drw9celNGiclfR5QtpyY4b3epHHShJJf+SklFm7/pnA95SspANijZQyWl2eImm96k7iI3iRvA1HH
vU/vC9vf9lUi0fbuA4qUKO8IS/w+7xKRyH+nLCl5hPDA3+NVohK9+hDexgNNv9zqVWIkGixNzaef
Eo1vP1J6j0ZKA6UMygHNKUU67/EqDZSSh5SVIDssLs5t36RxUq6IVskpS//w9hQiPqJh7lMm5P6n
++Ru354b6E0aKM3Kg/vkaR+8x6zQmzRO4t2DHcrNfirm+7Zv0jjJ8JOjTEvsEvre6k3heoo/9yiY
oaUXlu4vT0+e0tc3vEpMRK+Su3H/U1qlvEfr+u/1LhFpe5f4iDJ4SKO838tEJ+Je0ruUrRf389av
EqHIu79HPLj/4D6F6ze/q+8SM9F4ybjR0vO9e3tM49v1Sm/SaHcpl0cpSwj5e1CZ3qXBUj6EROYB
xenMTrd+lQZLOp/ie9LcEo/c7lViKBorxU/w6h5Quv+9XqXBkrSSJSf+eh+E6VUaKyUuaZ2d1Lck
5W79Ko2V2IkE7uA+jff2r4oIUBJxlzCmAIGdyZev0PiGN4mb6E3iJloKuf+QjBxz4m1fJSpB8ZP2
3aMUpPj5t32XyLT9KS3nPST3jJJO7/MqkYle/ZQW1shf53m91ZvETBjrvug1YmST+rjt2zRcGi0W
9CgVTpR6v7dpwGRfycBS3EiK5v1epiFjxDu0ILhL/st7vExstfvRiNQUBZ0kQge7Jmdz27dp1JS0
hUe7+5Cs5fu9zIMGf+zvUiRIjd7nZRo0RehkvWhRnOb5PV4WgSCuJhtPDjUluOXlnzp+Rg1ueJk4
jF4mU3T/4P7+Dl5+jzeJWtsUEJIjREmuB6ybb/sq0QrrvuTHk4sq5vq2rxKlSIZpPZNSmWJ0b/Um
MRaNlN4kb5FSmPdlUfO2r9JQdykpv0fuzA75jO/zKg11j9yoA5Lhew9Ygm/7Jo2UHNwdyh6SJ3X7
PombaKRkNMkI0fKXLCHc9k0aqNjMA1ogfK83aZzsRpFHTsmM93mTx0m8R3HZp/dZn9/qTWH8HUrQ
UoR/oPbn+Ve3yJESF9Gbu/QmLfvee3ifafv8q9u9SRQijYw+aTGK8tHv8SqRiCZ0nxQbMeF7oUsk
ogiUfHFasROmv9WbxEQ0UEoGf0oqiewlL9bd9k0aKMJeyn0D4/fqlAZKeTiK7hFJvlenNFAKAEgl
0Dh3mf9u9SZxEQ2UlCDZV1qjlrjstm/SQEktUKaH/HhZz7ztmzRO4nlKRtzf2ZEs8G3fpHGSLtr9
lKIcSlvfnheE6cmN3ycz8SnpFGNoXn31mlrc8DZxEr1NK2c793cpktznwd72TSITMf4OLdwePNiR
nONtXyU60Wgpg3ef4gdahnmPV0EoktSHYtFv0au+SrxEQwWh7u2RTaVI9Na90qs01gfk2NL6Ca2H
vlenNFRiCcqP7pMx55G+/u4t8hk0UkKXIrtPH977lLQvq6TbvknYUi6OYiSaGg0nb/sqoUvkpbiB
cgu0OPA+r8rMUCi5Tz6iBA63epWIxEPdx2IzWajbE4nepKGSf0fStvuAVrhZ5G77Kg2VcnGUyqB0
pZiZ275JIyVf+OFD4j8KCW/9Jok5DZQWAQ/IJFKi/n1epHHuj/fuUaj+8OG993qThknuEYULO7ua
DL7tmzRMkheybJQL3iP397avik4i3iUP5z6lzNnJOfn2LZZsiIvoTdIq3NltXyHakK889EbsDaIJ
hULv8wbR4tMxi8Ot3iAmoYHsHoyZO277Cg3kYMxTdNs3aCDkNr/PGzQQWm689RvEBjSQ9yEWvUHj
IHF+nzdoHJTOfJ83aBy0bHXrN4QnyZXAG2dvbpFbo0mnN4gX9w4or0AJNibzbV8lEnB6YO/h7s79
Tylgfo93iRjULa3GUbhLzv77vEpUoVcp5CR5J1f/1q8SY9BgSdh3ael7776I7G3fpLFS2AdLvk/v
vterNFTiX/JE7+3uSU75tm/SSD8l60gO0z5FChveDN8kzqGBkiiTjXpASwW3py69SQOlVN4DCkse
UGT9Pm/SOMnfOSBnlGKh93qTxkkxAsVB1K+sjd3qTeF3croJyP2DB5LvPPn2c/r2hjeJi+hNUvzk
cu/cJ9oy7972VSLR9i4pGHIoCWOxN7d9l4i0zREG+fr31Zjf9l0iE6G8v7tDy2oSG9/qTWIkGiz5
k+SFUkhkFrlu9yaNlUScslo0WArh3udVGiq9StlzcgVoYeR9XqWR7lJgQ9H8waf392/fK/ESDZX8
FnptZ5cihfd5k4Z6f0ypO1qJuS8yc9s3aaS05s7BMQnc+7xJA6UgYZcc7p1dWSW41ZvC+cQMlJT9
dHdvRxacnpze4lXiI7xK/iT56ruUZjG5s9u+TWTapskhg/Hg3s49k7a77dtEKloW2aPx2vTsbV8F
rUjqKCGFdaD36phYCoOmSfp0h4QA0vN+b9OgSeKhFYmxyHt/v7dp0OQR792/T7mehyZ4ve3LNGyy
z/vITVH+7j3eJfaiQRNLk+lB+PpeWNPLNOZ9wnqP/HFa1aFG7/MyDZkU831KkmIpihq9z8s05F1K
a1Ay5ME+ZZve42URDNJ1Dx7S+iRlh6kRXj57dYuXicfo5QOM+eF9CmVZB9z2TaIWuRbE1/u0bCe2
+pZvEqnIIjykrAh5M6LsbvsqEYpcygefEntQ+HLrN4mraKAkxLs7FBbe2ycpfo9XaaR7u+T17eyQ
mpS1qNu+iqHSzN47oAVriPB7vEpDJQncI73xkEJE1ne3epXYicb6cLy/RyknCvFYyd72TRrqQwqC
Sejvk/l7nzdppAdYkqHkBHmb7/MmBkqLE7Sw8SlNze3JK6xPjPTp/QekIyX0/s7LW8QNxEj0JpkE
Wm3bp/B7j2l021eJRtsPyUu9v0srG5I+v+2rRCTS6MT5lD8/kIz0bV8lKpE6p1UnsmCSrLrVm8RI
MtQdWvx5sEdS8z6v0lDJFyI1/MCs/dz2TRopkXfn04cPyAvjGOC2b9JAaU5JnT2ghWOWmMib/TeJ
kWiglPU8oGl5SBnT93iRhknuOAIdEjTm+tu+ycMkN56UL6UR3+dNHiYWP2n5VMKOW70pTE+k3acV
jV1awMSbL37qFuJCPIQ336c90YU5/QFSye/1JtOF8o0PSE+TNXyPN5ku5JBStpLijVu/SYxDoyPz
/fBTSu/vic9/2zdpnJTV2d/Z36X0KC3MvserNFASyz2aCGIfyeHd9lUa6T2KqSjS3b1/nyX6Vm8S
79BISaDJet6nJab3eZEGSpp291NSW7SE/T5v0jhpERjZi13SI+/zJg2Tlinv0UIjrWPc/k1hdFqy
obic2EFihc+fvKJvf+Lzp5veJCaiN0nlESvQqo16GLd9lUi0vUe51b0HtPj36R6T97bvEpEQ695D
puYBkfh93iUykazt7d6jLPv+LmvMW71KjITRUlbqHqWzKIPxXq/SaPf2ifPJQyFn4faDpVdpsAfk
2dD6yaf7Etbf9k0aKrkKJGr3KLt0+zeJl2ikRN/7n1ISgzIRt2cJepVGSks2tPhHoT1z4W1fpHES
K5EKJG44uD2F6E0aJyFLZuwB/cdK8FZvCueTUrm3S+RSv+bpi9+Lvr3hTWIjfhORF3Jh7/UmEYiy
jAeUaXlImTvmotu+SiTioJ5eM87JbV8lGpH+PNglRiCHiiXmNq+CjWioZGNoJW2XFip5Ym77Jg0V
XjlpMvJWxcjc9lUaKuUZSUBJ9VKk9h5v0kjJl6KkCQXj4jXe6k3iIxoo5VM/pXz1w4e3R5ZepHHS
Ggr5qQcHlL14nzdpmKR6D/ZpLffBzu2HSW/SMEkDkie1++mOSOit3hSmpwh6j5YoKeJhTjh9jcY3
vElMRG/SGiUyk5RBY9Le9k2iEDnk5I9T5k31wm1fJRIR5+5TqEQZO1Gdt32VaET4PqSlbgr4b/0i
8RANlLT1/i5lRtQi3vZNGiiFv/eJshRiPWBeuO2rNNAHxEUU6RPzMv/d9k0a531apXx4j1T9ewyU
uIgGukcRN2nAXfLK+28Ov0kDxbTco9QTMm7v0SeNk96Eqt6nD25PInqVBookHy3tkBu4c3veFa6n
V8nFOdgllxdvHr/6nL694U3iIrxJwSQ8q/ukBt/nVRBp9+H4PsU89IFYtdu+CzKREYYKpKiHmfC2
r4JMJG3E9Q8/JZ1961eJlWiwRGAKYO9ROMlzc9s3aax7FBY+pFTmHtn+93mVhkp2Yp9WTGhqKAn6
Hq/SUA9I298j/UBB8K3fJF6ikd4fPyTbREIja0O3fZNG+mBMhN25/5Bc3/d5kwZ6f3zvgPClv28/
L/QmjZNU2Q55nmSfbs8Mwvj05n2all1SLvzmV2h8w5vERrsfjcjdIH+VfLJdsfy3fZVItL17j5yy
/X0KZCRKvO27RCTiXoq4HrCcv8+rRKXt+9Tr3g4tl8ic3uZN4iOiEnV6Dyt+93Ye3n6s9CqNFSqf
bDglER/ensL0Kg2VPA6KnMj+Kw/e8k0aKSXv97CMRebt1m8SJ9FID4h7KV+0d//93qSBfjq+T2b4
PoXwtycRvUnjpFQKKfx7JHC3n1F6k8ZJLIi87gPSSrd+0/A9vUf5Isqd483f68tbRAXERfwmWbYH
5KmIb3/bN4lCJDE0KZTgoLTw+7xKJIJVpGz7LiUOeF5u+yrRiF4laaG0AQXTt36V2IiGSkkDcsk+
jb84+CKNlPxzWkW5jxjxfbqkgSJ9RPxOQe3tx0lv0jhJLxAnUIqDmehWLxIT0SjvUbb9/s4DWKf3
eZOGuUf5DYqBaVre600aJvVJKSfig/vv9SYNc3d8bx/LgaTrb/2mYXmgukv8x3zw4jZvEgvRm5QN
3HtACwsaE9z2TaIQqVx6k1iX4pj3eZVIRNkjcsnI5aXF/Pd5lWhEr4L17tHSC8vorV4lLqKhkrRQ
CpKmZv8+uwy3fZXGCgV4D5GBuFW3fZOGSnaJpoUM0z6Ly23fpJHujynN9ZA87Ye3x5b4iAZKQQw8
IzDE+7xJ4yRbuLv/6QFlj5gDb/smjRPx7ENaXSV1/z5v0jiJFyihRwZN4olbvSlcTzqX3rtP08Jv
nr2+ReKTuIjepJiAnDjylA8OmLi3fZVItA2flSLEA8pXMTPc9l0iEmWA7j8kfnhAOb33eZWoRK8+
+JTW3UnM2ard6lViJBksOdgURR+I83nbV2mwD5BIvE9LeKIdbvsmDfVTcs8/JV1Gnb7PmzRSypLt
EBeRgrj9QImTaKAPyaZR1ESreMy9t32TxkkRF7lGJOM0O+/zKg2UxJQIRAtGkpm77Zs00Pvs2e9T
Cuj2AxXGpwiGAvD7exKWnr7GGze8SFxEL5Kz++kDLMlC3X/08qOBt2NvE5W2aZ3+Ib1N9onSpu/3
OlGKE/AkdDS996jV+7xN1KK3Sac9JImn9P17vE1MJQPf33tIqzLEz9Tqfd6mge+SHJCfTytCD/eo
1fu8TeMm72WHPB82se/3Mg2bloX27t+7d3+XnKb3eJk4jEZNscL9TynFTd4wNXqfl2nQtOZBnE0J
w4P37ZnH/JDyNZSNJVF8v5dpzORB7e3t39vdI2/mPV4W2SAvk6Zpl5L7hsmend1i5ZeYjF4mD4Ei
jn3SXKxBbvsmUYv0M4WuUCA0Ue/xKtGKYoedB5QX29UF+du+SpQib2j3U1LOFO+wHbvVq8RYNFTy
hshgUwS6IyGz/+qmV2msD4it7pEfdV+Wl277Jg2Vkkz3iCkotfBefdJIaS2XUCX3QpzNW71J3EQD
RZ97lE2DznqPN2mc5F+AjT7dkUzGbd+kcZI/fvAAifb77/UmjZOmZZ9kFknv28+ocD69SrSllCPp
SLz64vlT+vqGV4mP5NW9+/sHBzuE9vu8SkTapsVG8o1JWNWXv+WrRCVyTCiu279HTs17vQoykU9D
CTUKIe7dfqzESTLWTw/uU86GdDK1goa47ds0XPL+yABSjEYWn1q9z9s04gek2SjLT9HLPjV6n5dp
zLRMQNqF1q+Jkd/jZeIrGjWF7cRU9ylleoue6UX3Mg2aPBXyPffJfL4XxehlGjNF0g85H/jePdOY
yRRQxPaA3O33elkEgvQqrcwd3N8xRvvbX91CmIjB6F3ydHaJq8mEsQTf9k0iFjKguw/I+lH49l7v
Eq3ITaGkDIWL+8Sb7/EqUYokYo/yVrQ+sseK41avEmPRWMmKkE/2KY1XEg+3fZUGS57Rpw8owUJ0
Zl/0tq/SWCmgofU5CqUkkXTbN2moD8b3iR/uU8e375PYiUZKS8QkAg/ufSorK7d9kwZKrhQZkR2S
/dtPDL1J46TlJ8oc0J8HtycuvUnjhKIiz49WOG7fp3A+zSjpCsqH78nC4Fevj+nrG14lPqJXyS3Y
IzNAzjKT6LZvEonIElC2glKKu5L5uu2rRCO4QOR20Xrk3nv1SkSiod7fpcWRh7Sgc+tXiY9oqCDS
LjIsLKa3fZFGSon/fXJh7u+Tp/g+r9JIyTFGLh0K8X3epIHeJ3khe7dDhuvWbxIf0TjvgY8+pSyf
uKa3fRMDpTwdRfF7+7JkcNs3aZykP/f3KL9CGZb3eZPGSQxIDsEDypHcfpzC9VBjFGkhSYI3j796
Td/e8CYxEb9JaxQPDsjXY3G57ZtEIcqCUmRH+v6BTMttXyUSEdOTjaBVoD3Jkdz2VaIR+TC7+/u0
aHXvPjP9rV4lNqKh7hHT05orLQvyxETfjLxJQ6UkyR75tPT6AQvMbV+loe6PdyhY2Cd8OdVx2zdp
pLSg/ZDS2jsPJda41ZvERzRQch3I5aGlRFGBt32TBkpvkud+QMmK93mRhkl5mYekEijeuP2s0Js0
TAo1KECheFtE9FZvCtOTKqLnAdGXzf7rn7yFkSAmojeJ/+7t0+L7fVpoe59XiUJIgt6n5D8pBpHS
275LRCKF/WCHFpbvkcPxPq8Slcg9IgtMI6Vs8a1fJT6iwYJMCJVJPbzPmzTWXZJwMqakBt+rTxop
LfqT6afsi+Thb/smDZREjZzPfVq8Ek661avESTTQh+SQ0SLS3j3JCNz2TRroAYk3PPV7eyykt32T
BkrZdNILFB3Imsxt38RAqU8SUNJjtyeR8D2ZCerugMRNx/l70bc3vElcRG8SAz7cIWagFLMS93av
EomQhKRUCyWYKQn5Hq8SjahXSivtk6/yXm8SjUjZk4H5lJSDBOe3epX4iIZKyn6f7D5Cr/d4kQZK
Hg4C+ofkQr7XqzRQ0p4k3fd3KSx4nzdpoKQEKcdK2WGx37d6k9iIxkl97pFzTubp9jNKb2KglG5B
lpMWZd7nTRoneY+UtLu/S7b0fd6kcRInkPuHuPb22ArTU587ZA8pFuc+P391Qt/e8CYxEb1J7Eer
/eDbT01edPjt8G2i0jYpQSyH3ycv2yQfbvs6kWqb9BkWHWixg0KD93ud6EW5VVoovke5KQpt3+Nt
4ioZOQksaRky6dTqfd6mke/uj7FAQ4vVe+81cHqbBk5idO+ADACZyfd9m8Z9QL4PWSxasDEJjFu9
TExGw0aoS/keovn7vkyjplQTJWoP7j8gy/V+L9OgyUhTSEXhOaUh3+9lGjORmxZ0d3YpvKdGt37Z
iAd0Fvxiwydfnp48PaUmN7xOfEavE6fcpxV+ij2Iyd/rZSIZ0j7UOxkW0n7v9zbRjLI3D/Z3yXCr
ebj9y0QzevlTMoUPaG2LldgtXyYmw6AhmhT8kBJkrfvl6cnT271Mgya1QCqQROshLem9V8805vtj
ysxRBE76/v3epSF/SoaNMir75Du8x7vEYjRiSoXep7CL1mzfZ5bpXRowhSO79/dpSU7irtu/S+Pd
I5eZMuXkEb4Pf9C7NN7dMaXJkI2U1f9bvmukYpdSt58+fCjuw+fbD+jbG94kxqI3iTcoZ0DhNNnI
93mVCIUIgZxQspD7D3h6b/suEQr8TAt4D4it3udNIhOFNOQS3n/4gEKwW79KHCVjJdkju0y+wPu8
SmMlyafcKQkvxbjv8yoNlZalkfjCktj7vElDJUVJ0QH5Ap/eHl3iJRop6ed9SnUQxkPYRt+kgZI1
+/T+3qcHtBD3Pn3SOCk1Q/rlgDz99+qTxkl+/sEe+b4PxVm61ZuG7ymEIqbfFW3++fbeDn19w6vE
R/QqLIGow1u/ReShBOh9Sp0qbW75HhGHeiOHWTTKrd8j0hDL7lG26z1GR6xDo7s3pgXj9+mOXqPh
kTNDLqT4ybd+j4ZHq0f37olZu/VrNLr98QFZ4vfojfiFBkcaeo9i5/d6jQZHubiHknu+9Vs0NFqV
Ju56j6HRazQ0Cq4ekCG4/WvC0HvkG4kiPvnyOX1xw0vEJPQSZRIOdomOtG546zfpTSLJ9u6nlFDF
ysWeJHFv2ysRhgLlnft798mLlEjltq8ScUj775M4HHxKrtitXyWWobGSltmlhah7n356+7HSmzTW
XcKXNPFDWrLj2bztqzRU0v6fIqp/SFbnfV6loZKNpPWSvXvkNd6eSsRCNNSD8Q6x3s7ugThet30T
Q6WkAEXn9ynWfq9XaagPxuTq0XrUPTGRt30TIx0/pCiKMvPiod7qTcP05LiQcqSZxZunx7sP6esb
XiVOolfJG6CokXzqXRnprd8lKm1T4LcH15YC7vd6l8hEHEx5qYcHezvv+S4RilKc5PrQMgYZ2tu/
S+yE8ZKt3N2hvOqB2K5bv0vj5eGSonhIbDF6n3dpvBT4UJBLzojw4q1fpeGSkr+3T/EtzdHtXyWW
otFS+LCLlDl1+16v0mD3SSceUCLkPjPjrd+koZIJJG9t9x4FAe/1Kg2VlNM+rYySG8RUev7mK/r2
hjeJJ2ikcGofUlLsgRjR275JAwU3kekFfZkTb/sqjZS8p08fINARf+a2b9JAd+hNUk1E4lu/SMwg
46TIDIlZ1oa3fZPG+XCMXMU9WkrjabntmzRMck5onDt7hPH7vEnDpBD0gBbhdh5+entsiRNonHBR
aP2DFphu3ye9SeMkx4FsI60XSp7ztm/SOHexFvHp/sGteMi9SeMkrULLuJQOItVy605Fd5MChjJ6
QFaKFdLr41u8SkyEV8kD371/j6Rb/P7bvkpEonQh8R5lkeif93mVqESruTsk3g/vqRa87asgE4Vy
lGsjlf8eYyVOwlgpfU1v3rtP63jv8yqNlSwNzQxl+ig7+R5v0lDZtyOdcl802W3fpJF+SrZ8n9ZW
H+qazW3eJFaigZK/QxlkUqD3dMXxlq/SQCmDTF7A/QNKQb/PmzTQB5S5frhHPsvOe6HLA91/+JBy
NhSb3fpNYXxysQ4ov09EYuI++fwVfXvDm8RG9Cbx/b0DypySI8psdNtXiUTbD8Z7O2RgaCmE2ei2
rxKNiHlpaYqyaZTDGP3Gya1fJSIR85JCogXSg32emVu9SoxEY6W0CS3fmQWU275II304prVnWici
FnyfN2mglBWnsAIBAjPgbd+kcRIbUWLpPhnT208psRENk0zMwaef7pCnYlZebvsyDZVsxcN7OyTh
JODv9zKNFgT+FGsA5Du/38s0YOLhfYou7pNn9h7vOv7fJ5t6nyycvPzqy1sID3EUv/wprTuScZR4
/LZvErFIAmihhdLgD0WR3vZVIhU0PxEZyUPmqdu+SoQiK0dhwqf3DsihvPWrxFQ0VJJ28vQf0kqL
maDbvkyjJW26T5SilSnyu9/vbRrwAZmrPbKu5KdRo/BlarfhZRryfVJS5A4cYB3yPV4mxqJB79N6
3EPKDJCqeb93aczkz+7sPaC3H75vxzRkBOpkoskdeS960cs0ZApySClTJEkrDu/xspGHg4cPKarb
J5aWl0+/2nvw+9P3t0j+EJcRCOIyShrRCigtb4HL3g8AkQ4LVPfukbdK/MKa+v0gEP1I1ZNZImVC
Vvj9ARANGQCtI9Na0/sCIL4DESi2oMQZ5S4eslf4fgCICBTm7pD3TFIjafP3A0A02CczsAflJjHK
+71PJCDJ2aER7JGv957vExsSBSjqJe6nxb57X+N9IgDCfNI4FOdvmAF6Ifo+jZ8CYAq0aDWJjNl7
90/jx0IWOfVkud+X/iJJpDA//ZTWDMkXxPsnr87o2xveJObjN6FoHzzcIQF+jzeJZmRWPqW0F/lV
pCvf41UiF8UiDx7ScMm3Z6m97atEKfLJ9mkRgjLuMlW3epWYjIYKMdl9QG7kjtjB275KYyU/m9jz
4S7F4DxBt32VxkqJRfI1iEbvMTX0Jg31IQX+B7SqSunMW79J7EQj/ZTYmYJ+Emhmx9u+SQMlQSZF
QEuFD1mQbvsmjZOG+Sn5VZQYvz2F6E0aJ9mQnQdk7EkH3vpNYXviBcprH3z6QFyMb7/6Sfr2hjeJ
jehN4gXSloTt/qdMohtf/WhvB68SibZpeZ9W3miBQzLxt32VaLRNfETLhOQ9isjc9lUiEnli90lQ
aUVUwvdbvUp8RGMlR4xiNXI8iRvEzN72ZRrt3g71e29vh9YdjJG+7ds0YForebBPTEz+4z61ep+3
acwkOp/SssUDYsn3epvYiob9YEwGlRafibOo0fu8TMM+oMQQ2Nn5+bd9mUZ9nzzQe5TrpiVhavQ+
L9OgyRkgb4xWXcgZeI+XRSCIYBQ4wpbuUaOPfuPk/wGQTpkxt6IAAA==
L9OQd8d7+/cf7N17+Ol7zTS9TGPeGR/s7T/89N7+wXsR7B6PeXtnvPvg/sH+/Z1PDcVI0KjFDW8T
k9Hbe+O9B/f2H47e4y0i1fbe7vjh3u7u3o3vee8Rlbb3xwf3DnYevM9rRJ/d8Q7xxO2RJE7iod3b
2dnZfZ+3aGh7JHWf7t3/9H1eo5EdjB8e7L/HwOgtGtj++P7+Dk36bd8idqGB3Rt/em+P1Nl7vEUD
27033t0Be77HazSw++P7Ozuf3n+ft3hgn9779OD2fQkrfzreOzjAO2++go244R3iDXpnf3zvwe4+
yd5DZsbbvkkE2T4YHzx4+ODg/qf3ed5u+6oQhZTjvXsPHvIs3PZNIsz98af7Dx/uHdzb4Vm/1ZvE
KzTQB+NPd0gvPny4c3sS0Zs0UGJpMkP7Dx98+untB0qv0kAfjnf39z7d2dn/lOXotm/SQB+O98hs
7u/cvz22xDo8zns7uwcP773POOlNGuenJIMPPn24d//g9sjSmzTMe+MHB3v39u49EEa/7Zs0zHvj
e2QkD/b2yMre9k1h9j3ivvv3Htx/+ID7PHsKF+mGN4mH5M2H5Bfs78p03vZNotD2/fG9h/sHu6SD
mf1u+yqRaPveeP/+7oODTx/svterRCNyZPb29qnPB+QQ3PZV4iIaKhm3+w/I97q/84Dn9Lav0lgf
jHfv7e/dI//r9lSiN2moxA0P6d1PyW97nzdppPvjB7sHn+4/eLBz+zklPqKBksIkP4/+f/th0os0
TJpREs/7+5/u35629CYN89PxzsEBqbAHIi63fZOGeX+8e//TXdJG+zcO074pTL8/JjfnAZHrAQv3
8588pm9v6JOYiN4kf3aPpIwQFnt521eJRKToH5IT/unBw4c8obd9lWhErEsE2tv7dG+f5fu2rxKR
COGdvf3d+zQ5tx8rsRGNdWf8cOfhwQEM7/u8SUPdJfd5n9zQnR2xabd9lYb6gDzBh+S1P3w/dGmk
n4539x6SGSU9eOs3iZFooCSjO+TY7e/sMgve9k0a6D600Q75vKKNbvsmjZOc7Yfk81Kg8F590jgp
QDi4f4+02IPb01bYntTYPhlSYqRd9olOfgrB7Q2vEhvh1d3x/oN7B7sPSd+/z6tEI0SvBzvE+Pff
q1OiEdmJPbIu+w9JrbzPq0QkcvEpBLu391Ccxtu9SnwkQ31I6pPssEzqbV+loZKfAua9t0uB33u8
SUO9R7HXzsMdUkqsy277Jo2UDOLuvXtkJz5lJ/BWbxIj0UBJ2R/skQlWR+W2b9I44W48vP/w4YEw
723fpHHu0ps7Ow+o0/fClsZJ8eXeDg2a+OHWb1q2J1khM3HvwT28+uTVLSSG2EheJUduh7wVcQNv
+yrRCGxPxungPvkq79UtUYm8HMpc3HtI6SJmwdu+SmQijPdoash1kNDsVq8SJ9Fg98YPiXVpqGJP
b/smjXWXgtz9g51793bFVNz2VRrq/pico/ukzyRMu+2bNFLSg7sHD3aJg2+eGvMmsRINlHTZ/qcP
yS0TXXarPulNGigJG6Wi9vYfPLz9lNKbPE6IDLkrD28/TnqTxnkPOR1KYz14j3EK45MHuUvKnpIy
ePHll8/pyxteJCbiF0kTkVJRb+62bxKBtsl+04tkiCl99R6vEoVI735K0daDTw8o2fcerxKJyD/a
PYDOvn/AivdWrxIX0VBJXihNd/8+OSssL7d9lcZKXL/74N7O/XsPJaC47as0VkSXMOEH+2zDb/sm
DfUhucvkzlEMc3t0iY9opKSPiPmIGWhu3+dVGumnlL0mf26f/LL3eZMGCk/7PukyktT3eZMGSqnf
T6FPKGt96zeF7YmRKBqGWdtjKf3i9Pemr294lRgJr5Ii290llU3BE7VCFvOL09+bWtz8NpFpe4+Q
fvgpqV+K26jZ+7xOtKIAgShMvh2FfNTqfd4mepHN+PThAekXYmdqdeu3ia1k4BRI7e6z3L7f2zRw
ysNSfp94ZIc06vu9TeOmOI5k9wH5pe9Fc3qZhr1LfvSn5DXtUGL2PV4mHqNRi8WjIdss/21fpkGT
Xibevk8m6L1mi16mMUMuDh4gV/te5KaXaczk25Jn8HCfyEaNbv2ySMc9yqTufXp/79NdQ+2T41uE
3MRk9DJpy3u790m4dkVx3fZVItc2qUsyuKRoKQJ+n3eJWvDmSeGR3yhR4W1fJVqRWOzs7dCixv59
tiq3epVYiwZLY71HIvHp7r3b40tv0lhpqBQr0RyRNXufV2molLshg7JHK0/v1SmNlGLRexSHkmFh
hXmrN4mfaKC0XrVPBCILyNbztm/SQClAozzepyYHeNs3aZzU5wEFWKQp36tPGidZ3ftYW9vdu/04
hfVJV1Aug14mcac3z17fIktKXERvkitPaZ+HCNnf502iEDlEpF4+JYdcWPe2rxKJSNju08Q8oJUB
ptFtXyUaUdhCi4g7u/Tyrd8kLqKRkkrc3f30gGLR93qTRrr7KZldis8oSHufN2mglBgDZfcPxHu7
7Zs0TvJpHu4/pPW1HTJbQ6/+/iffDl4lNqKB0qs7D0ilEHlv3yu9SiOlNRBa6dyh0ON9XqSB3qdo
kmaUZPu9uqSB7o0/ffDgAaWaaAn/tm8K0++TYSdfnniesT359i3iZmIiepOYfv8hBWcPVLhv+SYR
iJiepmTv4UNSR+/zKpGInDfKP+9S/k/V2C1fJRrRq+Rmwg3RTNNtXiU+4qHSqjk5EaTw3+dNGiqx
0f59StE/gDF+j1dpqLR8R17qA/IixEjc8k0aKWXbSV9TCvo9yEt8RAMlv4NCgL0ditDe500aKNwd
ijyIDd+rTxonhdvghl1SKu/zJo2TJpTsKE2ppL1v9aZwPZID++TnEH3x5rPbBM3ERfQm+RpYv3tA
eown9LavEom2dw+I7x8cHBzsS6B023eJSOSnPIA9vEfLL+/zKlGJXqUFdRJwcq9u/SoxEg2WpoaM
C60x7d9+rPQmjXUXdg1Ghgb7Pq/SUImT7n+KBTFJ1N/2TRopuTi0qEWJaGakW71IjETjpOzL3g5J
2s4+M+9t36Rx0rQ83KcEgTqtt32ThklRKKV0H9CK4+0JRG/SMCmKJPOyR+mM278pbA9zSCtF5HAw
J3x5evKUvr7hVeIiepW8DVqn/HQXocV7vUtE2qYFHzJNSDyK0Nz6ZaITsS/5VrQ8QNr3vd4lSlHY
uvvwAbEvyeumd8N3iZlowDTefVoFJO3yXm/ScHcp1KZ1vAPysN5jtPQujZY8ZlIs5O7IEsytX6XB
3ieEKdtJ5vw9eiWOorFSqn//4N59ilaZi2/9Kg2Wcv33Hn4KMX8PtqBXaax7lDYiz+7epzvvMVZ6
lcZKwnOfZpayl6z5b/eqyAClEWkZhaROIqiXr9D4hjeJm+hN4qb7lDjfO6BY831eJSptkz58QJri
4X0Svvd5l8i0TSuXlBomcb/PUnvbV4lM9Oqn5H6QzRBP4FavEjdhsBRpUk6PpOfB7XulV2mwNFaa
G/I+9iTBdttXaazkuezuU1aPUp/v8yYN9VNaPaeHFiduPzfESzTS3b0x5eMo77n7qUm23PZtGuwD
Wl+jkPzT3b1PqdH7vEzD3R/Tkiml5O6TG/J+L9OISfIoWED6/r1eFikgViazTutHByZH9FPHz6jB
DS8TW9HLZIBojXmfMgks87d9k6i1TVHgp7RuQOEcs8ZtXyVaYbX34c4uZSBlGf62rxKlSHApItsh
z4CtwK3eJK6ikdKbe5/u33tI8nf7TulVGuourfZSxpPMFrHVe7xKQ90jXXFAWd57oslv+yaNlBy2
HdJQD+6J1brVm8RNNFJKSZHYkbY4uP3E0Js0UIoDKb1Ky2yiY277Jo2TVrUfUMRK5L09H9GbPE7i
PYrsyTu99ZvC+LT+SbNC5JWo9flXyIze8CZxEb1JhoPWc/ZoIYnN1W3fJAqRGt77dIeiZVKo7/Mq
kYgGep/e2ycmfJ83iUTksFHISlrtgWjhW71KXEQjJdW/S+mWezvM87d9kQZKiU1aIkAQKOs5t32V
BrpPq+GUe9ulnNT7vEkDRbrlwYNPKb5hzX+rN4mLaJzkpz2k1QHSDbenEL1JAyVpoWwqpavFJN/2
TRoneaSU7SNsJQS87Zs0TtJFJCsINkRcbvWqMD2lPu4/IAtHfrBR96++ek0tbnibOInepiXQHQqX
iSWY8W/7JpGJGJ9SCZT+IKf0fV4lOmG0nz6g5BKtF7/PqyDULlkKkrUDCtRv9yq9SrxEQyWhIZO6
Q9Zc1uFv1Su9SmN9MKY0LnESic77vElDJTVIfsv+fZXx19+9RRqDRkro7pACvfcA2DIz3fZNwpZS
cA/3KJYjU/FenRK6RN7d/QOKqcQk3vZNmZhdWtHaB9K3fpVoxCOlTMR9uDrv8R6Nk/JvpD1pcYci
yPd5lcZ5n1b+D8iFxQLee7xJ4yT/d3//gPoln/C2b5KM0zDvIxtLOYzb90jv0TCRwNinkIjU5/u8
SaPcGx/QyhnZNEma3PZNGiXJCi0nEyPsHtyeEUQfkYBSNHRAepCl7OTbt1ijIRaiN0mjMJ63fYVo
c2/M6G16w3+DaEJu+fu8QbT4dHz7gRCP0EAoF8i8fNtXaCAHY57c275BAyGX+X3eoIEQI936DWID
Gsj7EIveoHGQLL/PGzQOymC+zxs0DlqnuvUbwpO0how3zt7cIptGk05vEC/uEQ9TVkvyAbd9lUiA
fACtuNzbo/UTJsZt3yVioFvKIjy4RxnL93mVqEKv7tzbpYXVPQnqb/UqMQYNlrxfWlKlcHWfddpt
36SxEqfvUA5id+eeqLXbvkpDpWCRHADymiWcue2bNFIyb5QwoTTc/m3elDeJc2igtEy5T/G8rjXe
pkd6j4Z5nxYjiB3uPxT7f9s3aZQUne4cUAqCiPQ+b9IoaVIoc0GO873bT6dwO7mhe7To9ynxH948
+fZz+vaGN4mH6E2o/V1krWWV/LZvEoW2d0m70OoxJSlFYG77LtFomwz5PkWZlIB7yAx423eJSoQx
0WiHolRx6m71KrERDZYMFY30U0oX3r5TepMGSwKOZSLq9j3wpVdprPTqvXvkr39KNH6fV2moFIAR
hQ5ocerB7YdKvERDpTXZA1piv79HIcJ7vElDReL5U5JRUkrv8yaNlNbYydvepyz7+7xI46TogJIr
GOd7cKFwPnHDA+S6KRxncXty+vwjarL5VWIkefUh6Wys2fBIn5zeold6lWi0TTNDC3i0zkl+7Pu8
S1SiFZDdg0/Jzkiy47ZvgkwUVVNuhvQSU+lWbxIj0VBJZOg90kvvQSR6k0a6S+xAGc1disff500a
Jzncuw8p/0us9D5v0jg/pcVGWkrYoxXSW79JjETjJIf7wT5lqyk6pkYIjG/7Mg2VArcdSnJTbPG+
L9Nod8dkLCg0oXwSNXqfl2nAlBaiFCyt9ZCxeY+XRQLIapCokgdu3j17dYt3iaPoXbKrFL0hUwNC
3/ZFohVZjYe0IEbaiRXTbd8EoShqvIfJvac2+ZavEpnIb3y49ylkdjO6/pvEUTRO6pRWtUjz71GC
/Lad0qs00j2iL/X56afEyO/zqgyVFq8pf/ZQorHbvkpDJdu6R2sJRCMJAW/1KjETjfXhmGJHileN
f3bLN2moB8QNDynoFCV82xdpoNQlCTo5aMS/7/Emxknqm1ylTynPfXtshe8paffgAWEr+dDvvLxF
aEBsRC9S3oJyX5R2I4fyfV4lCm0/HD+g3A7l3iTPcttXiUakvGkRgEIEyha+z6tEJDJU9/DqA1ou
ufWrxEcY6874/kNa9iMWfJ83aagH490HO/dJ2CQ/fts3aaR7pIR3Keu2Jwnn275JAyU9SMn8+7Q8
w/JyqzeJj2icZODopd370KC/8e3fpHGSOqKEM/25f3ts6U0aJ2VhdyjeIwZkrr/tmzROSsFS0LZL
63W3H6dh+n1S9MQI4jy8+KlbiAsxEb1JfX66RwuEFDG+z5tEIWL6g909WmCUFaHbvkkUIk64/4AS
5JSWfJ83iULECTQp5BEy893qRWIhGuY+uSvkAdBSyfu8SKMkN/0BSfXePlnx93mVhklCRr49JQCI
vu/zKo2THPx7u5BO4b5bvUk8RAPFfN7/lIIKWT+97Zs0UpKVA0oyY+H2fd6kgVJejMSa0r2iTW77
Jo+T+iNJoXHf+k3D8Ts0Iw93WVI+f/KKvrvhvR1+D1p+9yGBoYTo+7xKBNomm0/p3l1KwEpi5bbv
EolIWEgrkCv4QKzobV8lGm0fEJFoaeYexYq3fpXYCIOl1Arp6k93Pv30vV6lwe7dJwu8e3CfPNDb
I0yv0lhpqEQg5JGYkW77Jg31IS3VkY/y4EAcz1u9SYxEIyWfiibm/qfqL9z2TRoouffk7FJGR+zv
bd+kcRILkqzxos77vEnjJCGlVQPy4d6Dj4TtYfLvHxzQ38y9T1/8XvTtDW8SG9GbpI2IzPTX3vu9
SiQiNqLFexIa8o7e51WiEYkb2SRaqJMl1Nu+STQifB/skFOEab31q8RGNFSywA+xEr9HQZf35k1v
0kjBRrQESqHEe71JAyVJe/CA5ub+Dlvg275JA6VgCzxP6N6+T2IjGicl/sltJBt8/73epHFSroEi
kHvEvCzct32Txknu9acPPyVfV4Lw275J4yTWpSGSH7dz+zeF6Sl1evCQbRp7VKev0fiGN4mJ6E3y
4ih/fu8ecdL7vEkUIr/8/sN9Sp7uS1bltq8SiYhzySkiI0w0fp9XiUaEL8Va5DDIjN7qTeIiGik5
OLRER4l0UUa3fZNGSjkrypnu3aN81+2JRK/SSGkJ9D7lBCnb+15v0kDJuuzsUOz8UPKQt3qT2IgG
SszwYO8hOawS7d/2zb2PRjQvZEWRyaGcyHu8SgOlV8m9+ZRyvWINb/sqjRR5vU8fUID3qSj7W70q
fE+vPjjYo6Xih/vMSMevPqevb3iVGAmv3idPmRY4yHF4nzeJHbZ3H1LWdYcSxPvizt32XZCJ4vZ7
OzSx5Di8z6sg0z5RmLxl8jhYgd7qVeIlGus9UiykWfb3hcC3fZPGukcccfDpvYc7Eojc9k0aKRkK
4gdSZvffg0j0Ko0U6RR6aHXu9m8SK9FA79ObeI080Pd5kwb6gJKu9yiI3nl4+3mhN2mgZGPIyaYF
UPblbvsiDZNUGXITB6rKbvWmsD29SYmUgz3y7vnNr9D4hjeJiehNEpjdA0ry7uzdZwkPXx1+lSi0
vXuPom9i+f2HkiC77btEI+LdPegGEnLh3Vu+SlQiOaWcJy0/EXfc+lXiIwyWJGaH0rS0EiSTestX
abDEvbRoukN8fyCK5Zav0lj3Kb6kZUx6WP3e9k0a6qeELi3HUCx9+5ESK9FID8YHsOCU07v9rNKb
NFDyymhBen+Pckfv8yaNk5z7+1izonTg+7xJ46SYlnKspMh2b88MhvEppfzgIQUGTKHf68tbhAXE
RvwmpVkf0Br6Pcb2tm8ShWiNjVgByRgRmdu+SiQio0j5jT2K9SRhettXiUb06oPdA8rSUl7v1q8S
G9FQ4YDS8tp7vkgj/ZRyZLQsR9r+9tSlN2mgtL5G00KWSVTZbd+kcZJiINceiuHWLxIT0SjvjUlV
79AyuDjLt32ThrlHSxqUYqVpea83aZjU54Pd+8QHEn3f9k0aJhlDUrr75G7c/k3D8kCVHMiHPJ0v
bvMmsRC/ibXA+2TTuM/bvkkUguIkTtg7oIWU93mVSEThLK+gUEaZFedtXyUa0au03rNL3QoX3epV
4iIaKkkLsR7pE1qSe59Xaaz3x+RSUXKXXI73eZOGSobpIS1RUaj3Xm/SSO9T0LUPT1sTibd5k/iI
BkommHI4tGgvauy2b9I490krUJi3S/o6/uZHL08ib9I4SZ8QYUkRvQcz0Js0TqQZKGt0QFbt1m8K
11PEdUBBAS2TMhedvb5F2pO4iN4kD4f6JAN8IAx421eJRNtk9SnDS9qeQor3eZeIRBkgMqGU9/xU
uOG2rxKV6FXkKMhxFTLd6lViJBksJcHv79+ngPh9XqXBPqBVtYf3H9BU3R5fepOGSrmuHVpfIIFj
833bN2mkn44Pdg7IU1Ff+VZvEifRQB8S995/uLtPS9Hv8yaNkwIuSnBQpEez8z6v0kDJkyMCUZQn
WZXbvkkDhYCTr/yA1jxv/aYw/n3ysUmp7O/IQE9f45Ub3iQ2ojdJadNKHpl9ns/bvkgUQtb+0wf7
D+9/uicSftt3iUSUtafl9gNKN5g0Bd65+VWiEb1Kb+4e7DyUmOBWrxIfYaikBmmh/v4e5Yip1Ufv
8TaNl8J3cltJo2Fd7v3ephHT1O5Rzwf3aDnw/V6mMZPokGtGxpwytu/xMjEVjZqkZ5fYmFylB9To
fV6mQT8Y72EhiczVe6FNL/OY75HXc7BPc/x+79KQafVgh/iSDPNDanTrl0UayK+ktMXuQwqwqRFe
fnZ2iyVfYjB6mZKMlKcmBhOuvu2bRCzSyCQQB7SUJZ7wbV8lUlG0QBqDEnD7IsC3fZUoRf4P2Y8D
4k2Jbm71KvEVDZVepaTdzg6lq9/rVRorseQuObR4Ru/xJg2V/Ghy3ff3xKG47Ys0UHqRFpQo5ScW
+lZvEjPROMkBf0hZJRIfdn9u+yYNk5yYA+KkA1pqeZ83aZhE2ntkPGC43udNGid5pbsU3ZD/Lg7t
rV4Vxiej9XB/j6INYnx688Xzp/TtDW8SF9Gb1ClZu4fwEt/nTSIRLaOSC005fSLx+7xKNCKFvEdL
AaTcZOnstq+CSHBhKLamgJVn5lavEh/JUEmxkMGjOJlaQT3c9m0aLjl7n1IO7d4BJT+o1fu8TSOm
xD6tkJOnSCto7/cyjZmUMQIOhADU6NYvE1fRqD8dk8gd0LBpqfH9XqZB36ekDTk0oDY16r686WUa
M80U+eSUPyRP6v1epjGT6NJk0ZoECeB7vCziQEr1Aa1JkJ9h0P72V7eQJeIwepnCyh1aGKPX2Rm7
7ZtELaQ8dx/sUOhOSwzv8y4Ri9wxCmRJmZNUvM+rRCoSiT1ap/qU1ktv/SYxFg2V7ADJ0qeUZyA7
8B6EordpuLsPxvfJbO1S7t24Vbd9mwZMARAt1xOViczv9zIN+QExNTEmJTneC29iLRo15e5pbYN8
ZtLQ7/cyDZrECa465ZeNV3Xbl2nMFGtSGol8ZwqF3u9lGjP5J6RrSZgpDfoeL4tE0ExTiEve0R5F
yPL2V6+PqcUNbxOH0dtk0/bIiJJ/xOravHnTm0SubeIRcsk+pYljT+O2rxKxCOV9WqamTILw9W1f
JVLRqzS5JE/iyt3qTWIsHillfWkZ6t4eC/9t36SRkn0gq7/3YG+P5uc9XqWRUvrs3n2KLx5IUv22
b9JA75Mc7O5QwEkMdds3iZtooPco+iMnBU7g+7yJgY4PHpAxgTl6nzdpnKRtKIm1e29Xljpu+yaN
kyz3fUppUiB0ewYUxkefpOMekCnBm8dfvaZvb3iTmIjf3KWojVb4KIx5jzeJQhQPYBmfPFaKGN/j
VSIRdPkuyTiZL56X275KNCL35h4JDA1XRO1WrxIb0VDJXtJS0P2HtBL6Pm/SUCldQgJz/x4tltx+
qNQpDZXyDwe7D3fuU+b4fd6kkVJ8SZ7U/n2yeLd+k/iIBkqJAPLC6FWzEnq7N2mgJC97n5I/Q/H0
+7xJ47w/fkBh+Kdk21ml3PZNGicFIbv798lCks6+7ZvC9fTmAwojyPfiN1//5C0MBXERvUkBKa12
kJmiFNj7vEokQj6U0iQ794l9eUpv+y4RiTT2AVzrB++HMBGJHKd90ijk4e7fHmHiIxorcQMtQtGq
BYvabV+kke4SB9JC3X1KjrzHizRM4gWySySkYkhv+yYN8yHniyk38R6jJC6iUe5TmEb/Ub8sLbd9
k4ZJ6RTKqj8kdmAN2Htz4E0aJ8VKlPYiUXuPcdKbNM4dsqKUnX5ADHjrN4XnSV/v0joJ+Vba5+9F
397wJrEQvUnMRyv3ZCIeUnT2Hq8SiSgrSbERUYi8hvd5lWhEve58ukeGnxjpfV4lIpGqp0D23n0K
oFmn3OpV4iMaK6n6faIYGTblo9u9SUPd3SOrRis7FN28z5s0Ukrg7NJi0qe06Ps+b9JASRk92H24
e49ChVu/SXxE4yQ7uk/pXtJEt+cGehPjpKjmwafIQqp03+5NGiepv08p1kZE8x4v0jBpOqH77tM6
1K3fFKbfIeEmr4hMEw/z81cn9O0NbxIP0ZvEfp9SavkA2ohaISK47dtEpO3dT8c79+5RbH/PxBO3
fZsItU1uA2XU75G/8r5vE7Uopvj0PvnoFFWYTMyt3iaWwrj3KJgh34yCGRO13vZtGvcu+eq7lDUl
B+S9MKe3adxkIB9SAEYrd+9Hc3qbxk0LILTETukYWiV6j5eJxWjYD8YPSNvQmMmbfb+XadS0lHD/
HsUotHJIjd7nZRr0PnJIpFnvU7zwfi/TmMkEPaRUIbzL93hXZIOITeurFJNRflJe/vL05OkpNbnh
dWIzep0E5D45UWRTxKDc/mWiGFJBJJlkBB9KUHf7t4lkFAV8isQKZa5Z197+ZSIZvUwqgVwFyuu+
x8vEYxg0BRHkqtJSGfHJxpc/f/n70IvuZRo06QRaHn5AGpvShe/1Mo35PtGL2JtCNTakt3+XhkxJ
GXLNKS3D6vOWrxKD0YApDUXmm9bnJO12+3dpvOR5Hnx6b4fcsvchNL1LwyXLhgCcfMj3IRW9S8Ml
zU2iSFG0hGu3fNcIxR6txpPalzX1z7cf0Lc3vEl8RW8Sa1BahKw4aa/3eZUIBX3/kOI1So2Q4nuP
d4lQxM6UoaQ0B2nO93mV6ARV/5BSjGTnbt8rcRQNllZFKL9Bga3I0G3fpLGS4JM3ePCQciTv1SkN
lSLiXUrJ0CLO7QlMb9JIycffp+B9l1K5t36TeIkGCu2MhOTDvfd6kwa6N96nOJGCcBac275Iw6SU
zMPdHVql3b89C9KbNEzydnYe7oBEt37RcP3+p/dIFdP6o7y5t0Nf3/AqMRG9SmZgn1xepc4t3yPy
0PIYMbvE/Ld+j6hD/ZGISfbn1u8RbUg8YTCU527zHrEOjQ8MQJnh93qNhkfRwD6FHzr3t3yPhnef
9M+O2JZbv0ajIwZ/QL787V8jhuHBkbck2ZNbv0aD2yNNR1LxXq/R2EhTkZfzHmOj12hslKyh9Nt7
TLjw9N744OG+RMknXz6nb254i9iE3oJ9pxwW2Q4m5m3fJKLA5aeV0x0yWsLVJ7d8lyhDYfIOLd/T
6gUT9ba9EnFI/e9/SoaDYl3m0Vu9SjxDQ6WUEhkN8idkSm77Jg11l9BF6oPSb7enEr1KIyX1T8aZ
UmDkB73PqzRUig4oC/EpTQ4rm1u9SRxEIz0g6pLqp0wCJydv+yZGSkE9uXoUVnx6e06iV2mkD8b3
iLiUQPv09uSlNzFQCnd3DijP8x4DNTxP0dN9wnifZfr5m6/o2xveJD7iN+9TILFD9vy93iQSUYKR
LDKx7n1x8277KpGITAeNmTTJ+71JJMJi36ef7j6495BJdKs3iYt4oJ8+fPiAyfQ+b9JAyUnbIfLc
o4m53ZvyJo2T0po7O+R/U9bufd6kcZLnfkDeN+VpmHNv9SYxEY3zPkWGnyIqFRG97Zs0TmIiyntQ
6vf2pKUXaZjkuJAaukep9dsTiN6kYZJtJq/5AQXgt+9TOB4+7ANKRpGDxhR6fXyLV4mH8Cp5EsQL
tKqjub5bvkokohWsfQpcP6XlOh7qbV8lIm3fH9OaGSV/d3ZZ19/2VaISSRq9RWv6tOh761eJjzBW
MqIkL2Qm9m+PML1KY6WMDqWSdu9THvd93qShPoA7+ik53iLet32TRkpv7lFQQo7l7SeVOIkGSkYC
ORw47LfvlF6lgZJ8P9x/QLlqEvH3eZVHSgxIFu2+Ls/Imze+SSOllMB9knCyT7cfqXA+OZjkhVEK
V1Zun3z+ir694U3iI3qTGJ/WgyiFQcz0Pq8SjSi3SPHXffo/i8xt3yQSEfPu7JMx3N0RGt32VaIR
fJz9e2SeaHZu/SoxEg2Vgk0KpnaJmVhibvsmjfQheUdkv/f3RX/e9k0aKXkbFNxS2kMC8tu+SQOl
JCQ9ZPzFCN/qTWIjGud9CooODnZ2d4R1b/smjZPUEbnX4IXb05bepHGSJ/fpAVl9ilfe500aJ8UP
O7uf0pKk2OBbvWmYnjLL90jr0uqr5CxffXkLiSE+kpfvH+zTMqrI2m3fJCKRf76/t/eQ/EeZ09u9
ylQifU+JfOQtRJfd9lUiE8kp5bF2CGXRZbd6lTiJhkpprN17lAogoXmfN2mo5PFSzu7B7o4uttz2
VRrqA8rs7DzcJ1eHGeK2b9JIyfv8lDxI8rRv3yexEg10nxifetx9uGOy57d9mcZKCSUSUyyVvBc3
0cs0WnJ7SYuSSiIX4P1epgGT1JHY0MoUxezv8bIIAa3kkqdES9bkocnLp1/tPfj96ftbBMfEWgQC
/gAtlmPtkLny/QAQ6ZDAv7dHq6z792i55b0hEP1Iq9OaMsXZtIT5/gCIhgBAKOxT/MDG6D0AENeB
COx2ko9MocT7AyAi0GrXPqnd+wfk9kYA3ACAaLBPrvb+PUJBFP77IUAkuE/B/wFpfnGb3+N14kIi
wH1awSF3ktbm37d7ep/GTyLwcP/hHpnXr9E/DZ/EnuL5+6TQ2Sl9v/dp+OTAk6P3KVHjfTnICtL+
A1popew53j95dUbf3vAm8R6/CaeYHL5PeeS3fZNoRh4U6dc9Qvk+y81tXyVyUdRB7sGDA3JJ3utV
ohR5XxS60oIKpcBv/SrxGA2VpGT/ISWKdh9oMu2Wr9JYyaOGm7lHDPY+b9JQyQqR6btP2XPxbm/7
Kg2VOiW19oCSGQe3f5X4iYZKUd19Wpt4oLHkbd+kkVKQRP4MKSP/vZveo3GSJ/SAUqIHFO68z5s0
zH1yK+6Tc0vPrd8UrqcoEnxAYQ6b6W+/+kn69oY3iYvoTXJIyBV5SBrv/V4lAiE7unuPlD0Zeha2
275LRNqmoI6SWmQtSVe+z7tEJhgJotLe/f0DpvCtXiVGotHeGxMfkIH4dJ+54bZv0mD3yP3af0ix
A+WD3+dVGusuYgCK6h7QeN/nVRoqUenBferzgJyDW79KzERDpU7Jpz7YPaC8oXgUt32ZRnswpsiD
hkqZWWr0Pi/TeGlZe4dcCDIC7/syjZhcEHLBPiWZNc7frV4WMSCeIiqTpqD8LL/8/wAslEedsp8A
AA==
headers:
Access-Control-Allow-Headers:
- Content-Type
@@ -207,13 +205,13 @@ interactions:
Cache-Control:
- private
Content-Disposition:
- attachment;filename=DP_LIVE_10112023143408467.csv
- attachment;filename=DP_LIVE_26012024223439918.csv
Content-Encoding:
- gzip
Content-Type:
- text/csv
Date:
- Fri, 10 Nov 2023 13:34:08 GMT
- Fri, 26 Jan 2024 21:34:39 GMT
Transfer-Encoding:
- chunked
Vary:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,13 @@ import datetime
import pytest
from openbb_core.app.service.user_service import UserService
from openbb_oecd.models.composite_leading_indicator import OECDCLIFetcher
from openbb_oecd.models.gdp_forecast import OECDGdpForecastFetcher
from openbb_oecd.models.gdp_nominal import OECDGdpNominalFetcher
from openbb_oecd.models.gdp_real import OECDGdpRealFetcher
from openbb_oecd.models.long_term_interest_rate import OECDLTIRFetcher
from openbb_oecd.models.short_term_interest_rate import OECDSTIRFetcher
from openbb_oecd.models.unemployment import OECDUnemploymentFetcher
test_credentials = UserService().default_user_settings.credentials.model_dump(
mode="json"
@@ -54,3 +58,51 @@ def test_oecd_forecast_gdp_fetcher(credentials=test_credentials):
fetcher = OECDGdpForecastFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_oecd_unemployment_fetcher(credentials=test_credentials):
params = {
"start_date": datetime.date(2023, 1, 1),
"end_date": datetime.date(2023, 6, 6),
}
fetcher = OECDUnemploymentFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_oecdcli_fetcher(credentials=test_credentials):
params = {
"start_date": datetime.date(2023, 1, 1),
"end_date": datetime.date(2023, 6, 6),
}
fetcher = OECDCLIFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_oecdstir_fetcher(credentials=test_credentials):
params = {
"start_date": datetime.date(2023, 1, 1),
"end_date": datetime.date(2023, 6, 6),
}
fetcher = OECDSTIRFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_oecdltir_fetcher(credentials=test_credentials):
params = {
"start_date": datetime.date(2023, 1, 1),
"end_date": datetime.date(2023, 6, 6),
}
fetcher = OECDLTIRFetcher()
result = fetcher.test(params, credentials)
assert result is None