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 sysadmins@cs50.harvard.edu 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 sysadmins@cs50.harvard.edu 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)