From 5723b0e46eae7d5d43a45b53285c8109a48807d5 Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Thu, 31 Aug 2023 19:59:22 +0200 Subject: [PATCH 1/9] Add init files to test folders to improve discoverability --- openbb_sdk/sdk/core/tests/api/dependency/__init__.py | 0 openbb_sdk/sdk/core/tests/api/router/__init__.py | 0 openbb_sdk/sdk/core/tests/app/__init__.py | 0 openbb_sdk/sdk/core/tests/app/logs/__init__.py | 0 openbb_sdk/sdk/core/tests/app/logs/formatters/__init__.py | 0 openbb_sdk/sdk/core/tests/app/logs/handlers/__init__.py | 0 openbb_sdk/sdk/core/tests/app/logs/utils/__init__.py | 0 openbb_sdk/sdk/core/tests/app/model/__init__.py | 0 openbb_sdk/sdk/core/tests/app/model/abstract/__init__.py | 0 openbb_sdk/sdk/core/tests/app/model/charts/__init__.py | 0 openbb_sdk/sdk/core/tests/app/model/hub/__init__.py | 0 openbb_sdk/sdk/core/tests/app/repository/base/__init__.py | 0 openbb_sdk/sdk/core/tests/app/repository/in_memory/__init__.py | 0 openbb_sdk/sdk/core/tests/app/results/__init__.py | 0 openbb_sdk/sdk/core/tests/app/service/__init__.py | 0 openbb_sdk/sdk/core/tests/app/static/__init__.py | 0 16 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 openbb_sdk/sdk/core/tests/api/dependency/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/api/router/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/logs/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/logs/formatters/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/logs/handlers/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/logs/utils/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/model/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/model/abstract/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/model/charts/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/model/hub/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/repository/base/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/repository/in_memory/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/results/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/service/__init__.py create mode 100644 openbb_sdk/sdk/core/tests/app/static/__init__.py diff --git a/openbb_sdk/sdk/core/tests/api/dependency/__init__.py b/openbb_sdk/sdk/core/tests/api/dependency/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/api/router/__init__.py b/openbb_sdk/sdk/core/tests/api/router/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/__init__.py b/openbb_sdk/sdk/core/tests/app/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/logs/__init__.py b/openbb_sdk/sdk/core/tests/app/logs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/logs/formatters/__init__.py b/openbb_sdk/sdk/core/tests/app/logs/formatters/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/logs/handlers/__init__.py b/openbb_sdk/sdk/core/tests/app/logs/handlers/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/logs/utils/__init__.py b/openbb_sdk/sdk/core/tests/app/logs/utils/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/model/__init__.py b/openbb_sdk/sdk/core/tests/app/model/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/model/abstract/__init__.py b/openbb_sdk/sdk/core/tests/app/model/abstract/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/model/charts/__init__.py b/openbb_sdk/sdk/core/tests/app/model/charts/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/model/hub/__init__.py b/openbb_sdk/sdk/core/tests/app/model/hub/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/base/__init__.py b/openbb_sdk/sdk/core/tests/app/repository/base/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/in_memory/__init__.py b/openbb_sdk/sdk/core/tests/app/repository/in_memory/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/results/__init__.py b/openbb_sdk/sdk/core/tests/app/results/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/service/__init__.py b/openbb_sdk/sdk/core/tests/app/service/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/static/__init__.py b/openbb_sdk/sdk/core/tests/app/static/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 06c984b04c6d195df0ee131a891297e34abc751c Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Thu, 31 Aug 2023 20:06:00 +0200 Subject: [PATCH 2/9] Help to_dataframe process more result types --- .../sdk/core/openbb_core/app/model/obbject.py | 69 +++++++++++-------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/obbject.py b/openbb_sdk/sdk/core/openbb_core/app/model/obbject.py index c5de65a8e5b..69f44838e11 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/obbject.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/obbject.py @@ -1,7 +1,8 @@ +"""The OBBject.""" from typing import Dict, Generic, List, Literal, Optional, TypeVar, Union import pandas as pd -from pydantic import Field +from pydantic import BaseModel, Field from pydantic.generics import GenericModel from openbb_core.app.charting_manager import ChartingManager @@ -42,45 +43,60 @@ class OBBject(GenericModel, Generic[T], Tagged): ) def __repr__(self) -> str: - return f"{self.__class__.__name__}\n\n" + "\n".join( - f"{k}: {v}" for k, v in self.dict().items() + """Human readable representation of the object.""" + return ( + self.__class__.__name__ + + "\n\n" + + "\n".join( + [ + f"{k}: {v}"[:83] + + ("..." if len(f"{k}: {v}") > len(f"{k}: {v}"[:83]) else "") + for k, v in self.dict().items() + ] + ) ) - def to_dataframe( - self, concat: bool = True - ) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: - """Converts results field to pandas dataframe. + def to_dataframe(self) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: + """Convert results field to pandas dataframe. - Parameters - ---------- - concat : bool, optional - If True, it concatenates the dataframes, by default True. + Supports converting creating pandas DataFrames from the following + serializable data formats: + + - List[Basemodel] + - List[Dict] + - List[List] + - List[str] + - List[int] + - List[float] + - Dict[str, Dict] + - Dict[str, List] Returns ------- Union[pd.DataFrame, Dict[str, pd.DataFrame]] Pandas dataframe or dictionary of dataframes. """ - if not self.results: + if self.results is None or self.results == []: raise OpenBBError("Results not found.") try: res = self.results - if isinstance(res, list): - if isinstance(res[0], dict): - for r in res: - dict_of_df = { - k: basemodel_to_df(v, "date") for k, v in r.items() - } - df = pd.concat(dict_of_df, axis=1) if concat else dict_of_df - else: - df = basemodel_to_df(res, "date") # type: ignore + df = None + + if isinstance(res, list) and all( + isinstance(item, BaseModel) for item in res + ): + df = basemodel_to_df(res, "date") else: - df = basemodel_to_df(res, "date") # type: ignore + df = pd.DataFrame(res) + + if df is None: + raise OpenBBError("Unsupported data format.") # Improve output so that all columns that are None are not returned df = df.dropna(axis=1, how="all") + df.sort_index(axis=1, inplace=True) except Exception as e: raise OpenBBError("Failed to convert results to DataFrame.") from e @@ -88,7 +104,7 @@ class OBBject(GenericModel, Generic[T], Tagged): return df def to_dict(self) -> Dict[str, List]: - """Converts results field to list of values. + """Convert results field to list of values. Returns ------- @@ -103,8 +119,8 @@ class OBBject(GenericModel, Generic[T], Tagged): return results def to_chart(self, **kwargs): - """ - Create or update the `Chart`. + """Create or update the `Chart`. + Note that the `chart` attribute is composed by: `content`, `format` and `fig`. Parameters @@ -126,8 +142,7 @@ class OBBject(GenericModel, Generic[T], Tagged): return self.chart.fig def show(self): - """Displays chart.""" - + """Display chart.""" if not self.chart or not self.chart.fig: raise OpenBBError("Chart not found.") self.chart.fig.show() From d1844e31c1a4e8a114b7ae94789e3ef080fff59e Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Thu, 31 Aug 2023 20:07:18 +0200 Subject: [PATCH 3/9] Update to_dataframe tests --- .../sdk/core/tests/app/model/test_obbject.py | 118 +++++++++++++++--- 1 file changed, 99 insertions(+), 19 deletions(-) diff --git a/openbb_sdk/sdk/core/tests/app/model/test_obbject.py b/openbb_sdk/sdk/core/tests/app/model/test_obbject.py index 39bcf516ad6..934d7ae2d70 100644 --- a/openbb_sdk/sdk/core/tests/app/model/test_obbject.py +++ b/openbb_sdk/sdk/core/tests/app/model/test_obbject.py @@ -1,3 +1,4 @@ +"""Tests for the OBBject class.""" from unittest.mock import MagicMock, patch import pandas as pd @@ -7,20 +8,23 @@ from openbb_provider.abstract.data import Data def test_OBBject(): + """Smoke test.""" co = OBBject() - assert isinstance(co, OBBject) + assert isinstance(co, OBBject) # noqa: S101 def test_fields(): + """Smoke test.""" fields = OBBject.__fields__.keys() - assert "results" in fields - assert "provider" in fields - assert "warnings" in fields - assert "chart" in fields + assert "results" in fields # noqa: S101 + assert "provider" in fields # noqa: S101 + assert "warnings" in fields # noqa: S101 + assert "chart" in fields # noqa: S101 def test_to_dataframe_no_results(): + """Test helper.""" co = OBBject() with pytest.raises(Exception): co.to_dataframe() @@ -33,8 +37,10 @@ def test_to_dataframe_no_results(): ( [{"date": "2023-07-30", "value": 10}, {"date": "2023-07-31", "value": 20}], pd.DataFrame( - {"value": [10, 20]}, - index=pd.to_datetime(["2023-07-30", "2023-07-31"]), + [ + {"date": "2023-07-30", "value": 10}, + {"date": "2023-07-31", "value": 20}, + ], ), ), # Test case 2: Normal results without "date" column @@ -42,27 +48,94 @@ def test_to_dataframe_no_results(): [{"value": 10}, {"value": 20}], pd.DataFrame({"value": [10, 20]}, index=pd.RangeIndex(start=0, stop=2)), ), - # Test case 3: Empty results + # Test case 3: List of Data + ( + [ + Data(x=0, y=2), + Data(x=1, y=3), + Data(x=2, y=0), + Data(x=3, y=1), + Data(x=4, y=6), + ], + pd.DataFrame({"x": [0, 1, 2, 3, 4], "y": [2, 3, 0, 1, 6]}), + ), + # Test case 4: List of dict + ( + [ + {"x": 1, "y": 2}, + {"x": 1, "y": 3}, + {"x": 2, "y": 0}, + {"x": 3, "y": 1}, + {"x": 4, "y": 6}, + ], + pd.DataFrame({"x": [1, 1, 2, 3, 4], "y": [2, 3, 0, 1, 6]}), + ), + # Test case 5: List of Lists + ( + [[0, 1], [1, 3], [2, 0], [3, 1], [4, 6]], + pd.DataFrame([[0, 1], [1, 3], [2, 0], [3, 1], [4, 6]]), + ), + # Test case 6: List of Tuples + ( + [(3, 2), (1, 3), (2, 0), (3, 1), (4, 6)], + pd.DataFrame([(3, 2), (1, 3), (2, 0), (3, 1), (4, 6)]), + ), + # Test case 7: List of Strings + ( + ["YOLO2", "YOLO3", "YOLO0", "YOLO1", "YOLO6"], + pd.DataFrame(["YOLO2", "YOLO3", "YOLO0", "YOLO1", "YOLO6"]), + ), + # Test case 7: List of Numbers + ( + [1, 0.42, 12321298, 129387129387192837, 0.000000123], + pd.DataFrame([1, 0.42, 12321298, 129387129387192837, 0.000000123]), + ), + # Test case 7: Dict of Dicts + ( + { + "0": {"x": 0, "y": 2}, + "1": {"x": 1, "y": 3}, + "2": {"x": 2, "y": 0}, + "3": {"x": 3, "y": 1}, + "4": {"x": 4, "y": 6}, + }, + pd.DataFrame( + { + "0": {"x": 0, "y": 2}, + "1": {"x": 1, "y": 3}, + "2": {"x": 2, "y": 0}, + "3": {"x": 3, "y": 1}, + "4": {"x": 4, "y": 6}, + }, + ), + ), + # Test case 8: Dict of Lists + ( + {"0": [0, 2], "1": [1, 3], "2": [2, 0], "3": [3, 1], "4": [4, 6]}, + pd.DataFrame( + {"0": [0, 2], "1": [1, 3], "2": [2, 0], "3": [3, 1], "4": [4, 6]} + ), + ), + # Test case 9: Empty results ([], OpenBBError("Results not found.")), - # Test case 4: Results as None, should raise OpenBBError + # Test case 10: Results as None, should raise OpenBBError (None, OpenBBError("Results not found.")), ], ) def test_to_dataframe(results, expected_df): + """Test helper.""" # Arrange - if results: - results = [Data(**d) for d in results] co = OBBject(results=results) # Act and Assert if isinstance(expected_df, pd.DataFrame): result = co.to_dataframe() - assert result.equals(expected_df) + assert result.equals(expected_df) # noqa: S101 else: with pytest.raises(expected_df.__class__) as exc_info: co.to_dataframe() - assert str(exc_info.value) == str(expected_df) + assert str(exc_info.value) == str(expected_df) # noqa: S101 @pytest.mark.parametrize( @@ -88,6 +161,7 @@ def test_to_dataframe(results, expected_df): ], ) def test_to_dict(results, expected): + """Test helper.""" # Arrange if results: results = [Data(**d) for d in results] @@ -98,17 +172,18 @@ def test_to_dict(results, expected): with pytest.raises(expected.__class__) as exc_info: co.to_dict() - assert str(exc_info.value) == str(expected) + assert str(exc_info.value) == str(expected) # noqa: S101 else: result = co.to_dict() result.pop("index", None) - assert result == expected + assert result == expected # noqa: S101 @patch("openbb_core.app.charting_manager.ChartingManager.to_chart") @patch("openbb_core.app.model.obbject.OBBject.to_dataframe") def test_to_chart_with_existing_chart(mock_to_dataframe, mock_to_chart): + """Test helper.""" # Arrange mock_instance = OBBject() mock_instance.chart = Chart(content={"existing_chart_data": "some_data"}) @@ -122,8 +197,8 @@ def test_to_chart_with_existing_chart(mock_to_dataframe, mock_to_chart): result = mock_instance.to_chart() # Assert - assert result == {"fig": "some_mock_fig"} - assert mock_instance.chart.content == {"content": "some_new_content"} + assert result == {"fig": "some_mock_fig"} # noqa: S101 + assert mock_instance.chart.content == {"content": "some_new_content"} # noqa: S101 mock_to_dataframe.assert_called_once() @@ -134,6 +209,8 @@ def test_to_chart_with_new_chart( mock_charting_manager, mock_to_dataframe, ): + """Test helper.""" + def get_mock_dataframe(): data = { "col1": [1, 2, 3], @@ -155,8 +232,8 @@ def test_to_chart_with_new_chart( result = mock_instance.to_chart() # Assert - assert result == {"fig": "some_mock_fig"} - assert mock_instance.chart.content == {"content": "some_new_content"} + assert result == {"fig": "some_mock_fig"} # noqa: S101 + assert mock_instance.chart.content == {"content": "some_new_content"} # noqa: S101 # Ensure self.to_dataframe() was called mock_to_dataframe.assert_called_once() @@ -166,6 +243,7 @@ def test_to_chart_with_new_chart( def test_show_chart_exists(): + """Test helper.""" mock_instance = OBBject() # Arrange mock_instance.chart = MagicMock(spec=Chart) @@ -180,6 +258,7 @@ def test_show_chart_exists(): def test_show_chart_no_chart(): + """Test helper.""" mock_instance = OBBject() # Act and Assert @@ -188,6 +267,7 @@ def test_show_chart_no_chart(): def test_show_chart_no_fig(): + """Test helper.""" mock_instance = OBBject() # Arrange mock_instance.chart = Chart() From beb0663fd6db87ea75e2b557cf9585bd9b94a300 Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Thu, 31 Aug 2023 20:07:45 +0200 Subject: [PATCH 4/9] Inherit pydocstyle from environment --- openbb_sdk/extensions/economy/openbb_economy/economy_router.py | 1 + pyproject.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/openbb_sdk/extensions/economy/openbb_economy/economy_router.py b/openbb_sdk/extensions/economy/openbb_economy/economy_router.py index 3656911e734..ed2fd60ffa9 100644 --- a/openbb_sdk/extensions/economy/openbb_economy/economy_router.py +++ b/openbb_sdk/extensions/economy/openbb_economy/economy_router.py @@ -1,3 +1,4 @@ +"""Economy Router.""" from openbb_core.app.model.command_context import CommandContext from openbb_core.app.model.obbject import OBBject from openbb_core.app.provider_interface import ( diff --git a/pyproject.toml b/pyproject.toml index 4dc8e4c95de..5a9c3a09d7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -188,6 +188,7 @@ requires = ["setuptools<65.5.0", "poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.pydocstyle] +inherit = true convention = "numpy" match = '((?!test_).)*\.py' add-ignore = "D401" From f719bfa92d1f29ef6870ac7c58bd25c2ff910aa6 Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Fri, 1 Sep 2023 16:44:17 +0200 Subject: [PATCH 5/9] Handle fred's error when API key is wrong --- .../fred/openbb_fred/utils/fred_base.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/openbb_sdk/providers/fred/openbb_fred/utils/fred_base.py b/openbb_sdk/providers/fred/openbb_fred/utils/fred_base.py index 08d4bb0868b..a213598515f 100644 --- a/openbb_sdk/providers/fred/openbb_fred/utils/fred_base.py +++ b/openbb_sdk/providers/fred/openbb_fred/utils/fred_base.py @@ -1,3 +1,4 @@ +"""Base class for Fred API.""" from datetime import date from typing import Any, Optional from urllib.parse import urlencode @@ -8,7 +9,16 @@ ROOT_URL = "https://api.stlouisfed.org/fred" class Fred: + """Base class for Fred API.""" + def __init__(self, api_key: Optional[str]): + """Initialize Fred class. + + Parameters + ---------- + api_key : str + API key for FRED + """ self.api_key = api_key def __fetch_data(self, url: str, **kwargs: Any): @@ -23,8 +33,9 @@ class Fred: end_date: Optional[date] = None, **kwargs, ) -> dict: - """ - Get data for a Fred series id. This fetches the latest known data. + """Get data for a Fred series id. + + This fetches the latest known data. Code copied from: https://github.com/mortada/fredapi/blob/master/fredapi/fred.py Parameters @@ -57,4 +68,10 @@ class Fred: root = self.__fetch_data(url, **kwargs) if root is None: raise ValueError("No data exists for series id: " + series_id) + if ( + "error_code" in root + and "error_message" in root + and root["error_message"] != "" + ): + raise ValueError(root["error_message"]) return root["observations"] From 86b77960dbef525e34fb612157917941763a7667 Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Fri, 1 Sep 2023 16:44:55 +0200 Subject: [PATCH 6/9] Add support for multiindex dfs in to_dataframe --- .../sdk/core/openbb_core/app/model/obbject.py | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/obbject.py b/openbb_sdk/sdk/core/openbb_core/app/model/obbject.py index 69f44838e11..00b363f823f 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/obbject.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/obbject.py @@ -1,5 +1,5 @@ """The OBBject.""" -from typing import Dict, Generic, List, Literal, Optional, TypeVar, Union +from typing import Dict, Generic, List, Literal, Optional, TypeVar import pandas as pd from pydantic import BaseModel, Field @@ -56,13 +56,13 @@ class OBBject(GenericModel, Generic[T], Tagged): ) ) - def to_dataframe(self) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: + def to_dataframe(self) -> pd.DataFrame: """Convert results field to pandas dataframe. Supports converting creating pandas DataFrames from the following serializable data formats: - - List[Basemodel] + - List[BaseModel] - List[Dict] - List[List] - List[str] @@ -70,11 +70,12 @@ class OBBject(GenericModel, Generic[T], Tagged): - List[float] - Dict[str, Dict] - Dict[str, List] + - Dict[str, BaseModel] Returns ------- - Union[pd.DataFrame, Dict[str, pd.DataFrame]] - Pandas dataframe or dictionary of dataframes. + pd.DataFrame + Pandas dataframe. """ if self.results is None or self.results == []: raise OpenBBError("Results not found.") @@ -83,8 +84,18 @@ class OBBject(GenericModel, Generic[T], Tagged): res = self.results df = None - - if isinstance(res, list) and all( + if isinstance(res, list) and len(res) == 1 and isinstance(res[0], dict): + for r in res: + dict_of_df = {} + for k, v in r.items(): + if isinstance(v, list) and all( + isinstance(nv, BaseModel) for nv in v + ): + dict_of_df[k] = basemodel_to_df(v, "date") + else: + dict_of_df[k] = pd.DataFrame(v) + df = pd.concat(dict_of_df, axis=1) + elif isinstance(res, list) and all( isinstance(item, BaseModel) for item in res ): df = basemodel_to_df(res, "date") @@ -95,8 +106,8 @@ class OBBject(GenericModel, Generic[T], Tagged): raise OpenBBError("Unsupported data format.") # Improve output so that all columns that are None are not returned - df = df.dropna(axis=1, how="all") df.sort_index(axis=1, inplace=True) + df = df.dropna(axis=1, how="all") except Exception as e: raise OpenBBError("Failed to convert results to DataFrame.") from e From 6fb25f0a93dfaf8225b14a5a19f5c461ddebe2d6 Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Fri, 1 Sep 2023 16:45:06 +0200 Subject: [PATCH 7/9] Update tests --- .../sdk/core/tests/app/model/test_obbject.py | 56 ++++++++++++++++++- .../core/tests/app/test_charting_manager.py | 32 ++++++++--- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/openbb_sdk/sdk/core/tests/app/model/test_obbject.py b/openbb_sdk/sdk/core/tests/app/model/test_obbject.py index 934d7ae2d70..f4b8f25d944 100644 --- a/openbb_sdk/sdk/core/tests/app/model/test_obbject.py +++ b/openbb_sdk/sdk/core/tests/app/model/test_obbject.py @@ -116,9 +116,61 @@ def test_to_dataframe_no_results(): {"0": [0, 2], "1": [1, 3], "2": [2, 0], "3": [3, 1], "4": [4, 6]} ), ), - # Test case 9: Empty results + # Test case 9: List of dict of data + ( + [ + { + "df1": [ + Data(date="1956-01-01", another_date="2023-09-01", value=0.0), + Data(date="1956-02-01", another_date="2023-09-01", value=0.0), + Data(date="1956-03-01", another_date="2023-09-01", value=0.0), + ], + "df2": [ + Data(date="1955-03-01", another_date="2023-09-01", value=0.0), + Data(date="1955-04-01", another_date="2023-09-01", value=0.0), + Data(date="1955-05-01", another_date="2023-09-01", value=0.0), + ], + } + ], + pd.concat( + { + "df1": pd.DataFrame( + { + "date": [ + pd.to_datetime("1956-01-01"), + pd.to_datetime("1956-02-01"), + pd.to_datetime("1956-03-01"), + ], + "another_date": [ + "2023-09-01", + "2023-09-01", + "2023-09-01", + ], + "value": [0.0, 0.0, 0.0], + } + ).set_index("date"), + "df2": pd.DataFrame( + { + "date": [ + pd.to_datetime("1955-03-01"), + pd.to_datetime("1955-04-01"), + pd.to_datetime("1955-05-01"), + ], + "another_date": [ + "2023-09-01", + "2023-09-01", + "2023-09-01", + ], + "value": [0.0, 0.0, 0.0], + } + ).set_index("date"), + }, + axis=1, + ), + ), + # Test case 10: Empty results ([], OpenBBError("Results not found.")), - # Test case 10: Results as None, should raise OpenBBError + # Test case 11: Results as None, should raise OpenBBError (None, OpenBBError("Results not found.")), ], ) diff --git a/openbb_sdk/sdk/core/tests/app/test_charting_manager.py b/openbb_sdk/sdk/core/tests/app/test_charting_manager.py index 6af3851e72c..4f489858363 100644 --- a/openbb_sdk/sdk/core/tests/app/test_charting_manager.py +++ b/openbb_sdk/sdk/core/tests/app/test_charting_manager.py @@ -1,3 +1,4 @@ +"""Test charting manager.""" import pytest from openbb_core.app.charting_manager import ChartingManager from openbb_core.app.model.system_settings import SystemSettings @@ -6,34 +7,39 @@ from openbb_core.app.model.user_settings import UserSettings @pytest.fixture(scope="function", autouse=True) def setup_and_teardown(): + """Prepare test context.""" # Code to run before each test function yield # This is where the test function runs # Code to run after each test function - ChartingManager._instances = {} + ChartingManager._instances = {} # pylint: disable=protected-access def test_charting_manager(): - assert ChartingManager() + """Smoke test.""" + assert ChartingManager() is not None # noqa: S101 def test_charting_manager_singleton_prop(): + """Smoke test.""" cm_1 = ChartingManager() cm_2 = ChartingManager() - assert cm_1 is cm_2 + assert cm_1 is cm_2 # noqa: S101 def test_charting_settings(): + """Test helper.""" sys = SystemSettings(test_mode=True) user = UserSettings(preferences={"plot_enable_pywry": False}) cm = ChartingManager(user_settings=user, system_settings=sys) - assert cm.charting_settings.plot_enable_pywry is False - assert cm.charting_settings.test_mode is True + assert cm.charting_settings.plot_enable_pywry is False # noqa: S101 + assert cm.charting_settings.test_mode is True # noqa: S101 def test_charting_settings_setter(): + """Test helper.""" cm = ChartingManager() sys = SystemSettings(test_mode=True) @@ -41,12 +47,20 @@ def test_charting_settings_setter(): cm.charting_settings = (sys, user) - assert cm.charting_settings.plot_enable_pywry is False - assert cm.charting_settings.test_mode is True + assert cm.charting_settings.plot_enable_pywry is False # noqa: S101 + assert cm.charting_settings.test_mode is True # noqa: S101 def test_check_charting_extension_installed(): + """Test helper.""" cm = ChartingManager() - assert cm.check_charting_extension_installed("openbb_charting") is True - assert cm.check_charting_extension_installed("openbb_core_extension_2") is False + try: + import openbb_charting # pylint: disable=unused-import,import-outside-toplevel # noqa: F401,E501 + + assert cm.check_charting_extension_installed("charting") is True # noqa: S101 + except ImportError: + assert cm.check_charting_extension_installed("charting") is False # noqa: S101 + assert ( # noqa: S101 + cm.check_charting_extension_installed("openbb_core_extension_2") is False + ) From dce9912c3431e363d6d654920c45da4490d8fa26 Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Mon, 4 Sep 2023 11:56:06 +0200 Subject: [PATCH 8/9] Ignore S101 on workspace level and fix some tests --- .../sdk/core/tests/app/model/test_obbject.py | 26 +++++++++---------- .../core/tests/app/test_charting_manager.py | 20 +++++++------- pyproject.toml | 3 +++ 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/openbb_sdk/sdk/core/tests/app/model/test_obbject.py b/openbb_sdk/sdk/core/tests/app/model/test_obbject.py index f4b8f25d944..7b4ff7bb1d0 100644 --- a/openbb_sdk/sdk/core/tests/app/model/test_obbject.py +++ b/openbb_sdk/sdk/core/tests/app/model/test_obbject.py @@ -10,17 +10,17 @@ from openbb_provider.abstract.data import Data def test_OBBject(): """Smoke test.""" co = OBBject() - assert isinstance(co, OBBject) # noqa: S101 + assert isinstance(co, OBBject) def test_fields(): """Smoke test.""" fields = OBBject.__fields__.keys() - assert "results" in fields # noqa: S101 - assert "provider" in fields # noqa: S101 - assert "warnings" in fields # noqa: S101 - assert "chart" in fields # noqa: S101 + assert "results" in fields + assert "provider" in fields + assert "warnings" in fields + assert "chart" in fields def test_to_dataframe_no_results(): @@ -182,12 +182,12 @@ def test_to_dataframe(results, expected_df): # Act and Assert if isinstance(expected_df, pd.DataFrame): result = co.to_dataframe() - assert result.equals(expected_df) # noqa: S101 + assert result.equals(expected_df) else: with pytest.raises(expected_df.__class__) as exc_info: co.to_dataframe() - assert str(exc_info.value) == str(expected_df) # noqa: S101 + assert str(exc_info.value) == str(expected_df) @pytest.mark.parametrize( @@ -224,12 +224,12 @@ def test_to_dict(results, expected): with pytest.raises(expected.__class__) as exc_info: co.to_dict() - assert str(exc_info.value) == str(expected) # noqa: S101 + assert str(exc_info.value) == str(expected) else: result = co.to_dict() result.pop("index", None) - assert result == expected # noqa: S101 + assert result == expected @patch("openbb_core.app.charting_manager.ChartingManager.to_chart") @@ -249,8 +249,8 @@ def test_to_chart_with_existing_chart(mock_to_dataframe, mock_to_chart): result = mock_instance.to_chart() # Assert - assert result == {"fig": "some_mock_fig"} # noqa: S101 - assert mock_instance.chart.content == {"content": "some_new_content"} # noqa: S101 + assert result == {"fig": "some_mock_fig"} + assert mock_instance.chart.content == {"content": "some_new_content"} mock_to_dataframe.assert_called_once() @@ -284,8 +284,8 @@ def test_to_chart_with_new_chart( result = mock_instance.to_chart() # Assert - assert result == {"fig": "some_mock_fig"} # noqa: S101 - assert mock_instance.chart.content == {"content": "some_new_content"} # noqa: S101 + assert result == {"fig": "some_mock_fig"} + assert mock_instance.chart.content == {"content": "some_new_content"} # Ensure self.to_dataframe() was called mock_to_dataframe.assert_called_once() diff --git a/openbb_sdk/sdk/core/tests/app/test_charting_manager.py b/openbb_sdk/sdk/core/tests/app/test_charting_manager.py index 4f489858363..d326dd4efeb 100644 --- a/openbb_sdk/sdk/core/tests/app/test_charting_manager.py +++ b/openbb_sdk/sdk/core/tests/app/test_charting_manager.py @@ -16,7 +16,7 @@ def setup_and_teardown(): def test_charting_manager(): """Smoke test.""" - assert ChartingManager() is not None # noqa: S101 + assert ChartingManager() is not None def test_charting_manager_singleton_prop(): @@ -24,7 +24,7 @@ def test_charting_manager_singleton_prop(): cm_1 = ChartingManager() cm_2 = ChartingManager() - assert cm_1 is cm_2 # noqa: S101 + assert cm_1 is cm_2 def test_charting_settings(): @@ -34,8 +34,8 @@ def test_charting_settings(): cm = ChartingManager(user_settings=user, system_settings=sys) - assert cm.charting_settings.plot_enable_pywry is False # noqa: S101 - assert cm.charting_settings.test_mode is True # noqa: S101 + assert cm.charting_settings.plot_enable_pywry is False + assert cm.charting_settings.test_mode is True def test_charting_settings_setter(): @@ -47,8 +47,8 @@ def test_charting_settings_setter(): cm.charting_settings = (sys, user) - assert cm.charting_settings.plot_enable_pywry is False # noqa: S101 - assert cm.charting_settings.test_mode is True # noqa: S101 + assert cm.charting_settings.plot_enable_pywry is False + assert cm.charting_settings.test_mode is True def test_check_charting_extension_installed(): @@ -58,9 +58,7 @@ def test_check_charting_extension_installed(): try: import openbb_charting # pylint: disable=unused-import,import-outside-toplevel # noqa: F401,E501 - assert cm.check_charting_extension_installed("charting") is True # noqa: S101 + assert cm.check_charting_extension_installed("charting") is True except ImportError: - assert cm.check_charting_extension_installed("charting") is False # noqa: S101 - assert ( # noqa: S101 - cm.check_charting_extension_installed("openbb_core_extension_2") is False - ) + assert cm.check_charting_extension_installed("charting") is False + assert cm.check_charting_extension_installed("openbb_core_extension_2") is False diff --git a/pyproject.toml b/pyproject.toml index 5a9c3a09d7a..0627a3e4b0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -193,6 +193,9 @@ convention = "numpy" match = '((?!test_).)*\.py' add-ignore = "D401" +[tool.flake8] +max-line-length = 122 +ignore = "S101" [tool.ruff] line-length = 122 From 94d2174e2bd6154494e445838c0e0a6d901d1993 Mon Sep 17 00:00:00 2001 From: Theodore Aptekarev Date: Mon, 4 Sep 2023 12:59:46 +0200 Subject: [PATCH 9/9] Fix typo --- openbb_sdk/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openbb_sdk/CONTRIBUTING.md b/openbb_sdk/CONTRIBUTING.md index 55df96dda09..dfe25676839 100644 --- a/openbb_sdk/CONTRIBUTING.md +++ b/openbb_sdk/CONTRIBUTING.md @@ -420,7 +420,7 @@ Refer to the [`to_chart` implementation](openbb_sdk/extensions/charting/openbb_c In order to contribute to the SDK you need to setup your environment in order to ensure a smooth development experience.
-Need help seting up Miniconda or Git? +Need help setting up Miniconda or Git? Sometimes installing Miniconda or Git can be a bit tricky, so we've prepared a set of instructions to help you get started.