While checking my code for PSET8 (Finance) with check50 I ran into the error "check50 ran into an error while running checks!" and with a note in the end stating "Contact [email protected] with the URL of this check!"
The server and code runs fine when starting up with "flask run" without any issues when registering, logging in and using the different pages. I reached out to [email protected] twice but without any reply.
Is anyone able to spot what might be wrong with my code? Thanks!
https://cs50.me/checks/bd4bae95f384b2ca4ecc17708ed498d7b2c18fa3
application.py
import os
from cs50 import SQL
from flask import Flask, flash, jsonify, redirect, render_template, request, session
from flask_session import Session
from tempfile import mkdtemp
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from werkzeug.security import check_password_hash, generate_password_hash
from helpers import apology, login_required, lookup, usd
# Configure application
app = Flask(__name__)
# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Ensure responses aren't cached
@app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
# Custom filter
app.jinja_env.filters["usd"] = usd
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
# Configure CS50 Library to use SQLite database
db = SQL("sqlite:///finance.db")
@app.route("/")
@login_required
def index():
"""Show portfolio of stocks"""
# DB extract to populate 1st table with balance and total share value
balance_extract = db.execute("SELECT cash FROM users WHERE id = :user_id", user_id=session["user_id"])
balance = round(balance_extract[0]["cash"], 2)
# To be used further below to capture total share value
shares_value = 0
# DB extract to populate 2nd table with share details
shares_balance = db.execute("""SELECT share, MIN(price) AS low_purchase_price, SUM(split_balance) AS amount
FROM (SELECT share, price,
CASE WHEN method = 'sell' THEN -amount
WHEN method = 'buy' THEN amount
END AS split_balance
FROM transactions
WHERE user_id = :user_id)
GROUP by share
HAVING amount > 0""", user_id=session["user_id"])
# Modifying each element in shares to have the current price and current value as well
for s in shares_balance:
current_stat = lookup(s["share"])
current_price = round(current_stat["price"], 2)
s['name'] = current_stat["name"]
s['current_price'] = current_price
s['current_value'] = round(s["amount"] * current_price, 2)
s['purchase_indicator'] = current_price - s['low_purchase_price']
shares_value += s['current_value']
# Formatting the current value and price correctly in the loop
s['current_price'] = usd(s['current_price'])
s['current_value'] = usd(s['current_value'])
s['low_purchase_price'] = usd(s['low_purchase_price'])
# print(shares_balance)
# print(shares_value)
# Updates the cash for the user to be shown on the nav bar
session["user_balance"] = usd(balance)
#print(session)
print(shares_balance)
total_value = shares_value + balance
return render_template("index.html", balance=usd(balance), shares_value=usd(shares_value), total_value=usd(total_value), shares=shares_balance)
@app.route("/buy", methods=["GET", "POST"])
@login_required
def buy():
"""Buy shares of stock"""
# When submitting a purchase
if request.method == "POST":
# Defing variables for quick access later
symbol = (request.form.get("symbol")).upper()
amount = int(request.form.get("shares"))
# TODO: Server side error messages if input is not valid
if not symbol or not amount:
return apology("Please enter a symbol and/or shares")
if amount <= 0:
return apology("Please enter a positive number of shares")
result = lookup(symbol)
# If JSON query is unsuccessful for some reason
if not result:
return apology("This symbol does not exist")
# Ensures that there is enough credit for the user to complete the transaction
current_balance = db.execute("SELECT cash FROM users WHERE id = :user_id", user_id=session["user_id"])
new_balance = float(current_balance[0]["cash"]) - (float(result["price"]) * amount)
if new_balance < 0:
return apology("You don't have enough cash to complete this transaction")
# Creates a transaction in the the transaction table. ID and DATETIME are automatically populated
db.execute("""INSERT INTO transactions (user_id, share, amount, price, method)
VALUES (:user_id, :share, :amount, :price, 'buy')""", user_id=session["user_id"], share=symbol, amount=amount, price=result["price"])
# Updates the user's balance
db.execute("UPDATE users SET cash = :new_balance WHERE id = :user_id", new_balance=new_balance, user_id=session["user_id"])
# Updates the cash for the user to be shown on the nav bar
session["user_balance"] = usd(new_balance)
return redirect("/buy")
# User reached route via GET (as by clicking a link or via redirect)
else:
#balance = session["user_id"]
balance_extract = db.execute("SELECT cash FROM users WHERE id = :user_id", user_id=session["user_id"])
# rows[0]["id"]
balance = usd(balance_extract[0]["cash"])
return render_template("buy.html", balance=balance)
@app.route("/check", methods=["GET"])
def check():
"""Return true if username available, else false, in JSON format"""
user = request.args.get('username')
if len(user) <= 0:
return jsonify("Username must be at least 1 character")
rows = db.execute("SELECT * FROM users WHERE username = :user", user=user)
if not rows:
return jsonify(True)
return jsonify(False)
@app.route("/history")
@login_required
def history():
"""Show history of transactions"""
transactions = db.execute("""SELECT share, amount, price, method, datetime
FROM transactions
WHERE user_id = :user_id""", user_id=session["user_id"])
# Making the table more user friendly
for t in transactions:
t["method"] = 'Bought' if t["method"] == 'buy' else 'Sold'
t["price"] = usd(t["price"])
return render_template("history.html", transactions=transactions)
@app.route("/login", methods=["GET", "POST"])
def login():
"""Log user in"""
# Forget any user_id - CLEARS THE FLASHING METHOD
session.clear()
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# Ensure username was submitted
if not request.form.get("username"):
return apology("must provide username", 403)
# Ensure password was submitted
elif not request.form.get("password"):
return apology("must provide password", 403)
# Query database for username
rows = db.execute("SELECT * FROM users WHERE username = :username",
username=request.form.get("username"))
# Ensure username exists and password is correct
if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
return apology("invalid username and/or password", 403)
# Remember which user has logged in
session["user_id"] = rows[0]["id"]
# Redirect user to home page
flash('Login successfull')
return redirect("/")
# User reached route via GET (as by clicking a link or via redirect)
else:
return render_template("login.html")
@app.route("/logout")
def logout():
"""Log user out"""
# Forget any user_id
session.clear()
flash('You are successfully logged out')
# Redirect user to login form
return redirect("/")
@app.route("/quote", methods=["GET", "POST"])
@login_required
def quote():
"""Get stock quote."""
# When submitting a symbol
if request.method == "POST":
result = lookup(request.form.get("symbol"))
# If JSON query is unsuccessful for some reason
if not result:
return apology("This symbol does not exist")
return render_template("quoted.html", result=result)
# User reached route via GET (as by clicking a link or via redirect)
else:
return render_template("quote.html")
@app.route("/register", methods=["GET", "POST"])
def register():
"""Register user"""
if request.method == "POST":
# Ensures that all fields are not blank
if not request.form.get("username") or not request.form.get("password") or not request.form.get("confirmation"):
return apology("username/password cannot be blank", 403)
# Checks whether the username already exists in the database
rows = db.execute("SELECT * FROM users WHERE username = :username",
username=request.form.get("username"))
if len(rows) >= 1:
return apology("the username is already taken", 403)
# Ensures that password and confirmation matches
if request.form.get("password") != request.form.get("confirmation"):
return apology("passwords don't match", 403)
# Inserts the user into the database and hashes the password with generate_password_hash
db.execute("INSERT INTO 'users' ('username','hash') VALUES (:username,:hashedpwd)",
username=request.form.get("username"), hashedpwd=generate_password_hash(request.form.get("password")))
# Auto-login
# retrieving id of the username to be stored in the session for auto-login
userid = db.execute("SELECT * FROM users WHERE username = :username",
username=request.form.get("username"))
#print(userid)
#print(userid[0]["id"])
session["user_id"] = userid[0]["id"]
flash('You are successfully registered!')
# Sends the user to the index that provides a transaction overview
return redirect("/")
# User reached route via GET (as by clicking a link or via redirect)
else:
return render_template("register.html")
@app.route("/sell", methods=["GET", "POST"])
@login_required
def sell():
"""Sell shares of stock"""
# When submitting a purchase
if request.method == "POST":
# Defining variables for quick access below
symbol = (request.form.get("symbol")).upper()
amount = int(request.form.get("shares"))
# Server side error messages if input is not valid
if not symbol or not amount:
return apology("Please enter a symbol and/or amount of shares")
if int(amount) <= 0:
return apology("Please enter a positive number of shares")
result = lookup(symbol)
# If JSON query is unsuccessful for some reason
if not result:
return apology("This symbol does not exist")
# Quering database to see if the share and the amount is available
share_query = db.execute("""SELECT share, SUM(split_balance) as balance FROM
(
SELECT share,
CASE WHEN method = 'sell' THEN -amount
WHEN method = 'buy' THEN amount
END AS split_balance
FROM transactions
WHERE user_id = :user_id
)
WHERE share = :symbol
GROUP by share
HAVING balance >= :amount""", user_id=session["user_id"], symbol=symbol, amount=amount)
#print("SHARE QUERY")
#print(share_query)
if not share_query:
return apology("You don't own this share or have enough shares to sell")
# Inserts the selling price onto the user's account
current_balance = db.execute("SELECT cash FROM users WHERE id = :user_id", user_id=session["user_id"])
new_balance = float(current_balance[0]["cash"]) + (float(result["price"]) * amount)
# Creates a transaction in the the transaction table. ID and DATETIME are automatically populated
db.execute("""INSERT INTO transactions (user_id, share, amount, price, method)
VALUES (:user_id, :share, :amount, :price, 'sell')""", user_id=session["user_id"], share=symbol, amount=amount, price=result["price"])
# Updates the user's balance
db.execute("UPDATE users SET cash = :new_balance WHERE id = :user_id", new_balance=new_balance, user_id=session["user_id"])
# Updates the cash for the user to be shown on the nav bar
session["user_balance"] = usd(new_balance)
return redirect("/sell")
# User reached route via GET (as by clicking a link or via redirect)
else:
# TODO: Create dropdown instead after this implementation that has the users current stocks
#balance = session["user_id"]
balance_extract = db.execute("SELECT cash FROM users WHERE id = :user_id", user_id=session["user_id"])
# rows[0]["id"]
balance = usd(balance_extract[0]["cash"])
return render_template("sell.html", balance=balance)
# Used for jquery to fetch symbol data
@app.route('/_retrieve_symbol_data')
def retrieve_symbol_data():
result = lookup(request.args.get('symbol', 0, type=str))
# If none then hand the following back
if not result:
return jsonify(result="")
return jsonify(result=result)
def errorhandler(e):
"""Handle error"""
if not isinstance(e, HTTPException):
e = InternalServerError()
return apology(e.name, e.code)
# Listen for errors
for code in default_exceptions:
app.errorhandler(code)(errorhandler)