[BugFix] Fix Some Bugs Before Release (#7101)

* fix some bugs before release

* no-else
This commit is contained in:
Danglewood
2025-04-28 03:19:51 -07:00
committed by GitHub
parent 50f2e06a8e
commit 383363a4e3
7 changed files with 114 additions and 118 deletions

View File

@@ -13,7 +13,7 @@ from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
from pydantic import Field, PositiveFloat, field_validator
from pydantic import Field, field_validator
class CryptoHistoricalQueryParams(QueryParams):
@@ -47,12 +47,14 @@ class CryptoHistoricalData(Data):
date: Union[dateType, datetime] = Field(
description=DATA_DESCRIPTIONS.get("date", "")
)
open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", ""))
high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", ""))
low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", ""))
close: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("close", ""))
volume: float = Field(description=DATA_DESCRIPTIONS.get("volume", ""))
vwap: Optional[PositiveFloat] = Field(
open: float = Field(description=DATA_DESCRIPTIONS.get("open", ""))
high: float = Field(description=DATA_DESCRIPTIONS.get("high", ""))
low: float = Field(description=DATA_DESCRIPTIONS.get("low", ""))
close: float = Field(description=DATA_DESCRIPTIONS.get("close", ""))
volume: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("volume", "")
)
vwap: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("vwap", "")
)

View File

@@ -79,7 +79,7 @@ def get_engle_granger_two_step_cointegration_test(
short_run_ols = sm.OLS(dependent_series.diff().iloc[1:], (z.shift().iloc[1:]))
short_run_ols_fit = short_run_ols.fit()
alpha = short_run_ols_fit.params[0]
alpha = short_run_ols_fit.params.iloc[0]
# NOTE: The p-value returned by the adfuller function assumes we do not estimate z
# first, but test stationarity of an unestimated series directly. This assumption

View File

@@ -2108,7 +2108,7 @@ def test_equity_fundamental_management_discussion_analysis(params, obb):
@pytest.mark.integration
def test_equity_calendar_events(params, obb):
"""Test the equity calendar events endpoint."""
result = obb.equity.calendar_events(**params)
result = obb.equity.calendar.events(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0

View File

@@ -2,10 +2,8 @@
# pylint: disable=unused-argument
import asyncio
from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
from warnings import warn
from typing import Any, Literal, Optional
from dateutil.relativedelta import relativedelta
from openbb_core.provider.abstract.fetcher import Fetcher
@@ -17,12 +15,6 @@ from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import (
amake_request,
get_querystring,
)
from openbb_fmp.utils.helpers import get_interval
from pydantic import Field
@@ -69,13 +61,13 @@ class FMPCryptoHistoricalData(CryptoHistoricalData):
class FMPCryptoHistoricalFetcher(
Fetcher[
FMPCryptoHistoricalQueryParams,
List[FMPCryptoHistoricalData],
list[FMPCryptoHistoricalData],
]
):
"""Transform the query, extract and transform the data from the FMP endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> FMPCryptoHistoricalQueryParams:
def transform_query(params: dict[str, Any]) -> FMPCryptoHistoricalQueryParams:
"""Transform the query params. Start and end dates are set to 1 year interval."""
transformed_params = params
@@ -91,10 +83,20 @@ class FMPCryptoHistoricalFetcher(
@staticmethod
async def aextract_data(
query: FMPCryptoHistoricalQueryParams,
credentials: Optional[Dict[str, str]],
credentials: Optional[dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
) -> list[dict]:
"""Return the raw data from the FMP endpoint."""
# pylint: disable=import-outside-toplevel
import asyncio # noqa
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import (
amake_request,
get_querystring,
)
from openbb_fmp.utils.helpers import get_interval, response_callback
from warnings import warn
api_key = credentials.get("fmp_api_key") if credentials else ""
interval = get_interval(query.interval)
@@ -111,17 +113,16 @@ class FMPCryptoHistoricalFetcher(
symbols = query.symbol.split(",")
results = []
messages = []
results: list = []
messages: list = []
async def get_one(symbol):
"""Get data for one symbol."""
url = get_url_params(symbol)
data: list = []
data = []
response = await amake_request(url, **kwargs)
response = await amake_request(url, response_callback=response_callback)
if isinstance(response, dict) and response.get("Error Message"):
message = f"Error fetching data for {symbol}: {response.get('Error Message', '')}"
@@ -161,13 +162,13 @@ class FMPCryptoHistoricalFetcher(
@staticmethod
def transform_data(
query: FMPCryptoHistoricalQueryParams, data: List[Dict], **kwargs: Any
) -> List[FMPCryptoHistoricalData]:
query: FMPCryptoHistoricalQueryParams, data: list[dict], **kwargs: Any
) -> list[FMPCryptoHistoricalData]:
"""Return the transformed data."""
# Get rid of duplicate fields.
to_pop = ["label", "changePercent", "unadjustedVolume"]
results: List[FMPCryptoHistoricalData] = []
results: list[FMPCryptoHistoricalData] = []
for d in sorted(
data,
@@ -179,8 +180,8 @@ class FMPCryptoHistoricalFetcher(
reverse=False,
):
_ = [d.pop(pop) for pop in to_pop if pop in d]
if d.get("unadjusted_volume") == d.get("volume"):
_ = d.pop("unadjusted_volume")
if d.get("unadjusted_volume") and d["adjusted_volume"] == d.get("volume"):
_ = d.pop("unadjusted_volume", None)
results.append(FMPCryptoHistoricalData.model_validate(d))
return results

View File

@@ -2,10 +2,8 @@
# pylint: disable=unused-argument
import asyncio
from datetime import date as dateType
from typing import Any, Dict, List, Optional
from warnings import warn
from typing import Any, Optional
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.provider.abstract.fetcher import Fetcher
@@ -13,9 +11,6 @@ from openbb_core.provider.standard_models.forward_pe_estimates import (
ForwardPeEstimatesData,
ForwardPeEstimatesQueryParams,
)
from openbb_core.provider.utils.errors import EmptyDataError, UnauthorizedError
from openbb_core.provider.utils.helpers import amake_request
from openbb_intrinio.utils.helpers import response_callback
from pydantic import Field
@@ -59,13 +54,13 @@ class IntrinioForwardPeEstimatesData(ForwardPeEstimatesData):
class IntrinioForwardPeEstimatesFetcher(
Fetcher[IntrinioForwardPeEstimatesQueryParams, List[IntrinioForwardPeEstimatesData]]
Fetcher[IntrinioForwardPeEstimatesQueryParams, list[IntrinioForwardPeEstimatesData]]
):
"""Intrinio Forward PE Estimates Fetcher."""
@staticmethod
def transform_query(
params: Dict[str, Any]
params: dict[str, Any],
) -> IntrinioForwardPeEstimatesQueryParams:
"""Transform the query params."""
return IntrinioForwardPeEstimatesQueryParams(**params)
@@ -73,14 +68,20 @@ class IntrinioForwardPeEstimatesFetcher(
@staticmethod
async def aextract_data(
query: IntrinioForwardPeEstimatesQueryParams,
credentials: Optional[Dict[str, str]],
credentials: Optional[dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
) -> list[dict]:
"""Return the raw data from the Intrinio endpoint."""
# pylint: disable=import-outside-toplevel
import asyncio # noqa
from openbb_core.provider.utils.errors import EmptyDataError, UnauthorizedError
from openbb_core.provider.utils.helpers import amake_request
from openbb_intrinio.utils.helpers import response_callback
api_key = credentials.get("intrinio_api_key") if credentials else ""
BASE_URL = "https://api-v2.intrinio.com/zacks/forward_pe"
symbols = query.symbol.split(",") if query.symbol else None
results: List[Dict] = []
results: list[dict] = []
async def get_one(symbol):
"""Get the data for one symbol."""
@@ -90,33 +91,51 @@ class IntrinioForwardPeEstimatesFetcher(
url, response_callback=response_callback, **kwargs
)
except Exception as e:
warn(f"Symbol Error: {symbol} --> {e}")
else:
if data:
results.append(data) # type: ignore
raise OpenBBError(e) from e
if data:
results.append(data) # type: ignore
if symbols:
await asyncio.gather(*[get_one(symbol) for symbol in symbols])
if not results:
raise EmptyDataError(
f"There were no results found for any of the given symbols. -> {symbols}"
try:
gather_results = await asyncio.gather(
*[get_one(symbol) for symbol in symbols], return_exceptions=True
)
return results
for result in gather_results:
if isinstance(result, UnauthorizedError):
raise result
if isinstance(result, OpenBBError):
raise result
if not results:
raise EmptyDataError(
f"There were no results found for any of the given symbols. -> {symbols}"
)
return results
except Exception as e:
raise OpenBBError(
f"Error in Intrinio request -> {e} -> {symbols}"
) from e
async def fetch_callback(response, session):
"""Use callback for pagination."""
data = await response.json()
error = data.get("error", None)
if error:
message = data.get("message", "")
if "api key" in message.lower():
if "api key" in message.lower() or "view this data" in error.lower():
raise UnauthorizedError(
f"Unauthorized Intrinio request -> {message}"
f"Unauthorized Intrinio request -> {message} -> {error}"
)
raise OpenBBError(f"Error: {error} -> {message}")
forward_pe = data.get("forward_pe")
if forward_pe and len(forward_pe) > 0: # type: ignore
results.extend(forward_pe) # type: ignore
return results
url = f"{BASE_URL}?page_size=10000&api_key={api_key}"
@@ -130,9 +149,9 @@ class IntrinioForwardPeEstimatesFetcher(
@staticmethod
def transform_data(
query: IntrinioForwardPeEstimatesQueryParams,
data: List[Dict],
data: list[dict],
**kwargs: Any,
) -> List[IntrinioForwardPeEstimatesData]:
) -> list[IntrinioForwardPeEstimatesData]:
"""Transform the raw data into the standard format."""
symbols = query.symbol.split(",") if query.symbol else []
if symbols:

View File

@@ -3,8 +3,7 @@
# pylint: disable=unused-argument
from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
from warnings import warn
from typing import Any, Literal, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.index_historical import (
@@ -95,13 +94,13 @@ class PolygonIndexHistoricalData(IndexHistoricalData):
class PolygonIndexHistoricalFetcher(
Fetcher[
PolygonIndexHistoricalQueryParams,
List[PolygonIndexHistoricalData],
list[PolygonIndexHistoricalData],
]
):
"""Polygon Index Historical Fetcher."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> PolygonIndexHistoricalQueryParams:
def transform_query(params: dict[str, Any]) -> PolygonIndexHistoricalQueryParams:
"""Transform the query params."""
# pylint: disable=import-outside-toplevel
from dateutil.relativedelta import relativedelta
@@ -119,18 +118,21 @@ class PolygonIndexHistoricalFetcher(
@staticmethod
async def aextract_data(
query: PolygonIndexHistoricalQueryParams,
credentials: Optional[Dict[str, str]],
credentials: Optional[dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
) -> list[dict]:
"""Extract raw data from the Polygon endpoint."""
# pylint: disable=import-outside-toplevel
from openbb_core.provider.utils.helpers import ( # noqa
from aiohttp import ClientResponseError # noqa
from openbb_core.provider.utils.errors import OpenBBError, UnauthorizedError
from openbb_core.provider.utils.helpers import (
ClientResponse,
ClientSession,
amake_requests,
safe_fromtimestamp,
)
from pytz import timezone # noqa
from pytz import timezone
from warnings import warn
api_key = credentials.get("polygon_api_key") if credentials else ""
@@ -146,9 +148,12 @@ class PolygonIndexHistoricalFetcher(
async def callback(
response: ClientResponse, session: ClientSession
) -> List[Dict]:
) -> list[dict]:
data = await response.json()
if isinstance(data, dict) and data.get("status") == "NOT_AUTHORIZED":
raise UnauthorizedError(response.get("message", str(response)))
symbol = response.url.parts[4]
next_url = data.get("next_url", None) # type: ignore[union-attr]
results: list = data.get("results", []) # type: ignore[union-attr]
@@ -178,14 +183,24 @@ class PolygonIndexHistoricalFetcher(
return results
return await amake_requests(urls, callback, **kwargs) # type: ignore
try:
return await amake_requests(urls, callback, raise_for_status=True) # type: ignore
except ClientResponseError as e:
if e.status == 403:
raise UnauthorizedError(
f"Unauthorized Polygon request -> {e.message} -> {e.request_info.url}"
) from e
raise OpenBBError(
f"Error in Polygon request -> {e} -> {e.request_info.url}"
) from e
@staticmethod
def transform_data(
query: PolygonIndexHistoricalQueryParams,
data: List[Dict],
data: list[dict],
**kwargs: Any,
) -> List[PolygonIndexHistoricalData]:
) -> list[PolygonIndexHistoricalData]:
"""Transform the data."""
if not data:
raise EmptyDataError()

View File

@@ -1,72 +1,31 @@
"""Polygon Helpers Module."""
import json
from io import StringIO
from typing import Any, Dict, List, Optional, Tuple, TypeVar, Union
from typing import Any, Optional, Tuple, Union
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.errors import EmptyDataError, UnauthorizedError
from openbb_core.provider.utils.helpers import (
ClientResponse,
ClientSession,
amake_request,
)
from pydantic import BaseModel
T = TypeVar("T", bound=BaseModel)
class BasicResponse:
"""Basic Response class."""
def __init__(self, response: StringIO):
"""Initialize the BasicResponse class."""
# Find a way to get the status code
self.status_code = 200
response.seek(0)
self.text = response.read()
def json(self) -> dict:
"""Return the response as a dictionary."""
return json.loads(self.text)
def request(url: str) -> BasicResponse:
"""Request function for PyScript.
Pass in Method and make sure to await.
Parameters
----------
url: str
URL to make request to
Return
------
response: BasicRequest
BasicRequest object with status_code and text attributes
"""
# pylint: disable=import-outside-toplevel
from pyodide.http import open_url # type: ignore
response = open_url(url)
return BasicResponse(response)
async def response_callback(
response: ClientResponse, _: ClientSession
) -> Union[dict, List[dict]]:
) -> Union[dict, list]:
"""Use callback for make_request."""
data: Dict = await response.json() # type: ignore
data = await response.json() # type: ignore
if response.status != 200:
message = data.get("error") or data.get("message")
raise OpenBBError(f"Error in Polygon request -> {message}")
keys_in_data = "results" in data or "tickers" in data
if isinstance(data, dict) and data.get("status") == "NOT_AUTHORIZED":
raise UnauthorizedError(response.get("message", str(response)))
if not keys_in_data or len(data) == 0:
raise EmptyDataError()
if not data:
raise EmptyDataError("No data was returned from the Polygon endpoint")
return data
@@ -78,7 +37,7 @@ async def get_data(url: str, **kwargs: Any) -> Union[list, dict]:
async def get_data_many(
url: str, sub_dict: Optional[str] = None, **kwargs: Any
) -> List[dict]:
) -> list[dict]:
"""Get data from Polygon endpoint and convert to list of schemas.
Parameters