diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 6c9f345..c82e141 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -27,7 +27,9 @@ jobs: - name: Set Environment run: | echo "SECRET_KEY=$(openssl rand -base64 32)" >> $GITHUB_ENV - echo "DEBUG=true" >> $GITHUB_ENV - name: Run Tests + env: + DEBUG: true + FMP_API_KEY: ${{ secrets.FMP_API_KEY }} run: | python -Wa stockhelper/manage.py test stockhelper/ -v 2 diff --git a/Dockerfile b/Dockerfile index cb8116a..97e9858 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,10 +4,15 @@ FROM python:3-alpine # Send python output in real-time without needing to be buffered ENV PYTHONUNBUFFERED=1 WORKDIR /code - COPY requirements.txt /code -# Install all python dependencies -RUN pip install -r requirements.txt + +# Install the packages needed to install psycopg2-binary: https://stackoverflow.com/a/47871121 +RUN \ + apk add --no-cache postgresql-libs && \ + apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev && \ + # Install all python dependencies + pip install -r requirements.txt --no-cache-dir && \ + apk --purge del .build-deps COPY . /code WORKDIR /code/stockhelper diff --git a/README.md b/README.md index 766b51d..acdbc9a 100644 --- a/README.md +++ b/README.md @@ -43,13 +43,13 @@ The Card model contains the word and definition for every financial term used th The app is split up into 4 sections: -- **Screener:** This is where users can filter stocks by country, price, sector, and exchange. +- **Screener:** This is where users can filter stocks by country, price, sector, and exchange. -- **Flashcards:** Users can view a list of financial terms used throughout the app. +- **Flashcards:** Users can view a list of financial terms used throughout the app. -- **Portfolio:** Users can view a list of stocks they've invested in. They start off with $10,000 and can view how their net worth changes each day. +- **Portfolio:** Users can view a list of stocks they've invested in. They start off with $10,000 and can view how their net worth changes each day. -- **Details:** This page shows stock information about a company as well as a detailed analysis of the stock price for short and long-term investments. This is also where users can trade stocks. +- **Details:** This page shows stock information about a company as well as a detailed analysis of the stock price for short and long-term investments. This is also where users can trade stocks. The app also features an authentication system so users can save their portfolio and balance whenever they're logged in. Logging in is required for the Portfolio and Details view. Using session cookies, the users' sessions can last up to 2 weeks. @@ -65,16 +65,16 @@ This project is hosted live at Heroku and can be accessed directly at [https://h ### Manual -After cloning this repo, run the following: +Before cloning this repo, you will need to obtain an API key from [Financial Modeling Prep](https://financialmodelingprep.com/developer/docs/). Then do the following: -1. Create a virtual environment, install all dependencies, and run the Django server: `./start.sh` or `source start.sh` (See step 3 for the differences.) +1. Create a virtual environment, install all dependencies, save the API key, and run the Django server: `./start-local.sh YOUR_API_KEY` or `source start-local.sh YOUR_API_KEY` (See step 3 for the differences.) ON future visits, the API key doesn't need to be provided. 2. Open `localhost:8000` in your browser. -3. When done, press `CTRL/CMD-C` to stop the Django server. If you used `./start.sh`, you will return to your current shell. But if you used `source start.sh`, you will need to deactivate the virtual environment: `deactivate` +3. When done, press `CTRL/CMD-C` to stop the Django server. If you used `./start-local.sh`, you will return to your current shell. But if you used `source start-local.sh`, you will need to deactivate the virtual environment: `deactivate` ### Docker -Make sure [Docker](https://www.docker.com/products/docker-desktop) and [Docker Compose](https://docs.docker.com/compose/install/) are installed. Then run the following: +Make sure [Docker](https://www.docker.com/products/docker-desktop) and [Docker Compose](https://docs.docker.com/compose/install/) are installed. And make sure to obtain an API key from [Financial Modeling Prep](https://financialmodelingprep.com/developer/docs/). Then do the following: -1. Start up a container for this app: `docker-compose up -d` +1. Pass in the API key and start up a container for this app: `./start-docker.sh YOUR_API_KEY`. On future visits, the container can be spun up directly using `./start-docker.sh` or `docker-compose up -d`. 2. Once everything's installed, open `localhost:8000` in your browser. 3. When done, shut down and clean up the container: `docker-compose down` diff --git a/docker-compose.yml b/docker-compose.yml index 603ce5c..1cf86d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,12 @@ version: "3.9" services: web: build: . - command: python manage.py runserver 0.0.0.0:8000 + command: > + sh -c "python manage.py migrate && + python manage.py loaddata cards.json && + python manage.py runserver 0.0.0.0:8000" + env_file: + - ./stockhelper/.env volumes: - .:/code ports: diff --git a/start-docker.sh b/start-docker.sh new file mode 100644 index 0000000..b9fbb35 --- /dev/null +++ b/start-docker.sh @@ -0,0 +1,29 @@ +#!/bin/bash +ENV="stockhelper/.env" + +# Populate the .env file if it doesn't exist +if [ ! -e "$ENV" ]; then + # Throw an error if the user didn't pass in their API key + if [ $# == 0 ]; then + echo "Error: No API key provided" + exit 1 + fi + + echo $'Generating a secret key...' + if type openssl > /dev/null; then + echo "SECRET_KEY=$(openssl rand -base64 32)" > "$ENV" + else + echo "SECRET_KEY=$(head -c 32 /dev/urandom | base64)" > "$ENV" + fi + + echo $'\nTurning on DEBUG mode...' + echo "DEBUG=true" >> "$ENV" + + echo $'\nSaving the API key...' + echo "FMP_API_KEY=$1" >> "$ENV" +else + echo "The .env file is already created." +fi + +# Spin up a docker container +docker-compose up -d diff --git a/start-local.sh b/start-local.sh new file mode 100644 index 0000000..e46b64f --- /dev/null +++ b/start-local.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Execute all the necessary steps to run the project + +# Check if the virtual environment exists +VENV="venv" + +if [ ! -d "$VENV" ]; then + echo "1. Creating a folder for the virtual environment..." + python3 -m venv "$VENV" +else + echo "1. The virtual environment already exists." +fi + +# $'strings' allow for escaped characters +echo $'\n2. Activating the virtual environment...' +source "$VENV"/bin/activate + +echo $'\n3. Installing all python dependencies...' +pip3 install -r requirements.txt + +echo $'\n4. Heading into the stockhelper directory...' +cd stockhelper + +if [ ! -e .env ]; then + # Throw an error if the user didn't pass in their API key + if [ $# == 0 ]; then + echo "Error: No API key provided" + exit 1 + fi + + echo $'\n5. Generating a secret key...' + + if type openssl > /dev/null; then + echo "SECRET_KEY=$(openssl rand -base64 32)" > .env + else + echo "SECRET_KEY=$(head -c 32 /dev/urandom | base64)" > .env + fi + + echo $'\n6. Turning on DEBUG mode...' + echo "DEBUG=true" >> .env + + echo $'\n7. Saving the API key...' + echo "FMP_API_KEY=$1" >> .env +else + echo $'\n5. The secret key already exists.' + echo $'\n6. DEBUG mode is on.' + echo $'\n7. The API key is already stored.' +fi + +echo $'\n8. Creating the SQLite database...' +python3 manage.py migrate + +echo $'\n9. Loading the flashcards data...' +python3 manage.py loaddata cards.json + +echo $'\n10. Running the Django server...' +python3 manage.py runserver diff --git a/start.sh b/start.sh deleted file mode 100755 index da8c457..0000000 --- a/start.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Execute all the necessary steps to run the project - -# Check if the virtual environment exists -VENV="venv" - -if [ ! -d "$VENV" ]; then - echo "1. Creating a folder for the virtual environment..." - python3 -m venv "$VENV" -else - echo "1. The virtual environment already exists." -fi - -# $'strings' allow for escaped characters -echo $'\n2. Activating the virtual environment...' -source "$VENV"/bin/activate - -echo $'\n3. Installing all python dependencies...' -pip3 install -r requirements.txt - -echo $'\n4. Heading into the stockhelper directory...' -cd stockhelper - -if [ ! -e .env ]; then - echo $'\n5. Generating a secret key...' - - if type openssl > /dev/null; then - echo "SECRET_KEY=$(openssl rand -base64 32)" > .env - else - echo "SECRET_KEY=$(head -c 32 /dev/urandom | base64)" > .env - fi - - echo $'\n6. Turning on DEBUG mode...' - echo "DEBUG=true" >> .env -else - echo $'\n5. The secret key already exists.' - echo $'\n6. DEBUG mode is on.' -fi - -echo $'\n7. Creating the SQLite database...' -python3 manage.py migrate - -echo $'\n8. Loading the flashcards data...' -python3 manage.py loaddata cards.json - -echo $'\n9. Running the Django server...' -python3 manage.py runserver diff --git a/stockhelper/stockapp/api.py b/stockhelper/stockapp/api.py index 90fe4a3..4bd9d54 100644 --- a/stockhelper/stockapp/api.py +++ b/stockhelper/stockapp/api.py @@ -1,8 +1,16 @@ +import dotenv +import os +from pathlib import Path import requests from time import sleep +BASE_DIR = Path(__file__).resolve().parent.parent +dotenv_file = os.path.join(BASE_DIR, ".env") +if os.path.isfile(dotenv_file): + dotenv.load_dotenv(dotenv_file) + # Data provided by Financial Modeling Prep: https://financialmodelingprep.com/developer/docs/ -API_KEY = "174c8948d0e48bad0418e1fe49d72e15" +API_KEY = os.environ["FMP_API_KEY"] FMP = "https://financialmodelingprep.com" RETRY_LIMIT = 5