Skip to content

Commit

Permalink
Replaced django with streamlit and added Docker
Browse files Browse the repository at this point in the history
  • Loading branch information
wleong1 committed Apr 7, 2024
1 parent 8e48134 commit 9b46591
Show file tree
Hide file tree
Showing 11 changed files with 307 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
python-version: 3.x
- name: Install Dependencies
run: |
pip install pytest requests pandas yfinance
pip install pytest requests pandas yfinance psycopg2-binary
- name: pytest
run: |
pytest tests/
Empty file modified .gitignore
100644 → 100755
Empty file.
28 changes: 28 additions & 0 deletions src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
FROM ubuntu:22.04

# set a directory for the app
WORKDIR /usr/src/

# copy all the files to the container
COPY . .

# install dependencies
RUN apt-get update
RUN apt-get install -y python3
RUN apt install -y python3-pip
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install flask

# Copy the shell script into the image
COPY entrypoint.sh /usr/local/bin/entrypoint.sh

# Make the shell script executable
RUN chmod +x /usr/local/bin/entrypoint.sh

# Set the entrypoint
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

# EXPOSE 8000

# CMD cd django-stock-tracker && python3 manage.py runserver 0.0.0.0:8000 Can run this in the terminal
# but need to specify docker run -it -p 8888:8000 stock
3 changes: 3 additions & 0 deletions src/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cd ..
python3 src/flask_endpoints.py
105 changes: 105 additions & 0 deletions src/flask_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""This module creates the endpoints to be called by streamlit."""

from typing import Union, Any
from flask import Flask, jsonify, request # pylint: disable=E0401
import pandas as pd
from src.model import Model
from src.live_price_display import LivePriceDisplay # type: ignore[import-untyped]
from src.news_display import NewsDisplay
import sys # pylint: disable=C0411
import os # pylint: disable=C0411
sys.path.append(os.getcwd())

models: Model = Model()
news_disp: NewsDisplay = NewsDisplay()
price_disp: LivePriceDisplay = LivePriceDisplay()
all_data: Union[pd.DataFrame, Any] = models.process_data()

app = Flask(__name__)

def update_graph(company: str) -> str:
"""
This method gets the data from the selected company.
Args:
company: The ticker symbol of the company
Returns:
chart_data: A DataFrame containing required information of all companies
"""
raw_data: pd.Series = all_data[company]
data: dict = {
"date": raw_data["trade_date"],
"close": raw_data["close"]
}
df: pd.DataFrame = pd.DataFrame(data) # pylint: disable=C0103
chart_data: str = df.to_json()
return chart_data

def update_price(company: str) -> Union[float, str]:
"""
Returns the price of the selected company using core modules.
Args:
company_name: The ticker symbol of the company.
Returns:
The most recent price.
"""
price: Union[float, str] = price_disp.display_final_price_yf(company)
return price

def update_news(company: str) -> list:
"""
Get the formatted news.
Args:
company: The ticker symbol of the company
Returns:
news: The most recent five articles
"""
news: list = news_disp.format_news_django(company)
return news

@app.route("/model/generate_company_list", methods=["GET"])
def generate_company_list():
"""
Returns the list of companies.
Args:
None.
Returns:
ticker_list: The list of companies in json format.
"""
ticker_list: list
ticker_list, _ = models.generate_company_list()
return jsonify(ticker_list)

@app.route("/update_data", methods=["GET", "POST"])
def update_data():
"""
Returns the data of the selected company.
Args:
None.
Returns:
response: The data for the selected company in json.
"""
response: dict = {}
data: dict = request.get_json()
company: str = data["company"]
print(f"Received company: {company}")
processed_price: Union[float, str] = update_price(company)
processed_news: list = update_news(company)
processed_chart_data: str = update_graph(company)

response["price"] = processed_price
response["news"] = processed_news
response["graph"] = processed_chart_data
return jsonify(response)

if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
10 changes: 5 additions & 5 deletions src/live_price_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ def display_final_price_av(company_name: str) -> Union[str, dict, Any]:
Returns a the price using Alpha Vantage.
Args:
company_name: The ticker symbol of the company
company_name: The ticker symbol of the company.
Returns:
The most recent price in string
The most recent price.
"""
try:
# Gets last available price by default
Expand Down Expand Up @@ -57,13 +57,13 @@ def display_final_price_av(company_name: str) -> Union[str, dict, Any]:
@staticmethod
def display_final_price_yf(company_name: str) -> Union[float, str]:
"""
Returns a the price using Yahoo Finance.
Returns the price of the selected company using Yahoo Finance.
Args:
company_name: The ticker symbol of the company
company_name: The ticker symbol of the company.
Returns:
The most recent price in string
The most recent price.
"""
# Uncomment below for full company names in selection rather than ticker symbols.
# conn = psycopg2.connect(database = "stocks", user='postgres', password='123456')
Expand Down
8 changes: 6 additions & 2 deletions src/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def generate_company_list() -> Tuple[list, list]:
:return: (list) A list of companies.
"""
conn = psycopg2.connect(
host="172.19.0.2",
host="stocks-postgres",
database="stocks",
user="postgres",
password="123456",
Expand Down Expand Up @@ -89,7 +89,11 @@ def process_data(self) -> Union[pd.DataFrame, str]:
companies_list: Tuple[list, list] = self.generate_company_list()
companies_data: dict = {}
conn: psycopg2.extensions.connection = psycopg2.connect(
database="stocks", user="postgres", password="123456"
host="stocks-postgres",
database="stocks",
user="postgres",
password="123456",
port="5432"
)
number_of_companies: int = len(companies_list[0])
for company_idx in range(1, number_of_companies + 1):
Expand Down
49 changes: 49 additions & 0 deletions src/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
appdirs==1.4.4
asgiref==3.7.2
beautifulsoup4==4.12.3
certifi==2023.5.7
charset-normalizer==3.1.0
coverage==7.4.3
Django==4.1.12
django-admin-volt==1.0.10
exceptiongroup==1.1.1
frozendict==2.4.0
gunicorn==21.2.0
html5lib==1.1
idna==3.4
iniconfig==2.0.0
install==1.3.5
jmespath==1.0.1
lxml==4.9.3
multitasking==0.0.11
mypy==1.8.0
mypy-extensions==1.0.0
numpy==1.26.4
packaging==23.1
pandas==2.2.1
pandas-stubs==2.2.0.240218
peewee==3.17.1
pluggy==1.0.0
psycopg2-binary==2.9.9
pytest==7.3.1
pytest-cov==4.1.0
python-dateutil==2.8.2
python-dotenv==1.0.0
pytz==2023.3
requests==2.31.0
ruff==0.3.0
six==1.16.0
soupsieve==2.5
sqlparse==0.4.4
tomli==2.0.1
types-openpyxl==3.1.0.20240301
types-psycopg2==2.9.21.20240311
types-pytz==2024.1.0.20240203
types-requests==2.31.0.20240218
typing_extensions==4.8.0
tzdata==2023.3
urllib3==2.0.2
utils==1.0.1
webencodings==0.5.1
whitenoise==6.5.0
yfinance==0.2.37
22 changes: 22 additions & 0 deletions streamlit/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# app/Dockerfile

FROM python:3.10src/requirements.txt-slim

WORKDIR /app

COPY . .

RUN apt-get update && apt-get install -y \
build-essential \
curl \
software-properties-common \
git \
&& rm -rf /var/lib/apt/lists/*

RUN pip3 install -r requirements.txt

EXPOSE 8501

HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health

ENTRYPOINT ["streamlit", "run", "streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
42 changes: 42 additions & 0 deletions streamlit/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
appdirs==1.4.4
asgiref==3.7.2
beautifulsoup4==4.12.3
certifi==2023.5.7
charset-normalizer==3.1.0
coverage==7.4.3
exceptiongroup==1.1.1
frozendict==2.4.0
gunicorn==21.2.0
html5lib==1.1
idna==3.4
iniconfig==2.0.0
install==1.3.5
jmespath==1.0.1
lxml==4.9.3
multitasking==0.0.11
numpy==1.26.4
packaging==23.1
pandas==2.2.1
pandas-stubs==2.2.0.240218
peewee==3.17.1
plotly==5.20.0
pluggy==1.0.0
python-dateutil==2.8.2
python-dotenv==1.0.0
pytz==2023.3
requests==2.31.0
ruff==0.3.0
six==1.16.0
soupsieve==2.5
sqlparse==0.4.4
streamlit==1.33.0
tomli==2.0.1
types-openpyxl==3.1.0.20240301
types-pytz==2024.1.0.20240203
types-requests==2.31.0.20240218
typing_extensions==4.8.0
tzdata==2023.3
urllib3==2.0.2
utils==1.0.1
webencodings==0.5.1
whitenoise==6.5.0
46 changes: 46 additions & 0 deletions streamlit/streamlit_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""This module configures the streamlit web app."""
import json
import requests
import plotly.express as px # type: ignore[import-untyped] # pylint: disable=E0401
import pandas as pd
import streamlit as st # type: ignore[import-untyped] # pylint: disable=E0401


company_list_response: requests.Response = requests.get(
"http://core-modules:5000/model/generate_company_list"
)
company_list: list = company_list_response.json()
st.write("Hello, let's learn more about a company together!")
company = st.selectbox("Pick a company", [None] + company_list)
st.write("You selected:", company)

if company:
payload: dict = {"company": company}
update_response: requests.Response = requests.post(
"http://core-modules:5000/update_data", json=payload
)
price: float = update_response.json()["price"]
news: list = update_response.json()["news"]
st.sidebar.write(f"{company}'s most recent price: {price}")

news_container = st.sidebar.container()
for article in news:
news_container.markdown(f"- [{article['title']}]({article['url']})")

chart_data: dict = json.loads(update_response.json()["graph"])
date: dict
close: dict
date, close = chart_data["date"].values(), chart_data["close"].values()
df = pd.DataFrame(close, date)

fig = px.line(df)

fig.update_layout(
title=company,
xaxis_title='Date',
yaxis_title='Close',
width=800,
height=600
)

st.plotly_chart(fig)

0 comments on commit 9b46591

Please sign in to comment.