diff options
author | Chavithra PARANA <chavithra@gmail.com> | 2022-11-30 16:08:34 +0100 |
---|---|---|
committer | Chavithra PARANA <chavithra@gmail.com> | 2022-11-30 16:08:34 +0100 |
commit | 663b6ce5fe03b0d790cee05d4b1b915febde80c4 (patch) | |
tree | e541cb287aba94f689e80af0ee0c6d20f78d0788 | |
parent | 971c5eba075ab2faf9cb666bfaa6349e33264024 (diff) | |
parent | 1dc3bc0b569d4c3c90719649361da84618a3514e (diff) |
Merge branch 'main' of github.com:OpenBB-finance/OpenBBTerminal
14 files changed, 610 insertions, 654 deletions
diff --git a/.gitignore b/.gitignore index c5c1f458506..2d55e8dad81 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ iframe_figures/ exports/* .idea .coverage +.scannerwork htmlcov openbb_terminal/config_plot.py openbb_terminal/config_terminal.py diff --git a/build/docker/build.sh b/build/docker/build.sh index 2b1451944ee..78f7bf4f57e 100644 --- a/build/docker/build.sh +++ b/build/docker/build.sh @@ -4,10 +4,11 @@ echo "Building docker release" # SET DEFAULT PARAMETERS IF EMPTY OPENBBTERMINAL_DOCKER_REGISTRY="${OPENBBTERMINAL_DOCKER_REGISTRY:-ghcr.io}" -OPENBBTERMINAL_DOCKER_GITHUB_REPOSITORY="${OPENBBTERMINAL_DOCKER_GITHUB_REPOSITORY:-openbb-finance}" -OPENBBTERMINAL_DOCKER_RELEASE_VERSION="${OPENBBTERMINAL_DOCKER_RELEASE_VERSION:-0.0.1}" +OPENBBTERMINAL_DOCKER_GITHUB_REPOSITORY="${OPENBBTERMINAL_DOCKER_GITHUB_REPOSITORY:-openbb-finance/openbbterminal}" +OPENBBTERMINAL_DOCKER_RELEASE_VERSION="${OPENBBTERMINAL_DOCKER_RELEASE_VERSION:-0.0.0}" # SET IMAGES NAMES -OPENBBTERMINAL_DOCKER_POETRY_IMAGE=${OPENBBTERMINAL_DOCKER_REGISTRY,,}/${OPENBBTERMINAL_DOCKER_GITHUB_REPOSITORY,,}/poetry:${OPENBBTERMINAL_DOCKER_RELEASE_VERSION,,} +OPENBBTERMINAL_DOCKER_POETRY_IMAGE=${OPENBBTERMINAL_DOCKER_REGISTRY,,}/${OPENBBTERMINAL_DOCKER_GITHUB_REPOSITORY,,}/openbb:${OPENBBTERMINAL_DOCKER_RELEASE_VERSION,,} +OPENBBTERMINAL_DOCKER_POETRY_IMAGE_LATEST=${OPENBBTERMINAL_DOCKER_REGISTRY,,}/${OPENBBTERMINAL_DOCKER_GITHUB_REPOSITORY,,}/openbb:latest # DISPLAY PARAMETERS echo "OPENBBTERMINAL_DOCKER_REGISTRY = $OPENBBTERMINAL_DOCKER_REGISTRY" @@ -17,4 +18,4 @@ echo "OPENBBTERMINAL_DOCKER_RELEASE_VERSION = $OPENBBTERMINAL_DOCKER_RELEASE_VER echo "OPENBBTERMINAL_DOCKER_POETRY_IMAGE = $OPENBBTERMINAL_DOCKER_POETRY_IMAGE" # Docker command to build image -docker build -f build/docker/poetry.dockerfile -t "${OPENBBTERMINAL_DOCKER_POETRY_IMAGE}" .
\ No newline at end of file +docker build -f build/docker/openbb.dockerfile -t "${OPENBBTERMINAL_DOCKER_POETRY_IMAGE}" -t "${OPENBBTERMINAL_DOCKER_POETRY_IMAGE_LATEST}" .
\ No newline at end of file diff --git a/build/docker/docker-compose.yaml b/build/docker/docker-compose.yaml index 013d3fb1278..823c4b694de 100644 --- a/build/docker/docker-compose.yaml +++ b/build/docker/docker-compose.yaml @@ -1,12 +1,12 @@ version: '3.8' services: - poetry: + openbb: environment: DISPLAY: host.docker.internal:0.0 volumes: - - ~/OpenBBUserData:/root/OpenBBUserData - - ~/.openbb_terminal:/root/.openbb_terminal + - ~/OpenBBUserData:/home/python/OpenBBUserData + - ~/.openbb_terminal:/home/python/.openbb_terminal platform: linux/amd64 - image: ghcr.io/openbb-finance/openbbterminal/poetry:2.0.0 + image: ghcr.io/openbb-finance/openbbterminal/openbb:2.0.0 stdin_open: true # docker run -i tty: true # docker run -t
\ No newline at end of file diff --git a/build/docker/openbb.dockerfile b/build/docker/openbb.dockerfile new file mode 100644 index 00000000000..ed683074cda --- /dev/null +++ b/build/docker/openbb.dockerfile @@ -0,0 +1,64 @@ +# SETUP PYTHON IMAGE +FROM --platform=linux/amd64 python:3.10-slim-bullseye as python + +LABEL org.opencontainers.image.source https://github.com/OpenBB-finance/OpenBBTerminal + +# SETUP DEBIAN IMAGE +FROM python as debian + +RUN apt-get update + +RUN apt-get -y install --no-install-recommends \ + gcc \ + g++ \ + make + +RUN apt-get -y install --no-install-recommends \ + git + +RUN apt-get -y install --no-install-recommends \ + curl \ + wget + +RUN apt-get -y install --no-install-recommends \ + libsm6 \ + libxt6 \ + libgl1-mesa-glx \ + libpng16-16 \ + python3-tk + +RUN apt-get clean + +RUN useradd --create-home --shell /bin/bash python + +USER python +WORKDIR /home/python + +# SETUP POETRY IMAGE +FROM debian as poetry + +ENV PATH="/home/python/.local/bin:${PATH}" + +RUN pip install --upgrade pip wheel +RUN pip install poetry==1.1.15 + +# SETUP OPENBB IMAGE +FROM poetry as repository + +COPY --chown=python:python pyproject.toml poetry.lock terminal.py ./ + +RUN mkdir openbb_terminal +COPY --chown=python:python openbb_terminal openbb_terminal + +RUN mkdir -p website/content/sdk/quickstart +COPY --chown=python:python ./website/content/sdk/quickstart/installation.md ./website/content/sdk/quickstart + +# SETUP OPENBB IMAGE +FROM repository as dependencies + +RUN poetry install --no-root --no-dev --extras optimization --extras prediction + +# SETUP OPENBB IMAGE +FROM dependencies as openbb + +CMD ["poetry", "run", "python", "terminal.py"]
\ No newline at end of file diff --git a/build/docker/poetry.dockerfile b/build/docker/poetry.dockerfile deleted file mode 100644 index 12c201cd82f..00000000000 --- a/build/docker/poetry.dockerfile +++ /dev/null @@ -1,73 +0,0 @@ -############################################### -# Base Image openbb poetry build -############################################### -FROM --platform=linux/amd64 python:3.9-slim-bullseye as python - -LABEL org.opencontainers.image.source https://github.com/OpenBB-finance/OpenBBTerminal - -# PIP_DEFAULT_TIMEOUT: Set the default timeout for pip - -ENV PIP_DEFAULT_TIMEOUT=100 \ - POETRY_VERSION=1.1.15 \ - POETRY_HOME="/opt/poetry" \ - POETRY_VIRTUALENVS_IN_PROJECT=true \ - POETRY_NO_INTERACTION=1 \ - PYSETUP_PATH="/opt" \ - VENV_PATH="/opt/.venv" - -# prepend poetry and venv to path -ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH" - -############################################### -# Builder Image openbb poetry build -############################################### - -FROM python as poetry-deps - -RUN apt-get update - -RUN apt-get install --no-install-recommends -y \ - curl \ - build-essential \ - unzip \ - git \ - python3-tk - -RUN curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh -RUN bash nodesource_setup.sh -RUN apt-get install -y nodejs -RUN apt-get -y autoremove -RUN apt-get clean -RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -# install poetry - respects $POETRY_VERSION & $POETRY_HOME -RUN curl -sSL https://install.python-poetry.org | python3 - - -# copy project requirement files here to ensure they will be cached. -WORKDIR $PYSETUP_PATH - -# Copy poetry files -COPY pyproject.toml website/content/sdk/quickstart/installation.md poetry.lock ./ - -RUN mkdir -p website/content/sdk/quickstart -RUN mkdir openbb_terminal -RUN mv installation.md ./website/content/sdk/quickstart -RUN touch openbb_terminal/__init__.py - -# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally -RUN poetry install --no-dev --no-interaction -E optimization -E prediction - -############################################### -# Production Image openbb poetry build -############################################### - -FROM python as poetry - -COPY --from=poetry-deps $PYSETUP_PATH $PYSETUP_PATH - -WORKDIR $PYSETUP_PATH -COPY . . - -RUN echo "OPENBB_LOGGING_APP_NAME=gst_docker" > .env - -CMD ["python", "terminal.py"] diff --git a/openbb_terminal/core/library/breadcrumb.py b/openbb_terminal/core/library/breadcrumb.py index 36c5e1d3a65..2f263b2e2ef 100644 --- a/openbb_terminal/core/library/breadcrumb.py +++ b/openbb_terminal/core/library/breadcrumb.py @@ -5,6 +5,8 @@ from openbb_terminal.core.library.metadata import Metadata from openbb_terminal.core.library.trail_map import TrailMap from openbb_terminal.core.library.operation import Operation +from openbb_terminal import feature_flags as obbff + # pylint: disable=import-outside-toplevel @@ -68,6 +70,8 @@ class MetadataBuilder: class Breadcrumb: + __version__ = obbff.VERSION + def __init__( self, metadata: Optional[Metadata] = None, @@ -100,7 +104,6 @@ class Breadcrumb: self._trail = trail self.__doc__ = metadata.docstring - if trail == "": BreadcrumbLogger() diff --git a/openbb_terminal/stocks/insider/openinsider_model.py b/openbb_terminal/stocks/insider/openinsider_model.py index f011cea68bd..1bcb09ff0da 100644 --- a/openbb_terminal/stocks/insider/openinsider_model.py +++ b/openbb_terminal/stocks/insider/openinsider_model.py @@ -1434,38 +1434,30 @@ def get_insider_types() -> Dict: @log_start_end(log=logger) -def get_print_insider_data(type_insider: str = "lcb", limit: int = 10): +def get_print_insider_data(type_insider: str = "lcb"): """Print insider data Parameters ---------- type_insider: str Insider type of data. Available types can be accessed through get_insider_types(). - limit: int - Limit of data rows to display Returns ------- data : pd.DataFrame Open insider filtered data """ - response = requests.get(f"http://openinsider.com/{d_open_insider[type_insider]}") - soup = BeautifulSoup(response.text, "html.parser") - table = soup.find("table", {"class": "tinytable"}) - - if not table: - console.print("No insider information found", "\n") + response = requests.get( + f"http://openinsider.com/{d_open_insider[type_insider]}", + headers={"User-Agent": "Mozilla/5.0"}, + ) + df = ( + pd.read_html(response.text)[-3] + .drop(columns=["1d", "1w", "1m", "6m"]) + .fillna("-") + ) + if df.empty: return pd.DataFrame() - - table_rows = table.find_all("tr") - - res = [] - for tr in table_rows: - td = tr.find_all("td") - row = [tr.text.strip() for tr in td if tr.text.strip()] - res.append(row) - - df = pd.DataFrame(res).dropna().head(n=limit) columns = [ "X", "Filing Date", @@ -1503,5 +1495,4 @@ def get_print_insider_data(type_insider: str = "lcb", limit: int = 10): df["Insider Name"] = df["Insider Name"].apply( lambda x: "\n".join(textwrap.wrap(x, width=20)) if isinstance(x, str) else x ) - return df diff --git a/openbb_terminal/stocks/insider/openinsider_view.py b/openbb_terminal/stocks/insider/openinsider_view.py index cd39aad16ca..5343b07a7b5 100644 --- a/openbb_terminal/stocks/insider/openinsider_view.py +++ b/openbb_terminal/stocks/insider/openinsider_view.py @@ -117,11 +117,11 @@ def print_insider_data(type_insider: str = "lcb", limit: int = 10, export: str = export: str Export data format """ - df = get_print_insider_data(type_insider, limit) + df = get_print_insider_data(type_insider) if not df.empty: print_rich_table( - df, + df.head(limit), headers=[x.title() for x in df.columns], show_index=False, title="Insider Data", @@ -132,7 +132,7 @@ def print_insider_data(type_insider: str = "lcb", limit: int = 10, export: str = ) if df.shape[1] == 13: - l_chars = [list(chars) for chars in df["X"].values] + l_chars = [list(chars) for chars in df["X"].values if chars != "-"] l_uchars = np.unique(list(itertools.chain(*l_chars))) for char in l_uchars: diff --git a/tests/openbb_terminal/stocks/insider/cassettes/test_openinsider_view/test_print_insider_data.yaml b/tests/openbb_terminal/stocks/insider/cassettes/test_openinsider_view/test_print_insider_data.yaml index 69a13f24a8b..35a8e9b2963 100644 --- a/tests/openbb_terminal/stocks/insider/cassettes/test_openinsider_view/test_print_insider_data.yaml +++ b/tests/openbb_terminal/stocks/insider/cassettes/test_openinsider_view/test_print_insider_data.yaml @@ -9,393 +9,372 @@ interactions: Connection: - keep-alive User-Agent: - - python-requests/2.27.1 + - Mozilla/5.0 method: GET uri: http://openinsider.com/insider-purchases response: body: string: !!binary | - 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 - |