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!
<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 :)symbol
sent fromsell.html
? Keep in mind, check50 does not use the front end (sell.html
), rather does an API call tosell
. 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?