0

I would like to ask for your help please with an error I keep getting on my code for the Finance problem. I can't seem to find a solution for it :/

I keep receiving:

:) application.py exists
:) application starts up
:) register page has all required elements
:) registering user succeeds
:) registration with an empty field fails
:) registration with password mismatch fails
:) registration rejects duplicate username
:) login page has all required elements
:( logging in as registered user succceeds
expected status code 200, but got 400

This is my code for application.py:

import os

from cs50 import SQL
from flask import Flask, flash, redirect, render_template, request, session, url_for
from flask_session import Session
from tempfile import mkdtemp
from werkzeug.exceptions import default_exceptions
from werkzeug.security import check_password_hash, generate_password_hash

from helpers import apology, login_required, lookup, usd

# Configure application
app = Flask(__name__)

# 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"""

    # Query database stocks database for total number of shares of each stock owned by user
    securities = db.execute("SELECT symbol, SUM(shares) FROM stock WHERE userid = :userid GROUP BY symbol",
                            userid=session["user_id"])

    # Add current price and total value to each row; calculate total securities value
    securities_value = 0.0
    for security in securities:
        # try 10 times to get quote -- often no value is returned
        for i in range(0, 10):
            quote_value = lookup(security["symbol"])
            if not quote_value == None:
                break

        if quote_value == None:
            security["current_price"] = 0
        else:
            security["current_price"] = round(float(quote_value["price"]), 2)

        security["total_value"] = round(security["current_price"] * int(security["SUM(shares)"]), 2)
        securities_value += security["total_value"]

    # Query database for current cash balance
    rows = db.execute("SELECT * FROM users WHERE id = :userid", userid=session["user_id"])
    if not rows:
        return apology("error retreiving cash balance", 400)
    cash_balance = rows[0]["cash"]

    # Calculate total portfolio value
    portfolio_value = round(securities_value + cash_balance, 2)

    return render_template("index.html", securities=securities, securities_value=securities_value, cash_balance=cash_balance,
                           portfolio_value=portfolio_value)

@app.route("/buy", methods=["GET", "POST"])
@login_required
def buy():
    """Buy shares of stock"""

    #Make sure user arrives via post
    if request.method == "POST":

        stock = lookup(request.form.get("symbol"))

        if stock == None:
            return apology("invalid ticker symbol")

        try:
            shares = int(request.form.get("shares"))
        except:
            return apology("must be a number")

        #Make sure number of shres is positive
        if shares < 0:
            return apology("input positive number")

        rows = db.execute("SELECT cash FROM users WHERE id = :user_id",
                            user_id=session["user_id"])

        available_cash = rows[0]["cash"]

        price = stock["price"]

        total_price = shares * price

        if available_cash < total_price:
            return apology("insufficient funds")

        db.execute("UPDATE users SET cash = cash - :total_price WHERE id = :user_id", total_price=total_price, user_id=session["user_id"])
        db.execute("INSERT INTO 'transactions' (user_id, symbol, shares, price) VALUES (:user_id, :symbol, :shares, :price)", user_id=session["user_id"], symbol=request.form.get("symbol"), shares=shares, price=price)

        return redirect("/")

    else:
        return render_template("buy.html")

@app.route("/history")
@login_required
def history():
    """Show history of transactions"""

    transactions = db.execute(
        "SELECT symbol, shares, price_per_share, created_at FROM transactions WHERE user_id = :user_id ORDER BY created_at ASC", user_id=session["user_id"])

    return render_template("history.html", transactions=transactions)


@app.route("/funds/add", methods=["GET", "POST"])
@login_required
def add_funds():

    if request.method == "POST":
        try:
            amount = float(request.form.get("amount"))
        except:
            return apology("amount must be a real number", 400)

        db.execute("UPDATE users SET cash = cash + :amount WHERE id = :user_id", user_id=session["user_id"], amount=amount)

        return redirect(url_for("index"))
    else:
        return render_template("add_funds.html")


@app.route("/change_password", methods=["GET", "POST"])
@login_required
def change_password():
    """Allow user to change her password"""

    if request.method == "POST":

        # Ensure current password is not empty
        if not request.form.get("current_password"):
            return apology("must provide current password", 400)

        # Query database for user_id
        rows = db.execute("SELECT hash FROM users WHERE id = :user_id", user_id=session["user_id"])

        # Ensure current password is correct
        if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("current_password")):
            return apology("invalid password", 400)

        # Ensure new password is not empty
        if not request.form.get("new_password"):
            return apology("must provide new password", 400)

        # Ensure new password confirmation is not empty
        elif not request.form.get("new_password_confirmation"):
            return apology("must provide new password confirmation", 400)

        # Ensure new password and confirmation match
        elif request.form.get("new_password") != request.form.get("new_password_confirmation"):
            return apology("new password and confirmation must match", 400)

        # Update database
        hash = generate_password_hash(request.form.get("new_password"))
        rows = db.execute("UPDATE users SET hash = :hash WHERE id = :user_id", user_id=session["user_id"], hash=hash)

        # Show flash
        flash("Changed!")

    return render_template("change_password.html")


@app.route("/login", methods=["GET", "POST"])
def login():
    """Log user in."""

    # forget any user_id
    session.clear()

    # if 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", 400)

        # ensure password was submitted
        elif not request.form.get("password"):
            return apology("must provide password", 400)

        # 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(request.form.get("password"), rows[0]["hash"]):
            return apology("invalid username and/or password", 400)

        # remember which user has logged in
        session["user_id"] = rows[0]["id"]

        # redirect user to home page
        return redirect(url_for("index"))

    # else if 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()

    # Redirect user to login form
    return redirect(url_for("index"))


@app.route("/quote", methods=["GET", "POST"])
@login_required
def quote():
    """Get stock quote."""

    if request.method == "POST":

        #Get stock information
        stock = lookup(request.form.get("symbol"))

        #Validate ticker symbol
        if stock == None:
            return apology("invalid ticker symbol")

        return render_template("quoted.html", stock=stock)

    else:
        return render_template("quote.html")


@app.route("/register", methods=["GET", "POST"])
def register():
    """Register user"""

    #User reached route via POST
    if request.method == "POST":

        #User entered username
        if not request.form.get("username"):
            return apology("must provide username")

        #User entered password
        elif not request.form.get("password") or not request.form.get("confirmation"):
            return apology("must provide password")

        #Confirm password matches
        elif request.form.get("password") != request.form.get("confirmation"):
            return apology("password must match")

        #Hash the password
        hash = generate_password_hash(request.form.get("password"))

        #Insert user into database
        result = db.execute("INSERT INTO users (username, hash) VALUES (:username, :hash)",
                            username=request.form.get("username"), hash=hash)

        if not result:
            return apology("username already exists")
        else:
            return redirect(url_for("index"))

        #Remember user
        session["user_id"] = result

    #User reached route via GET
    else:
        return render_template("register.html")

@app.route("/sell", methods=["GET", "POST"])
@login_required
def sell():
    """Sell shares of stock"""

    if request.method == "POST":

        stock = lookup(request.form.get("symbol"))

        if stock == None:
            return apology("invalid ticker symbol")

        try:
            shares = int(request.form.get("shares"))
        except:
            return apology("must be a number")

        if shares < 0:
            return apology("input positive number")

        available_shares = db.execute("SELECT SUM(shares) as total_shares FROM transactions WHERE user_id = :user_id and symbol = :symbol GROUP BY symbol",
                                        user_id=session["user_id"], symbol=request.form.get("symbol"))

        if available_shares[0]["total_shares"] < 1 or shares > available_shares[0]["total_shares"]:
            return apology("not enough shares")

        price = stock["price"]

        total_price = shares * price

        db.execute("UPDATE users SET cash = cash + :total_price WHERE id = :user_id", user_id=session["user_id"], total_price=total_price)
        db.execute("INSERT INTO 'transactions' (user_id, symbol, shares, price) VALUES(:user_id, :symbol, :shares, :price)", user_id=session["user_id"], symbol=request.form.get("symbol"), shares=-shares, price=price)

        return redirect("/")

    else:
        available_stocks = db.execute("SELECT symbol, SUM(shares) as total_shares FROM transactions WHERE user_id = :user_id GROUP BY symbol", user_id=session["user_id"])
        return render_template("sell.html", available_stocks=available_stocks)


def errorhandler(e):
    """Handle error"""
    return apology(e.name, e.code)


# listen for errors
for code in default_exceptions:
    app.errorhandler(code)(errorhandler)
10
  • It would be helpful if you edited the question to add the details from the log as referenced here application raised an exception (see log for details) – DinoCoderSaurus May 3 '19 at 17:39
  • Hey there @DinoCoderSaurus, thanks for the suggestion! I've just added the details from the log. – user3124240 May 3 '19 at 17:41
  • The current version of the distro code uses check_password_hash in the login function (not pwd_context.verify). Suggest you change that line. Out of curiosity: from where or when did you download the distro code? That particular line is from the CS50x 2017version. – DinoCoderSaurus May 3 '19 at 18:03
  • Hey there @DinoCoderSaurus, thanks for pointing that out. I was double checking the code and saw this issue, already corrected it. What's bugging me now is that I'm receiving the following response: :( logging in as registered user succceeds expected status code 200, but got 400 – user3124240 May 3 '19 at 18:13
  • Speculation only: newly registered user has no transactions records, so stocks and quotes are empty when sent to portfolio.html – DinoCoderSaurus May 3 '19 at 18:35
0

https://werkzeug.palletsprojects.com/en/0.15.x/utils/#werkzeug.security.check_password_hash

check_password_hash(pwhash, password)

while you have

check_password_hash(request.form.get("password"), rows[0]["hash"])

password <-> hash confusion

You must log in to answer this question.

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