0

I already looked through every post I could find on this topic but nothing seemed to work for me yet. I have tried to recreate the error by: creating a new account, buying 4 shares of 'AAAA', then selling 2 of those shares and checking if the result is correct (TOTAL: $56.00, CASH: $9,944.00). This does seem to work so I am very confused and honestly a bit frustrated why check50 wont let me on :-( I also checked if the amount is displayed in the dollar format in index, that seems to be the case as well.

Here is my code for app.py:

import os
import time
from cs50 import SQL
from flask import Flask, flash, redirect, render_template, request, session
from flask_session import Session
from werkzeug.security import check_password_hash, generate_password_hash

from helpers import apology, login_required, lookup, usd

# Configure application
app = Flask(__name__)

# Custom filter
app.jinja_env.filters["usd"] = usd

# Configure session to use filesystem (instead of signed cookies)
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.after_request
def after_request(response):
    """Ensure responses aren't cached"""
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Expires"] = 0
    response.headers["Pragma"] = "no-cache"
    return response


@app.route("/")
@login_required
def index():
    """Show portfolio of stocks"""
    # create table summarizing:
    # owned stocks, number of shares owned, current stock price, total value
    # current cash balance, grand total (stocks+cash value)
    user_id = session["user_id"]
    userdatabase = db.execute("SELECT cash as c FROM users WHERE id = ?", user_id)
    transactiondatabase = db.execute("SELECT stocksymbol, SUM(sharesbought) as nsharesowned FROM transactions WHERE user_id = ? AND transactiontype = ? GROUP BY stocksymbol", user_id, 'buy')

    # TODO: the way the prices are calculated need to be changed:
    # 1. for each stock, calculate how many shares have been bought (ever) and how many shares have been sold (ever)
    # --> bought - sold = currently held shares of that stock

    # another database call for distinct stock symbol
    # 2. sum of value of each stock: currentlyhelshares * individualstockprice

    # Too direct those informations to the html file, they must be saved in four different dictionaries containing: symbol, sharesheld, individualprice, totalvalue
    # the dicts are actually lists because there are no keys to the values

    allinkldict = []
    totalvaluedict = []
    totalvalue = 0
    for stock in transactiondatabase:
        # fill each dictionary with the corresponding value for the actual stock
        stockprice = lookup(stock['stocksymbol'])

        # shares owned at the moment
        sharessold = db.execute("SELECT stocksymbol, SUM(sharesbought) as nsharesowned FROM transactions WHERE user_id = ? AND stocksymbol = ? AND transactiontype = ? GROUP BY stocksymbol", user_id, stock['stocksymbol'], 'sell')
        if not sharessold:
            sharessold = [{"nsharesowned": 0}]
        if stock['nsharesowned']-sharessold[0]['nsharesowned'] != 0:
            if not sharessold:
                tempprice = stock['nsharesowned']
                dictnewelement = {"symbol": stock['stocksymbol'], "sharesheld": (
                    stock['nsharesowned']), "stockprice": stockprice['price'], "totalvalue": (stock['nsharesowned']) * stockprice['price']}
                totalvalue += (stock['nsharesowned']) * stockprice['price']

            else:
                tempprice = stock['nsharesowned']
                dictnewelement = {"symbol": stock['stocksymbol'], "sharesheld": (stock['nsharesowned'] - (
                    sharessold[0]['nsharesowned'])), "stockprice": stockprice['price'], "totalvalue": (stock['nsharesowned'] - sharessold[0]["nsharesowned"]) * stockprice['price']}
                totalvalue += ((stock['nsharesowned']-(sharessold[0]['nsharesowned'])) * stockprice['price'])

            totalvaluedict.append((stock['nsharesowned'] - tempprice) * stockprice['price'])
            allinkldict.append(dictnewelement)

    countcash = userdatabase[0]['c']

    counttotal = totalvalue + countcash
    return render_template("index.html", userdatabase=userdatabase, transactiondatabase=transactiondatabase, counttotal=counttotal, countcash=countcash, allinkldict=allinkldict)


@app.route("/buy", methods=["GET", "POST"])
@login_required
def buy():
    """Buy shares of stock"""
    # input 1: stock symbol <-- apology if blank/nonexistent
    # input 2: number of shares <-- apology if negative
    # direct input to buy
    # redirect to homepage
    # check if user has enough cashF
    # keep track of bought shares in a new table in finance

    if request.method == "POST":
        Sharesymbol = request.form.get("symbol")
        Sharesymbol = Sharesymbol.upper()

        numberofshares = request.form.get("shares")

        if not numberofshares.isnumeric():
            return apology("Please enter a number regarding how many stocks you want to sell.")
        if Sharesymbol is not None:
            shareinformation = lookup(Sharesymbol)
            if shareinformation is None:
                return apology("Please enter a valid stock symbol")
        if Sharesymbol is None or Sharesymbol == "":
            return apology("Please enter valid stock symbol.")

        if numberofshares is None or float(numberofshares) < 1 or float(numberofshares) % 1 != 0:
            return apology("Please enter the number of stocks you want to purchase. The number must be greater than zero.")

        usercurrentcash = db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"])
        if usercurrentcash is not None and float(usercurrentcash[0]["cash"]) <= shareinformation['price'] * float(numberofshares):
            return apology("Not enough cash to execute transaction.")

        else:
            # add buy to transaction table
            user_id = session["user_id"]
            db.execute("INSERT INTO transactions (user_id, stocksymbol, stockprice, sharesbought, transactiontype) VALUES (?,?,?,?,?)",
                       user_id, Sharesymbol, shareinformation['price'], numberofshares, "buy")

            # reduce customer cash by cash used to buy shares
            newcash = usercurrentcash[0]["cash"] - shareinformation['price'] * float(numberofshares)
            db.execute("UPDATE users SET cash = ? WHERE id = ?", newcash, user_id)
            return redirect("/")
    else:
        return render_template("buy.html")


@app.route("/history")
@login_required
def history():
    """Show history of transactions"""
    user_id = session["user_id"]
    userdatabase = db.execute("SELECT cash as c FROM users WHERE id = ?", user_id)
    transactiondatabase = db.execute(
        "SELECT stocksymbol, sharesbought, transactiontype, stockprice, date as timestamp FROM transactions WHERE user_id = ?", user_id)
    return render_template("history.html", userdatabase=userdatabase, transactiondatabase=transactiondatabase)


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

    # Forget any user_id
    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 = ?", 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
        return redirect("/")

    # User reached route via GET (as by clicking a link or via redirect)
    else:
        return render_template("login.html")


@app.route("/changepassword", methods=["GET", "POST"])
@login_required
def changepassword():
    user_id = session["user_id"]
    # User reached route via POST (as by submitting a form via POST)
    if request.method == "POST":

        # Ensure password was submitted
        if not request.form.get("oldpassword"):
            return apology("Must provide old password")
        if not request.form.get("newpassword"):
            return apology("Must provide new password")
        if not request.form.get("confirmnewpassword"):
            return apology("Must confirm new password")

        # Query database for username
        rows = db.execute("SELECT * FROM users WHERE id = ?", user_id)

        # Ensure password is correct
        if not check_password_hash(rows[0]["hash"], request.form.get("oldpassword")):
            return apology("Invalid old password")

        # substitute old password in database
        newpassword = request.form.get("newpassword")
        confirmnewpassword = request.form.get("confirmnewpassword")
        if newpassword != confirmnewpassword:
            return apology("Please fill in a matching Password in both boxes.")

        else:
            # add user to the database
            db.execute("UPDATE users SET hash=? WHERE id=?", generate_password_hash(newpassword), user_id)

            # Redirect user to home page
        return redirect("/")

    else:
        return render_template("changepassword.html")


@app.route("/logout")
def logout():
    """Log user out"""

    # Forget any user_id
    session.clear()

    # Redirect user to login form
    return redirect("/")


@app.route("/quote", methods=["GET", "POST"])
@login_required
def quote():
    """Get stock quote."""
    # input: stock symbol
    # if called by OPEN open quoted
    if request.method == "POST":
        Sharesymbol = request.form.get("symbol")
        if Sharesymbol is not None:
            shareinformation = lookup(Sharesymbol)
            if shareinformation is None:
                return apology("Please enter a valid stock symbol")
        if Sharesymbol is None or Sharesymbol == "":
            return apology("Please enter valid stock symbol.")
        else:
            stockinformation = lookup(Sharesymbol)
            return render_template("quoted.html", stockinformation=stockinformation)

    # if called by GET open quote
    else:
        return render_template("quote.html")


"""Register user"""


@app.route("/register", methods=["GET", "POST"])
def register():
    # check wether username is valid
    if request.method == "POST":
        name = request.form.get("username")
        password = request.form.get("password")
        confirmation = request.form.get("confirmation")
        if not request.form.get("username"):
            return apology("Username cannot be blank.")
        usertaken = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
        if len(usertaken) > 0:
            return apology("Username is already taken")
    # check if passwort and confirmation of password is valid
        if not password or not confirmation or password != confirmation:
            return apology("Please fill in a matching Password in both boxes.")
        else:
            # add user to the database
            db.execute("INSERT INTO users (username, hash) VALUES (?,?)", name, generate_password_hash(password))
            # login the newly registered user
            session["user_id"] = db.execute("SELECT * FROM users WHERE username = ?", name)[0]["id"]

            # Redirect user to home page
        return redirect("/")
    else:
        return render_template("register.html")


@app.route("/sell", methods=["GET", "POST"])
@login_required
def sell():
    """Sell shares of stock"""
    # input 1: stock symbol, form: select menu (name=symbol)
    # -> apology if: no stock selected, no shares of that stock owned
    # input 2: number of shares, form: text field with name being shares
    # -> apology if: input not positive integer, user does not own that many shares of stock
    # submit input with post to sell
    # subtract sold shares from number of shares currently held by user
    # redirect user to homepage

    user_id = session["user_id"]
    if request.method == "POST":
        numbershares = request.form.get("shares")
        stockselected = request.form.get("symbol")
        if not numbershares:
            return apology("Enter the number of shares you want to sell.")
        if not stockselected:
            return apology("Enter a valid stock symbol.")
        stocksymbolpre = ""
        stocksymbolpost = ""
        # extract the stock from stockselected
        for symbol in stockselected:
            if symbol == "'" or symbol == "{" or symbol == "}" or symbol == ":":
                continue
            else:
                stocksymbolpre += symbol
        isspace = False
        for symbol in stocksymbolpre:
            if isspace:
                stocksymbolpost += symbol
            if symbol == " ":
                isspace = True

        sharestotal = db.execute(
            "SELECT DISTINCT(stocksymbol), SUM(sharesbought) as nsharesowned FROM transactions WHERE user_id = ? AND stocksymbol = ? AND transactiontype = ? GROUP BY stocksymbol", user_id, stocksymbolpost, 'buy')
        sharessold = db.execute(
            "SELECT DISTINCT(stocksymbol), SUM(sharesbought) as nsharesowned FROM transactions WHERE user_id = ? AND stocksymbol = ? AND transactiontype = ? GROUP BY stocksymbol", user_id, stocksymbolpost, 'sell')
        if not sharestotal:
            return apology("Internal error.")
        if not sharessold:
            sharescurrentlyheld = sharestotal[0]["nsharesowned"]
        else:
            sharescurrentlyheld = sharestotal[0]["nsharesowned"] - sharessold[0]["nsharesowned"]

        # check if user has selected stock to sell
        if not numbershares:
            return apology("Please enter the amount of shares you want to sell")
        # check if user has enough shares to sell
        if int(numbershares) > sharescurrentlyheld:
            return apology("The number of shares you want to sell must be lower or equal to the shares you possess at the moment.")
        # check if the number of shares is positive
        if int(numbershares) < 1:
            return apology("The number of shares you want to sell must be higher or equal to 1.")

        # subtract sold shares from number of shares currently held by user
            # --> create new table and keep track there of how many stocks a user holds
            # new column in transactions named transactiontype of type String, it can be 'buy' when a share is bought and 'sell' when a share is sold

        # keep hold of transaction in database
        # -> extract the share name
        shareinformation = lookup(stocksymbolpost)
        db.execute("INSERT INTO transactions (user_id, stocksymbol, stockprice, sharesbought, transactiontype) VALUES (?,?,?,?,?)",
                   user_id, stocksymbolpost, shareinformation['price'], numbershares, 'sell')

        # increase customer cash by cash gotten by selling shares
        shareinformation = lookup(stocksymbolpost)
        usercurrentcash = db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"])
        newcash = usercurrentcash[0]["cash"] + shareinformation['price'] * float(numbershares)
        db.execute("UPDATE users SET cash = ? WHERE id = ?", newcash, user_id)
        # redirect user to homepage
        return redirect("/")

    else:
        # only show shares that user currently possesses
        transactiondatabase = db.execute("SELECT stocksymbol, SUM(sharesbought) as nsharesowned FROM transactions WHERE user_id = ? AND transactiontype = ? GROUP BY stocksymbol", user_id, 'buy')
        shareshelddict = []
        for stock in transactiondatabase:
            sharessold = db.execute("SELECT stocksymbol, SUM(sharesbought) as nsharesowned FROM transactions WHERE user_id = ? AND stocksymbol = ? AND transactiontype = ? GROUP BY stocksymbol", user_id, stock['stocksymbol'], 'sell')
            if not sharessold:
                sharessold = [{"nsharesowned": 0}]
            if stock['nsharesowned']-sharessold[0]['nsharesowned'] != 0:
                dictnewelement = {"symbol": stock['stocksymbol']}
                shareshelddict.append(dictnewelement)


        return render_template("sell.html", shareshelddict=shareshelddict)

And my code for index.html:


{% block title %}
    Home
{% endblock %}

    {% block main %}
    <h2>Your current portfolio:</h2>
    <head>
        <style>
        table {
          font-family: arial, sans-serif;
          border-collapse: collapse;
          width: 100%;
        }

        td, th {
          border: 1px solid #dddddd;
          text-align: left;
          padding: 8px;
        }

        tr:nth-child(even) {
          background-color: #dddddd;
        }
        </style>
        </head>
                    <table>
                        <thead>
                            <tr>
                                <td>Symbol</td>
                                <th>Shares</th>
                                <th>Price</th>
                                <th>TOTAL</th>
                            </tr>
                        </thead>
                        <tbody>
                        <!-- loop through database and show result-->
                        <ul>
                            <!-- stocks -->
                            {% for symbol in allinkldict %}
                            <tr>
                                <th>{{symbol['symbol']}}</th>
                                <th>{{symbol['sharesheld']}}</th>
                                <th>{{symbol['stockprice'] | usd}}</th>
                                <th>{{symbol['totalvalue'] | usd}}</th>
                            {% endfor %}

                            </tr>

                        </ul>
        <table>
            <tr>
                <th>
                </th>
            </tr>
                                        <!-- Cash -->
                                        <tr>
                                            <th>Cash </th>
                                            <td>{{countcash | usd}}</td>
                                        </tr>
                                        <!-- Total, calculate in python and print out here  -->
                                        <tr>
                                            <th>TOTAL</th>
                                            <td>{{counttotal | usd}}</td>
                                        </tr>
        </table>
                        </tbody>
                    </table>
{% endblock %}

I would really appreciate your help on this to be able to continue with the final project. Many thanks in advance!

4
  • Suggest removing the <ul> tag for starters. It is not valid content in a <table> element. TBH I have no idea if invalid HTML will cause check50 to fail, but your test is essentially what check50 is testing. The big difference is eyes (check50 doesn't have any :) Commented Feb 18 at 0:45
  • Thank you for the suggestion, unfortunately removing the <ul> tag (which honestly I don't know what it was doing there) in index.html did not fix the problem. check50 still fails on the test :(
    – scrypter
    Commented Feb 18 at 12:03
  • Why all this checking for special characters and essentially modifying the symbol sent from sell.html? Keep in mind, check50 does not use the front end (sell.html), rather does an API call to sell. Full disclosure, I couldn't wrap my mind around that code, but I'm genuinely curious what it's all about. What are the full results from the check50 log for that test? Commented Feb 18 at 17:01
  • Thank you very much for pointing this out! I had an error in sell.html where I was returning values in the format "'symbol': 'stocksymbol'" instead of just the actual symbol, which I then tried to correct in my python code to only show the symbol. I fixed my sell.html code to return only the stocksymbol to POST and deleted the parts of the code you wondered about. The program now passes all check50 tests, thank you very much for taking the time to help me!
    – scrypter
    Commented Feb 20 at 18:01

1 Answer 1

1

I had an error in sell.html where I was returning values in the format "'symbol': 'stocksymbol'" instead of just the actual symbol, which I then tried to correct in my python code to only show the symbol, which technically worked but check50 did not like. I fixed my sell.html code to return only the stocksymbol to POST and deleted the parts of the code where I formatted the POST return in a special way and the program now passes all check50 tests.

You must log in to answer this question.

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