Skip to content

Commit

Permalink
Merge branch 'develop' into release/3.2.5
Browse files Browse the repository at this point in the history
  • Loading branch information
montezdesousa authored Mar 14, 2024
2 parents 6ea3c45 + 3b7f18f commit 686adae
Show file tree
Hide file tree
Showing 22 changed files with 5,881 additions and 2,472 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ openbb_terminal/core/plots/assets/plotly*.js
.dccache
*rome.json
**/node_modules/*
.cursorignore
# pyinstaller artifacts
*.pyo
**/dist/*
Expand Down
122 changes: 92 additions & 30 deletions openbb_platform/core/openbb_core/app/static/package_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,12 +892,17 @@ def get_field_type(
Parameters
----------
field (FieldInfo): Pydantic field object containing field information.
target (Literal["docstring", "website"], optional): Target to return type for. Defaults to "docstring".
field_type (Any):
Typing object containing the field type.
is_required (bool):
Flag to indicate if the field is required.
target (Literal["docstring", "website"], optional):
Target to return type for. Defaults to "docstring".
Returns
-------
str: String representation of the field type.
str:
String representation of the field type.
"""
is_optional = not is_required

Expand All @@ -907,7 +912,7 @@ def get_field_type(
if "BeforeValidator" in str(_type):
_type = "Optional[int]" if is_optional else "int" # type: ignore

field_type = (
_type = (
str(_type)
.replace("<class '", "")
.replace("'>", "")
Expand All @@ -919,17 +924,22 @@ def get_field_type(
.replace(", None", "")
)

field_type = (
f"Optional[{field_type}]"
if "openbb_" in str(_type):
_type = (
str(_type).split(".", maxsplit=1)[0].split("openbb_")[0]
+ str(_type).rsplit(".", maxsplit=1)[-1]
)

_type = (
f"Optional[{_type}]"
if is_optional and "Optional" not in str(_type)
else field_type
else _type
)

if target == "website":
field_type = re.sub(r"Optional\[(.*)\]", r"\1", field_type)
field_type = re.sub(r"Annotated\[(.*)\]", r"\1", field_type)
_type = re.sub(r"Optional\[(.*)\]", r"\1", _type)

return field_type
return _type

except TypeError:
# Fallback to the annotation if the repr fails
Expand All @@ -939,11 +949,10 @@ def get_field_type(
def get_OBBject_description(
results_type: str,
providers: Optional[str],
target: Literal["docstring", "website"] = "docstring",
) -> str:
"""Get the command output description."""
available_providers = providers or "Optional[str]"
indent = 2 if target == "docstring" else 0
indent = 2

obbject_description = (
f"{create_indent(indent)}OBBject\n"
Expand Down Expand Up @@ -1358,7 +1367,7 @@ def get_provider_field_params(
# Should be removed if TYPE_EXPANSION is updated to include this
field_type = f"Union[{field_type}, List[{field_type}]]"

default_value = "" if field_info.default is PydanticUndefined else str(field_info.default) # fmt: skip
default_value = "" if field_info.default is PydanticUndefined else field_info.default # fmt: skip

provider_field_params.append(
{
Expand All @@ -1372,6 +1381,56 @@ def get_provider_field_params(

return provider_field_params

@staticmethod
def get_obbject_returns_fields(
model: str,
providers: str,
) -> List[Dict[str, str]]:
"""Get the fields of the OBBject returns object for the given standard_model.
Args
----
model (str):
Standard model of the returned object.
providers (str):
Available providers for the model.
Returns
-------
List[Dict[str, str]]:
List of dictionaries containing the field name, type, description, default
and optionality of each field.
"""
obbject_list = [
{
"name": "results",
"type": f"List[{model}]",
"description": "Serializable results.",
},
{
"name": "provider",
"type": f"Optional[{providers}]",
"description": "Provider name.",
},
{
"name": "warnings",
"type": "Optional[List[Warning_]]",
"description": "List of warnings.",
},
{
"name": "chart",
"type": "Optional[Chart]",
"description": "Chart object.",
},
{
"name": "extra",
"type": "Dict[str, Any]",
"description": "Extra info.",
},
]

return obbject_list

@staticmethod
def get_post_method_parameters_info(
docstring: str,
Expand Down Expand Up @@ -1429,7 +1488,7 @@ def get_post_method_parameters_info(
return parameters_list

@staticmethod
def get_post_method_returns_info(docstring: str) -> str:
def get_post_method_returns_info(docstring: str) -> List[Dict[str, str]]:
"""Get the returns information for the POST method endpoints.
Parameters
Expand All @@ -1439,10 +1498,11 @@ def get_post_method_returns_info(docstring: str) -> str:
Returns
-------
Dict[str, str]:
Dictionary containing the name, type, description of the return value
List[Dict[str, str]]:
Single element list having a dictionary containing the name, type,
description of the return value
"""
return_info = ""
returns_list = []

# Define a regex pattern to match the Returns section
# This pattern captures the model name inside "OBBject[]" and its description
Expand All @@ -1456,15 +1516,17 @@ def get_post_method_returns_info(docstring: str) -> str:
content_inside_brackets = re.search(
r"OBBject\[\s*((?:[^\[\]]|\[[^\[\]]*\])*)\s*\]", return_type
)
return_type_content = content_inside_brackets.group(1) # type: ignore
return_type = content_inside_brackets.group(1) # type: ignore

return_info = (
f"OBBject\n"
f"{create_indent(1)}results : {return_type_content}\n"
f"{create_indent(2)}{description}"
)
returns_list = [
{
"name": "results",
"type": return_type,
"description": description,
}
]

return return_info
return returns_list

@classmethod
def get_reference_data(cls) -> Dict[str, Dict[str, Any]]:
Expand All @@ -1489,7 +1551,7 @@ def get_reference_data(cls) -> Dict[str, Dict[str, Any]]:
# Route method is used to distinguish between GET and POST methods
route_method = getattr(route, "methods", None)
# Route endpoint is the callable function
route_func = getattr(route, "endpoint", None)
route_func = getattr(route, "endpoint", lambda: None)
# Attribute contains the model and examples info for the endpoint
openapi_extra = getattr(route, "openapi_extra", {})
# Standard model is used as the key for the ProviderInterface Map dictionary
Expand All @@ -1504,7 +1566,9 @@ def get_reference_data(cls) -> Dict[str, Dict[str, Any]]:
# Add endpoint examples
examples = openapi_extra.get("examples", [])
reference[path]["examples"] = cls.get_endpoint_examples(
path, route_func, examples # type: ignore
path,
route_func,
examples, # type: ignore
)
# Add data for the endpoints having a standard model
if route_method == {"GET"}:
Expand Down Expand Up @@ -1548,10 +1612,8 @@ def get_reference_data(cls) -> Dict[str, Dict[str, Any]]:
# Add endpoint returns data
# Currently only OBBject object is returned
providers = provider_parameter_fields["type"]
reference[path]["returns"]["OBBject"] = (
DocstringGenerator.get_OBBject_description(
standard_model, providers, "website"
)
reference[path]["returns"]["OBBject"] = cls.get_obbject_returns_fields(
standard_model, providers
)
# Add data for the endpoints without a standard model (data processing endpoints)
elif route_method == {"POST"}:
Expand Down
21 changes: 12 additions & 9 deletions openbb_platform/core/openbb_core/provider/registry_map.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Provider registry map."""

import sys
from copy import deepcopy
from inspect import getfile, isclass
from pathlib import Path
from typing import Any, Dict, List, Literal, Optional, Tuple, get_origin
Expand Down Expand Up @@ -82,9 +83,10 @@ def _get_map(self, registry: Registry) -> Tuple[MapType, Dict[str, Dict]]:
standard_data, extra_data = self.extract_info(fetcher, "data")
if model_name not in map_:
map_[model_name] = {}
# The deepcopy avoids modifications from one model to affect another
map_[model_name]["openbb"] = {
"QueryParams": standard_query,
"Data": standard_data,
"QueryParams": deepcopy(standard_query),
"Data": deepcopy(standard_data),
}
map_[model_name][p] = {
"QueryParams": extra_query,
Expand All @@ -98,25 +100,26 @@ def _get_map(self, registry: Registry) -> Tuple[MapType, Dict[str, Dict]]:
{p: {"model": provider_model, "is_list": is_list}}
)

self._merge_json_schema_extra(p, fetcher, standard_query, extra_query)
self._merge_json_schema_extra(p, fetcher, map_[model_name])

return map_, return_schemas

def _merge_json_schema_extra(
self,
provider: str,
fetcher: Fetcher,
standard_query: dict,
extra_query: dict,
model_map: dict,
):
"""Merge json schema extra for different providers"""
model: BaseModel = RegistryMap._get_model(fetcher, "query_params")
std_fields = model_map["openbb"]["QueryParams"]["fields"]
extra_fields = model_map[provider]["QueryParams"]["fields"]
for f, props in getattr(model, "__json_schema_extra__", {}).items():
for p in props:
if f in standard_query["fields"]:
model_field = standard_query["fields"][f]
elif f in extra_query["fields"]:
model_field = extra_query["fields"][f]
if f in std_fields:
model_field = std_fields[f]
elif f in extra_fields:
model_field = extra_fields[f]
else:
continue

Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Test the econometrics utils module."""

import numpy as np
import pandas as pd
from extensions.econometrics.openbb_econometrics.utils import (
get_engle_granger_two_step_cointegration_test,
mock_multi_index_data,
)


def test_get_engle_granger_two_step_cointegration_test():
"""Test the get_engle_granger_two_step_cointegration_test function."""
x = pd.Series(np.random.randn(100))
y = pd.Series(np.random.randn(100))

result = get_engle_granger_two_step_cointegration_test(x, y)

assert result


def test_mock_multi_index_data():
"""Test the mock_multi_index_data function."""
mi_data = mock_multi_index_data()
assert isinstance(mi_data, pd.DataFrame)
assert mi_data.index.nlevels == 2
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Test the quantitative helpers."""

import pandas as pd
from extensions.quantitative.openbb_quantitative.helpers import (
validate_window,
)


def test_validate_window():
input_data = pd.Series(range(1, 100))
validate_window(
input_data=input_data,
window=20,
)
Empty file.
Loading

0 comments on commit 686adae

Please sign in to comment.