Skip to content

Commit

Permalink
Merge remote-tracking branch 'OpenBB-finance/develop' into feature/ja…
Browse files Browse the repository at this point in the history
…mes-didier-feedback
  • Loading branch information
hjoaquim committed May 10, 2024
2 parents 3c39a3f + 83476ad commit b9b485d
Show file tree
Hide file tree
Showing 107 changed files with 813 additions and 379 deletions.
26 changes: 18 additions & 8 deletions cli/openbb_cli/argparse_translator/argparse_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,9 @@ def __init__(
def _handle_argument_in_groups(self, argument, group):
"""Handle the argument and add it to the parser."""

def _in_optional_arguments(arg):
def _in_group(arg, group_title):
for action_group in self._parser._action_groups:
if action_group.title == "optional arguments":
if action_group.title == group_title:
for action in action_group._group_actions:
opts = action.option_strings
if (opts and opts[0] == arg) or action.dest == arg:
Expand Down Expand Up @@ -286,16 +286,26 @@ def _update_providers(
# extend choices
choices = tuple(set(_get_arg_choices(argument.name) + model_choices))

# check if the argument is in the required arguments
if _in_group(argument.name, group_title="required arguments"):
for action in self._required._group_actions:
if action.dest == argument.name and choices:
# update choices
action.choices = choices
return

# check if the argument is in the optional arguments
if _in_optional_arguments(argument.name):
if _in_group(argument.name, group_title="optional arguments"):
for action in self._parser._actions:
if action.dest == argument.name:
# update choices
action.choices = choices
# update help
action.help = _update_providers(
action.help or "", [group.title]
)
if choices:
action.choices = choices
if argument.name not in self.signature.parameters:
# update help
action.help = _update_providers(
action.help or "", [group.title]
)
return

# if the argument is in use, remove it from all groups
Expand Down
58 changes: 33 additions & 25 deletions openbb_platform/core/openbb_core/app/command_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,31 +304,38 @@ def _chart(
**kwargs,
) -> None:
"""Create a chart from the command output."""
if "charting" not in obbject.accessors:
raise OpenBBError(
"Charting is not installed. Please install `openbb-charting`."
)
chart_params = {}
extra_params = kwargs.get("extra_params", {})

if hasattr(extra_params, "__dict__") and hasattr(extra_params, "chart_params"):
chart_params = kwargs["extra_params"].__dict__.get("chart_params", {})
elif isinstance(extra_params, dict) and "chart_params" in extra_params:
chart_params = kwargs["extra_params"].get("chart_params", {})

if "chart_params" in kwargs and kwargs["chart_params"] is not None:
chart_params.update(kwargs.pop("chart_params", {}))

if (
"kwargs" in kwargs
and "chart_params" in kwargs["kwargs"]
and kwargs["kwargs"].get("chart_params") is not None
):
chart_params.update(kwargs.pop("kwargs", {}).get("chart_params", {}))

if chart_params:
kwargs.update(chart_params)
obbject.charting.show(render=False, **kwargs)
try:
if "charting" not in obbject.accessors:
raise OpenBBError(
"Charting is not installed. Please install `openbb-charting`."
)
chart_params = {}
extra_params = kwargs.get("extra_params", {})

if hasattr(extra_params, "__dict__") and hasattr(
extra_params, "chart_params"
):
chart_params = kwargs["extra_params"].__dict__.get("chart_params", {})
elif isinstance(extra_params, dict) and "chart_params" in extra_params:
chart_params = kwargs["extra_params"].get("chart_params", {})

if "chart_params" in kwargs and kwargs["chart_params"] is not None:
chart_params.update(kwargs.pop("chart_params", {}))

if (
"kwargs" in kwargs
and "chart_params" in kwargs["kwargs"]
and kwargs["kwargs"].get("chart_params") is not None
):
chart_params.update(kwargs.pop("kwargs", {}).get("chart_params", {}))

if chart_params:
kwargs.update(chart_params)
obbject.charting.show(render=False, **kwargs)
except Exception as e: # pylint: disable=broad-exception-caught
if Env().DEBUG_MODE:
raise OpenBBError(e) from e
warn(str(e), OpenBBWarning)

# pylint: disable=R0913, R0914
@classmethod
Expand Down Expand Up @@ -449,6 +456,7 @@ async def run(
except Exception as e:
if Env().DEBUG_MODE:
raise OpenBBError(e) from e
warn(str(e), OpenBBWarning)

return obbject

Expand Down
29 changes: 18 additions & 11 deletions openbb_platform/core/openbb_core/app/provider_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,24 @@ def _create_field(
annotation = field.annotation

additional_description = ""
if (extra := field.json_schema_extra) and (
multiple := extra.get("multiple_items_allowed") # type: ignore
):
if provider_name:
additional_description += " Multiple comma separated items allowed."
else:
additional_description += (
" Multiple comma separated items allowed for provider(s): "
+ ", ".join(multiple) # type: ignore[arg-type]
+ "."
)
if extra := field.json_schema_extra:
providers = []
for p, v in extra.items(): # type: ignore[union-attr]
if isinstance(v, dict) and v.get("multiple_items_allowed"):
providers.append(p)
elif isinstance(v, list) and "multiple_items_allowed" in v:
# For backwards compatibility, before this was a list
providers.append(p)

if providers:
if provider_name:
additional_description += " Multiple comma separated items allowed."
else:
additional_description += (
" Multiple comma separated items allowed for provider(s): "
+ ", ".join(providers) # type: ignore[arg-type]
+ "."
)

provider_field = (
f"(provider: {provider_name})" if provider_name != "openbb" else ""
Expand Down
32 changes: 25 additions & 7 deletions openbb_platform/core/openbb_core/app/static/package_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,15 @@ def get_expanded_type(
original_type: Optional[type] = None,
) -> object:
"""Expand the original field type."""
if extra and "multiple_items_allowed" in extra:
if extra and any(
(
v.get("multiple_items_allowed")
if isinstance(v, dict)
# For backwards compatibility, before this was a list
else "multiple_items_allowed" in v
)
for v in extra.values()
):
if original_type is None:
raise ValueError(
"multiple_items_allowed requires the original type to be specified."
Expand Down Expand Up @@ -1450,6 +1458,10 @@ def _get_provider_field_params(
expanded_types = MethodDefinition.TYPE_EXPANSION
model_map = cls.pi.map[model]

# TODO: Change this to read the package data instead of pi.map directly
# We change some items (types, descriptions), so the reference.json
# does not reflect entirely the package code.

for field, field_info in model_map[provider][params_type]["fields"].items():
# Determine the field type, expanding it if necessary and if params_type is "Parameters"
field_type = field_info.annotation
Expand All @@ -1470,12 +1482,18 @@ def _get_provider_field_params(
) # fmt: skip

# Add information for the providers supporting multiple symbols
if params_type == "QueryParams" and field_info.json_schema_extra:
multiple_items_list = field_info.json_schema_extra.get(
"multiple_items_allowed", None
)
if multiple_items_list:
multiple_items = ", ".join(multiple_items_list)
if params_type == "QueryParams" and (extra := field_info.json_schema_extra):

providers = []
for p, v in extra.items(): # type: ignore[union-attr]
if isinstance(v, dict) and v.get("multiple_items_allowed"):
providers.append(p)
elif isinstance(v, list) and "multiple_items_allowed" in v:
# For backwards compatibility, before this was a list
providers.append(p)

if providers:
multiple_items = ", ".join(providers)
cleaned_description += (
f" Multiple items allowed for provider(s): {multiple_items}."
)
Expand Down
54 changes: 32 additions & 22 deletions openbb_platform/core/openbb_core/app/static/utils/filters.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"""OpenBB filters."""

from typing import Dict, List, Optional
from typing import Any, Dict, Optional

from openbb_core.app.utils import check_single_item, convert_to_basemodel


def filter_inputs(
data_processing: bool = False,
info: Optional[Dict[str, Dict[str, List[str]]]] = None,
info: Optional[Dict[str, Dict[str, Any]]] = None,
**kwargs,
) -> dict:
"""Filter command inputs."""
Expand All @@ -16,32 +16,42 @@ def filter_inputs(
kwargs[key] = convert_to_basemodel(value)

if info:
PROPERTY = "multiple_items_allowed"

# Here we check if list items are passed and multiple items allowed for
# the given provider/input combination. In that case we transform the list
# into a comma-separated string
for field, props in info.items():
if PROPERTY in props and (
provider := kwargs.get("provider_choices", {}).get("provider")
):
for p in ("standard_params", "extra_params"):
if field in kwargs.get(p, {}):
current = kwargs[p][field]
new = (
",".join(map(str, current))
if isinstance(current, list)
else current
provider = kwargs.get("provider_choices", {}).get("provider")
for field, properties in info.items():

for p in ("standard_params", "extra_params"):
if field in kwargs.get(p, {}):
current = kwargs[p][field]
new = (
",".join(map(str, current))
if isinstance(current, list)
else current
)

provider_properties = properties.get(provider, {})
if isinstance(provider_properties, dict):
multiple_items_allowed = provider_properties.get(
"multiple_items_allowed"
)
elif isinstance(provider_properties, list):
# For backwards compatibility, before this was a list
multiple_items_allowed = (
"multiple_items_allowed" in provider_properties
)
else:
multiple_items_allowed = True

if provider and provider not in props[PROPERTY]:
check_single_item(
new,
f"{field} -> multiple items not allowed for '{provider}'",
)
if not multiple_items_allowed:
check_single_item(
new,
f"{field} -> multiple items not allowed for '{provider}'",
)

kwargs[p][field] = new
break
kwargs[p][field] = new
break
else:
provider = kwargs.get("provider_choices", {}).get("provider")
for param_category in ("standard_params", "extra_params"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,24 @@ class QueryParams(BaseModel):
Merge different json schema extra, identified by provider.
Example:
FMP fetcher:
__json_schema_extra__ = {"symbol": ["multiple_items_allowed"]}
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
Intrinio fetcher
__json_schema_extra__ = {"symbol": ["multiple_items_allowed"]}
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": False}}
Creates a new field in the `symbol` schema with:
Creates new fields in the `symbol` schema:
{
...,
"multiple_items_allowed": ["fmp", "intrinio"],
"type": "string",
"description": "Symbol to get data for.",
"fmp": {"multiple_items_allowed": True},
"intrinio": {"multiple_items_allowed": False}
...,
}
Multiple fields can be tagged with the same or multiple properties.
Example:
__json_schema_extra__ = {
"<field_name_A>": ["some_prop", "another_prop"],
"<field_name_B>": ["yet_another_prop"]
"<field_name_A>": {"foo": 123, "bar": 456},
"<field_name_B>": {"foo": 789}
}
Attributes:
Expand Down
Loading

0 comments on commit b9b485d

Please sign in to comment.