From 2ca210b44e000daa834a7708dcc994d09328a0ac Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Thu, 22 Feb 2024 18:27:58 -0800 Subject: [PATCH 1/4] add obbject method for field descriptions --- .../core/integration/test_obbject.py | 12 +++- .../core/openbb_core/app/model/obbject.py | 26 ++++++++ .../core/tests/app/model/test_obbject.py | 63 +++++++++++++++++++ .../charting/openbb_charting/__init__.py | 2 +- .../core/plotly_ta/ta_class.py | 2 +- 5 files changed, 102 insertions(+), 3 deletions(-) diff --git a/openbb_platform/core/integration/test_obbject.py b/openbb_platform/core/integration/test_obbject.py index a76d5d644fba..7db3e4bc4769 100644 --- a/openbb_platform/core/integration/test_obbject.py +++ b/openbb_platform/core/integration/test_obbject.py @@ -75,7 +75,7 @@ def test_to_chart(obb): """Test obbject to chart.""" res = obb.equity.price.historical("AAPL", provider="fmp") - res.charting.to_chart() + res.charting.to_chart(render=False) assert isinstance(res.chart.fig, OpenBBFigure) @@ -89,3 +89,13 @@ def test_show(obb): stocks_data = obb.equity.price.historical("AAPL", provider="fmp", chart=True) assert isinstance(stocks_data.chart.fig, OpenBBFigure) assert stocks_data.chart.fig.show() is None + + +@pytest.mark.integration +def test_get_field_descriptions(obb): + """Test obbject get field descriptions.""" + + obb_data = obb.equity.profile("MSFT", provider="yfinance") + descriptions = obb_data.get_field_descriptions() + assert isinstance(descriptions, dict) + assert len(obb_data.to_df(index=None).columns) == len(descriptions) diff --git a/openbb_platform/core/openbb_core/app/model/obbject.py b/openbb_platform/core/openbb_core/app/model/obbject.py index 9f2a2da87463..a5625a9f4e79 100644 --- a/openbb_platform/core/openbb_core/app/model/obbject.py +++ b/openbb_platform/core/openbb_core/app/model/obbject.py @@ -112,6 +112,32 @@ def model_parametrized_name(cls, params: Any) -> str: """Return the model name with the parameters.""" return f"OBBject[{cls.results_type_repr(params)}]" + def get_field_descriptions(self) -> Dict[str, str]: + """ + Get a dictionary of the returned field keys with their descriptions. + + Fields automatically created by `alias_generator` will not have descriptions. + """ + descriptions = {} + model = None + if isinstance(self.results, list): + model = self.results[0].model_json_schema(by_alias=False).get("properties", None) # type: ignore + columns = self.to_df(index=None).columns.to_list() # type: ignore + if columns[0] == 0: + columns = self.to_df(index=None).iloc[:, 0].to_list() + else: + model = self.results.model_json_schema(by_alias=False).get("properties", None) # type: ignore + columns = list(self.results.model_dump(exclude_none=True).keys()) # type: ignore + if model is None: + raise OpenBBError( + "Could not extract model property definitions from OBBject." + ) + for i in columns: + if i in model: + descriptions[i] = model[i].get("description", None) + + return descriptions + def to_df( self, index: Optional[Union[str, None]] = "date", sort_by: Optional[str] = None ) -> pd.DataFrame: diff --git a/openbb_platform/core/tests/app/model/test_obbject.py b/openbb_platform/core/tests/app/model/test_obbject.py index ffe5b8368b2c..c49231ea8e6f 100644 --- a/openbb_platform/core/tests/app/model/test_obbject.py +++ b/openbb_platform/core/tests/app/model/test_obbject.py @@ -1,5 +1,6 @@ """Tests for the OBBject class.""" +from typing import Optional from unittest.mock import MagicMock import pandas as pd @@ -8,6 +9,7 @@ from openbb_core.app.utils import basemodel_to_df from openbb_core.provider.abstract.data import Data from pandas.testing import assert_frame_equal +from pydantic import Field def test_OBBject(): @@ -56,6 +58,22 @@ class MockDataFrame(Data): value: float +class MockDataModel(Data): + + name: str = Field(default=None, description="Common name of the company.") + cik: str = Field( + default=None, + description="Central Index Key assigned to the company.", + ) + cusip: str = Field(default=None, description="CUSIP identifier for the company.") + isin: str = Field( + default=None, description="International Securities Identification Number." + ) + lei: Optional[str] = Field( + default=None, description="Legal Entity Identifier assigned to the company." + ) + + @pytest.mark.parametrize( "results, expected_df", [ @@ -388,3 +406,48 @@ def test_show_chart_no_fig(): # Act and Assert with pytest.raises(OpenBBError, match="Chart not found."): mock_instance.show() + + +@pytest.mark.parametrize( + "results", + [ + # Test case 1: List of models. + ( + [ + MockDataModel( + name="Mock Company", + cik="0001234567", + cusip="5556789", + isin="US5556789", + lei=None, + ), + MockDataModel( + name="Mock Company 2", + cik="0001234568", + cusip="5556781", + isin="US5556788", + lei="1234567890", + ), + ] + ), + # Test case 2: Not a list. + MockDataModel( + name="Mock Company 3", + cik="0001234565", + cusip="5556783", + isin="US5556785", + lei="1234567891", + ), + ], +) +def test_get_field_descriptions(results): + """Test helper.""" + # Arrange + co = OBBject(results=results) + + # Act + descriptions = co.get_field_descriptions() + + # Assert + assert isinstance(descriptions, dict) + assert len(descriptions) == 5 diff --git a/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py b/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py index 68391476ac09..ebf29aa54f74 100644 --- a/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py +++ b/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py @@ -178,7 +178,7 @@ def to_chart( index = ( data.index.name if has_data and isinstance(data, (pd.DataFrame, pd.Series)) - else "" + else None ) data_as_df: pd.DataFrame = ( basemodel_to_df(convert_to_basemodel(data), index=index) diff --git a/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py b/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py index eba0a5994450..b27af21de2b3 100644 --- a/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py +++ b/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py @@ -181,7 +181,7 @@ def __plot__( if not isinstance(indicators, ChartIndicators): indicators = ChartIndicators.from_dict(indicators or dict(dict())) - + df_stock.index = pd.to_datetime(df_stock.index) self.indicators = indicators self.intraday = df_stock.index[-2].time() != df_stock.index[-1].time() self.df_stock = df_stock.sort_index(ascending=True) From 753b8cb4e22e41e21e97ac6c3dac7686b9f66e00 Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Thu, 22 Feb 2024 21:25:43 -0800 Subject: [PATCH 2/4] slightly different way --- .../charting/openbb_charting/core/plotly_ta/ta_class.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py b/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py index b27af21de2b3..eb1ec5463af9 100644 --- a/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py +++ b/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py @@ -181,7 +181,10 @@ def __plot__( if not isinstance(indicators, ChartIndicators): indicators = ChartIndicators.from_dict(indicators or dict(dict())) - df_stock.index = pd.to_datetime(df_stock.index) + df_stock.loc[:, "date"] = df_stock.index # type: ignore + df_stock["date"] = df_stock["date"].apply(pd.to_datetime) + df_stock.index = df_stock["date"] # type: ignore + df_stock.drop(columns=["date"], inplace=True) self.indicators = indicators self.intraday = df_stock.index[-2].time() != df_stock.index[-1].time() self.df_stock = df_stock.sort_index(ascending=True) From d4186e6626a6008b4dd7356e62302feab9ba5dec Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Fri, 23 Feb 2024 08:30:03 -0800 Subject: [PATCH 3/4] strip new line and white space --- openbb_platform/core/openbb_core/app/model/obbject.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openbb_platform/core/openbb_core/app/model/obbject.py b/openbb_platform/core/openbb_core/app/model/obbject.py index a5625a9f4e79..27e12c06720b 100644 --- a/openbb_platform/core/openbb_core/app/model/obbject.py +++ b/openbb_platform/core/openbb_core/app/model/obbject.py @@ -134,7 +134,12 @@ def get_field_descriptions(self) -> Dict[str, str]: ) for i in columns: if i in model: - descriptions[i] = model[i].get("description", None) + descriptions[i] = ( + str(model[i].get("description", None)) + .replace(" ", "") + .replace("\n", " ") + .strip() + ) return descriptions From 4750ac38b04c571673749fe3c67f502724add0fe Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Fri, 23 Feb 2024 08:58:46 -0800 Subject: [PATCH 4/4] undo charting bug fix --- openbb_platform/core/integration/test_obbject.py | 2 +- .../obbject_extensions/charting/openbb_charting/__init__.py | 2 +- .../charting/openbb_charting/core/plotly_ta/ta_class.py | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/openbb_platform/core/integration/test_obbject.py b/openbb_platform/core/integration/test_obbject.py index 7db3e4bc4769..1d9119d9c98f 100644 --- a/openbb_platform/core/integration/test_obbject.py +++ b/openbb_platform/core/integration/test_obbject.py @@ -75,7 +75,7 @@ def test_to_chart(obb): """Test obbject to chart.""" res = obb.equity.price.historical("AAPL", provider="fmp") - res.charting.to_chart(render=False) + res.charting.to_chart() assert isinstance(res.chart.fig, OpenBBFigure) diff --git a/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py b/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py index ebf29aa54f74..68391476ac09 100644 --- a/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py +++ b/openbb_platform/obbject_extensions/charting/openbb_charting/__init__.py @@ -178,7 +178,7 @@ def to_chart( index = ( data.index.name if has_data and isinstance(data, (pd.DataFrame, pd.Series)) - else None + else "" ) data_as_df: pd.DataFrame = ( basemodel_to_df(convert_to_basemodel(data), index=index) diff --git a/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py b/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py index eb1ec5463af9..eba0a5994450 100644 --- a/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py +++ b/openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly_ta/ta_class.py @@ -181,10 +181,7 @@ def __plot__( if not isinstance(indicators, ChartIndicators): indicators = ChartIndicators.from_dict(indicators or dict(dict())) - df_stock.loc[:, "date"] = df_stock.index # type: ignore - df_stock["date"] = df_stock["date"].apply(pd.to_datetime) - df_stock.index = df_stock["date"] # type: ignore - df_stock.drop(columns=["date"], inplace=True) + self.indicators = indicators self.intraday = df_stock.index[-2].time() != df_stock.index[-1].time() self.df_stock = df_stock.sort_index(ascending=True)