1

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

Screenshot of cs50.me

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)
0

Did it also mention "Check50 is taking longer than normal!"? From the FAQs section of the spec:

check50 is taking longer than normal!

If you see this, make sure to remove or comment out any print statements from application.py and try check50 again.

5
  • Hi DinoCoderSaurus. It didn't mention that when I ran check50 nor submit50. I saw that one in the FAQ section and removed all print statements but without any luck :( – ax-ab Apr 24 '19 at 14:28
  • The code above does have a print statement. – DinoCoderSaurus Apr 24 '19 at 14:31
  • You are right :) After your reply I tried to comment it out and ran check50 again but again it failed with the same error message. It should be alright to keep all the comments in my code from a check50 runtime point of view? – ax-ab Apr 24 '19 at 15:35
  • I think the problem is elsewhere, suspect layout.html or index.html. I ran this application.py through check50 (after commenting the errant print; IDE version 158) and the "application starts up" test passed. The test is a simple sending GET request to / checking that status code 200 is returned... . This comment in the code # Updates the cash for the user to be shown on the nav bar shifted focus to layout||index.html. It looks to me like a problem in the check50 system (based on the deprecated warning) and something in your app is provoking it. – DinoCoderSaurus Apr 24 '19 at 16:55
  • okay. I will look into that and maybe try to simplify my personal touches. Thanks for your helpfulness and super quick replies! – ax-ab Apr 24 '19 at 18:49
0

The issue was caused by package incompatibility between 'flask-session' and 'werkzeug' according to sysadmins@cs50.harvard.edu. They have deployed a fix which has solved my issue. My solution now fully passes check50 and submit50.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .