Skip to content

Commit

Permalink
Merge branch 'develop' into hotfix/update-api-endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
deeleeramone committed Dec 13, 2023
2 parents b60362c + f9fbb2a commit db79720
Show file tree
Hide file tree
Showing 62 changed files with 4,005 additions and 3,436 deletions.
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
- [ ] I have performed a self-review of my own code.
- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have adhered to the GitFlow naming convention and my branch name is in the format of `feature/feature-name` or `hotfix/hotfix-name`.
- [ ] I ensure that I am following th [CONTRIBUTING guidelines](https://github.com/OpenBB-finance/OpenBBTerminal/blob/main/CONTRIBUTING.md).
- [ ] I ensure that I am following the [CONTRIBUTING guidelines](https://github.com/OpenBB-finance/OpenBBTerminal/blob/main/CONTRIBUTING.md).
- [ ] (If applicable) I have updated tests following [these guidelines](/openbb_platform/CONTRIBUTING.md#qa-your-extension).

</details>
12 changes: 11 additions & 1 deletion .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ jobs:
python-version: "3.9"
architecture: x64

- name: Get changed files in openbb_terminal for PR
if: github.event_name == 'pull_request'
run: |
echo "files=$(git diff --name-only origin/${{ github.base_ref }}...${{ github.head_ref }} | grep 'openbb_terminal/.*\.py$' | xargs)" >> $GITHUB_ENV
- uses: actions/cache@v3
with:
path: ~/.cache/pip
Expand All @@ -50,7 +55,12 @@ jobs:
- run: black --diff --check .
- run: codespell --ignore-words=.codespell.ignore --skip="$(tr '\n' ',' < .codespell.skip | sed 's/,$//')" --quiet-level=2
- run: ruff .
- run: mypy --ignore-missing-imports openbb_terminal
- run: |
if [ -n "${{ env.files }}" ]; then
mypy --ignore-missing-imports ${{ env.files }}
else
echo "No Python files changed in openbb_terminal"
fi
- run: pylint terminal.py openbb_terminal tests

markdown-link-check:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""The init of the coverage helpers."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""Coverage API router helper functions."""
from inspect import _empty, signature
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type

from openbb_core.app.provider_interface import ProviderInterface
from pydantic import BaseModel, Field, create_model

if TYPE_CHECKING:
from openbb_core.app.static.app_factory import BaseApp

provider_interface = ProviderInterface()


def get_route_callable(app: "BaseApp", route: str) -> Callable:
"""Get the callable for a route."""
# TODO: Add return typing Optional[Callable] to this function. First need to
# figure how to do that starting from "BaseApp" and account for the possibility
# of a route not existing. Then remove the type: ignore from the function.

split_route = route.replace(".", "/").split("/")[1:]

return_callable = app

for route_path in split_route:
return_callable = getattr(return_callable, route_path)

return return_callable # type: ignore


def signature_to_fields(app: "BaseApp", route: str) -> Dict[str, Tuple[Any, Field]]: # type: ignore
"""Convert a command signature to pydantic fields."""
return_callable = get_route_callable(app, route)
sig = signature(return_callable)

fields = {}
for name, param in sig.parameters.items():
if name not in ["kwargs", "args"]:
type_annotation = (
param.annotation if param.annotation is not _empty else Any
)
description = (
param.annotation.__metadata__[0].description
if hasattr(param.annotation, "__metadata__")
else None
)
fields[name] = (
type_annotation,
Field(..., title="openbb", description=description),
)

return fields


def dataclass_to_fields(model_name: str) -> Dict[str, Tuple[Any, Field]]: # type: ignore
"""Convert a dataclass to pydantic fields."""
dataclass = provider_interface.params[model_name]["extra"]
fields = {}
for name, field in dataclass.__dataclass_fields__.items():
type_annotation = field.default.annotation if field.default is not None else Any # type: ignore
description = field.default.description if field.default is not None else None # type: ignore
title = field.default.title if field.default is not None else None # type: ignore
fields[name] = (
type_annotation,
Field(..., title=title, description=description),
)

return fields


def create_combined_model(
model_name: str,
*field_sets: Dict[str, Tuple[Any, Field]], # type: ignore
filter_by_provider: Optional[str] = None,
) -> Type[BaseModel]:
"""Create a combined pydantic model."""
combined_fields = {}
for fields in field_sets:
for name, (type_annotation, field) in fields.items():
if (
filter_by_provider is None
or "openbb" in field.title # type: ignore
or (filter_by_provider in field.title) # type: ignore
):
combined_fields[name] = (type_annotation, field)

model = create_model(model_name, **combined_fields) # type: ignore

# # Clean up the metadata
for field in model.model_fields.values():
if hasattr(field, "metadata"):
field.metadata = None # type: ignore

return model


def get_route_schema_map(
app: "BaseApp",
command_model_map: Dict[str, str],
filter_by_provider: Optional[str] = None,
) -> Dict[str, Dict[str, Any]]:
"""Get the route schema map."""
route_schema_map = {}
for route, model in command_model_map.items():
input_model = create_combined_model(
route,
signature_to_fields(app, route),
dataclass_to_fields(model),
filter_by_provider=filter_by_provider,
)
output_model = provider_interface.return_schema[model]
return_callable = get_route_callable(app, route)

route_schema_map[route] = {
"input": input_model,
"output": output_model,
"callable": return_callable,
}

return route_schema_map
45 changes: 33 additions & 12 deletions openbb_platform/core/openbb_core/app/model/obbject.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
Optional,
Set,
TypeVar,
Union,
)

import pandas as pd
Expand All @@ -21,9 +22,9 @@
from openbb_core.app.model.abstract.tagged import Tagged
from openbb_core.app.model.abstract.warning import Warning_
from openbb_core.app.model.charts.chart import Chart
from openbb_core.app.provider_interface import ProviderInterface
from openbb_core.app.query import Query
from openbb_core.app.utils import basemodel_to_df
from openbb_core.provider.abstract.data import Data

if TYPE_CHECKING:
try:
Expand All @@ -32,7 +33,6 @@
PolarsDataFrame = None

T = TypeVar("T")
PROVIDERS = Literal[tuple(ProviderInterface().available_providers)] # type: ignore


class OBBject(Tagged, Generic[T]):
Expand All @@ -42,7 +42,7 @@ class OBBject(Tagged, Generic[T]):
default=None,
description="Serializable results.",
)
provider: Optional[PROVIDERS] = Field( # type: ignore
provider: Optional[str] = Field( # type: ignore
default=None,
description="Provider name.",
)
Expand Down Expand Up @@ -89,11 +89,15 @@ def model_parametrized_name(cls, params: Any) -> str:
"""Return the model name with the parameters."""
return f"OBBject[{cls.results_type_repr(params)}]"

def to_df(self) -> pd.DataFrame:
def to_df(
self, index: Optional[str] = None, sort_by: Optional[str] = None
) -> pd.DataFrame:
"""Alias for `to_dataframe`."""
return self.to_dataframe()
return self.to_dataframe(index=index, sort_by=sort_by)

def to_dataframe(self) -> pd.DataFrame:
def to_dataframe(
self, index: Optional[str] = None, sort_by: Optional[str] = None
) -> pd.DataFrame:
"""Convert results field to pandas dataframe.
Supports converting creating pandas DataFrames from the following
Expand All @@ -109,18 +113,25 @@ def to_dataframe(self) -> pd.DataFrame:
- Dict[str, List]
- Dict[str, BaseModel]
Parameters
----------
index : Optional[str]
Column name to use as index.
sort_by : Optional[str]
Column name to sort by.
Returns
-------
pd.DataFrame
Pandas dataframe.
"""

def is_list_of_basemodel(items: List[Any]) -> bool:
def is_list_of_basemodel(items: Union[List[T], T]) -> bool:
return isinstance(items, list) and all(
isinstance(item, BaseModel) for item in items
)

if self.results is None or self.results == []:
if self.results is None or not self.results:
raise OpenBBError("Results not found.")

if isinstance(self.results, pd.DataFrame):
Expand All @@ -139,7 +150,7 @@ def is_list_of_basemodel(items: List[Any]) -> bool:
for k, v in r.items():
# Dict[str, List[BaseModel]]
if is_list_of_basemodel(v):
dict_of_df[k] = basemodel_to_df(v, "date")
dict_of_df[k] = basemodel_to_df(v, index or "date")
sort_columns = False
# Dict[str, Any]
else:
Expand All @@ -149,12 +160,17 @@ def is_list_of_basemodel(items: List[Any]) -> bool:

# List[BaseModel]
elif is_list_of_basemodel(res):
df = basemodel_to_df(res, "date")
dt: Union[List[Data], Data] = res # type: ignore
df = basemodel_to_df(dt, index or "date")
sort_columns = False
# List[List | str | int | float] | Dict[str, Dict | List | BaseModel]
else:
try:
df = pd.DataFrame(res)
# Set index, if any
if index and index in df.columns:
df.set_index(index, inplace=True)

except ValueError:
if isinstance(res, dict):
df = pd.DataFrame([res])
Expand All @@ -167,6 +183,10 @@ def is_list_of_basemodel(items: List[Any]) -> bool:
df.sort_index(axis=1, inplace=True)
df = df.dropna(axis=1, how="all")

# Sort by specified column
if sort_by:
df.sort_values(by=sort_by, inplace=True)

except OpenBBError as e:
raise e
except ValueError as ve:
Expand Down Expand Up @@ -223,7 +243,8 @@ def to_dict(
if not isinstance(self.results, dict):
transpose = False
else: # Only enter the loop if self.results is a dictionary
for key, value in self.results.items():
self.results: Dict[str, Any] = self.results # type: ignore
for _, value in self.results.items():
if not isinstance(value, dict):
transpose = False
break
Expand All @@ -237,6 +258,7 @@ def to_dict(
def to_chart(self, **kwargs):
"""
Create or update the `Chart`.
This function assumes that the provided data is a time series, if it's not, it will
most likely result in an Exception.
Expand Down Expand Up @@ -287,5 +309,4 @@ async def from_query(cls, query: Query) -> "OBBject":
OBBject[ResultsType]
OBBject with results.
"""

return cls(results=await query.execute())
6 changes: 2 additions & 4 deletions openbb_platform/core/openbb_core/app/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ def command_coverage(self) -> Dict[str, List[str]]:
return self._command_coverage

@property
def commands_model(self) -> Dict[str, List[str]]:
def commands_model(self) -> Dict[str, str]:
return self._commands_model

@staticmethod
Expand Down Expand Up @@ -526,9 +526,7 @@ def get_command_coverage(
return coverage_map

@staticmethod
def get_commands_model(
router: Router, sep: Optional[str] = None
) -> Dict[str, List[str]]:
def get_commands_model(router: Router, sep: Optional[str] = None) -> Dict[str, str]:
api_router = router.api_router

coverage_map: Dict[Any, Any] = {}
Expand Down
8 changes: 4 additions & 4 deletions openbb_platform/core/openbb_core/app/static/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def login(
Returns
-------
UserSettings
Optional[UserSettings]
User settings: profile, credentials, preferences
"""
hs = self._create_hub_service(email, password, pat)
Expand Down Expand Up @@ -145,7 +145,7 @@ def save(self, return_settings: bool = False) -> Optional[UserSettings]:
Returns
-------
UserSettings
Optional[UserSettings]
User settings: profile, credentials, preferences
"""
hub_session = self._base_app._command_runner.user_settings.profile.hub_session
Expand All @@ -172,7 +172,7 @@ def refresh(self, return_settings: bool = False) -> Optional[UserSettings]:
Returns
-------
UserSettings
Optional[UserSettings]
User settings: profile, credentials, preferences
"""
hub_session = self._base_app._command_runner.user_settings.profile.hub_session
Expand Down Expand Up @@ -202,7 +202,7 @@ def logout(self, return_settings: bool = False) -> Optional[UserSettings]:
Returns
-------
UserSettings
Optional[UserSettings]
User settings: profile, credentials, preferences
"""
hub_session = self._base_app._command_runner.user_settings.profile.hub_session
Expand Down
2 changes: 1 addition & 1 deletion openbb_platform/core/openbb_core/app/static/app_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class BaseApp:
def __init__(self, command_runner: CommandRunner):
self._command_runner = command_runner
self._account = Account(self)
self._coverage = Coverage()
self._coverage = Coverage(self)

@property
def account(self) -> Account:
Expand Down
Loading

0 comments on commit db79720

Please sign in to comment.