2049 words
10 minutes
CS50 Problem Set 9
CS50 Problem Set 9
Here’s my answers for the CS50 Problem Set 9. Hope that will help you a bit.
Problem 1: Birthdays
<!DOCTYPE html>
<html lang="en">
<head> <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@500&display=swap" rel="stylesheet"> <link href="/static/styles.css" rel="stylesheet"> <title>Birthdays</title> </head>
<body> <div class="header"> <h1>Birthdays</h1> </div> <div class="container"> <div class="section">
<h2>Add a Birthday</h2> <!-- TODO: Create a form for users to submit a name, a month, and a day --> <!-- With {action="/" method ="post"}, the form can send data back to the server, and meet the requirement {request.method == "POST"} in app.py--> <form action="/" method="post"> <input name="name" placeholder="Name" autofocus autocomplete="off" type="text" min="1" max="12"> <input name="month" placeholder="Month" autocomplete="off" type="number" min="1" max="31"> <input name="day" placeholder="Day" autocomplete="off" type="number"> <input type="submit" value="Add Birthday"> </form>
</div>
<div class="section">
<h2>All Birthdays</h2> <table> <thead> <tr> <th>Name</th> <th>Birthday</th> </tr> </thead> <tbody> <!-- TODO: Loop through the database entries to display them in this table --> {% for birthday in birthdays %} <tr> <td>{{ birthday["name"] }}</td> <td>{{ birthday["month"] }}/{{ birthday["day"] }}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </body>
</html>import os
from cs50 import SQLfrom flask import Flask, flash, jsonify, redirect, render_template, request, session
# Configure applicationapp = Flask(__name__)
# Ensure templates are auto-reloadedapp.config["TEMPLATES_AUTO_RELOAD"] = True
# Configure CS50 Library to use SQLite databasedb = SQL("sqlite:///birthdays.db")
@app.after_requestdef 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("/", methods=["GET", "POST"])def index(): if request.method == "POST":
# TODO: Add the user's entry into the database name = request.form.get("name") if not name: return redirect("/")
month = request.form.get("month") if not month: return redirect("/")
day = request.form.get("day") if not day: return redirect("/")
db.execute("INSERT INTO birthdays (name, month, day) VALUES(?, ?, ?)", name, month, day)
return redirect("/")
else:
# TODO: Display the entries in the database on index.html birthdays = db.execute("SELECT * FROM birthdays")
return render_template("index.html", birthdays=birthdays)Problem 2: Finance
import os
from cs50 import SQLfrom flask import Flask, flash, redirect, render_template, request, sessionfrom flask_session import Sessionfrom werkzeug.security import check_password_hash, generate_password_hash
from helpers import apology, login_required, lookup, usd
# Configure applicationapp = Flask(__name__)
# Custom filterapp.jinja_env.filters["usd"] = usd
# Configure session to use filesystem (instead of signed cookies)app.config["SESSION_PERMANENT"] = Falseapp.config["SESSION_TYPE"] = "filesystem"Session(app)
# Configure CS50 Library to use SQLite databasedb = SQL("sqlite:///finance.db")
@app.after_requestdef 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_requireddef index(): """Show portfolio of stocks""" # select user's portfolio and cash rows = db.execute("SELECT * FROM portfolio WHERE userid = ?", session["user_id"]) cash_result = db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"])
# get cash value cash = cash_result[0]['cash']
# intilize sum = cash sum = cash
for row in rows: stock_info = lookup(row['symbol']) if stock_info is None: continue # Skip if stock info not available row['name'] = stock_info['name']
# Ensure price is numeric before calculations (handles string prices from API) try: price_numeric = float(stock_info['price']) except (ValueError, TypeError): continue # Skip if price is not a valid number
row['price'] = price_numeric row['total'] = price_numeric * row['shares'] sum = sum + row['total']
# Format price and total as USD for display (don't modify original numeric values) row['price_usd'] = usd(price_numeric) row['total_usd'] = usd(row['total'])
return render_template("index.html", rows=rows, cash=usd(cash), sum=usd(sum))
@app.route("/buy", methods=["GET", "POST"])@login_requireddef buy(): """Buy shares of stock""" # if the method is GET, display buy.html to the user if request.method == "GET": return render_template("buy.html")
# if the method is POST else: symbol = request.form.get("symbol") shares = request.form.get("shares") quote = lookup(symbol)
if not symbol: return apology("Please provide a stock symbol!", 400)
if not shares: return apology("Please provide number of shares!", 400)
if quote == None: return apology("Invalid stock symbol!", 400)
# validate shares: must be a positive integer try: shares = float(shares) if shares != int(shares): return apology("Shares must be a whole number (no fractions)!", 400) shares = int(shares) if shares <= 0: return apology("Shares must be a positive number!", 400) except (ValueError, TypeError): return apology("Shares must be a numeric value!", 400)
# calculate the purchase symbol = symbol.upper() # Ensure quote['price'] is a number for calculations try: price = float(quote['price']) except (ValueError, TypeError): return apology("Invalid stock price data!", 400)
purchase = price * shares
# get the balance balance_result = db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"]) balance = balance_result[0]['cash']
# calculate the remainder remainder = balance - purchase
if remainder < 0: return apology("Insufficient money!", 400)
# update the database db.execute("UPDATE users SET cash = ? WHERE id = ?", remainder, session["user_id"])
# check if user already owns this stock existing = db.execute("SELECT shares FROM portfolio WHERE userid = ? AND symbol = ?", session["user_id"], symbol)
if existing: # update existing position old_shares = existing[0]['shares'] new_shares = old_shares + shares db.execute("UPDATE portfolio SET shares = ? WHERE userid = ? AND symbol = ?", new_shares, session["user_id"], symbol) else: # create new position db.execute("INSERT INTO portfolio (userid, symbol, shares) VALUES (?, ?, ?)", session["user_id"], symbol, shares)
# update history db.execute("INSERT INTO history (userid, symbol, shares, method, price) VALUES (?, ?, ?, ?, ?)", session["user_id"], symbol, shares, 'Buy', price)
flash(f"Bought {shares} shares of {symbol} costing {usd(purchase)}") return redirect("/")
@app.route("/history")@login_requireddef history(): """Show history of transactions"""
rows = db.execute("SELECT * FROM history WHERE userid = ?", session["user_id"])
return render_template("history.html", rows=rows)
@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 username = request.form.get("username") if not username: return apology("must provide username", 403)
# Ensure password was submitted password = request.form.get("password") if not password: return apology("must provide password", 403)
# Query database for username rows = db.execute( "SELECT * FROM users WHERE username = ?", username )
# Ensure username exists and password is correct if len(rows) != 1 or not check_password_hash( rows[0]["hash"], 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("/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_requireddef quote(): """Get stock quote."""
# if GET received, send quote.html to the user if request.method == "GET": return render_template("quote.html")
else:
# lookup in the database symbol = lookup(request.form.get("symbol"))
if symbol == None: return apology("invalid stock symbol", 400) else: return render_template("quoted.html", symbol=symbol)
@app.route("/register", methods=["GET", "POST"])def register(): """Register user""" # clear previous session session.clear()
# submit the register form if request.method == "POST":
# check whether the username exists if not request.form.get("username"): return apology("Username is required!", 400)
# check whether the password exists elif not request.form.get("password"): return apology("Password is required!", 400)
# check whether the confirmation exists elif not request.form.get("confirmation"): return apology("Confirmation is required!", 400)
password = request.form.get("password") confirmation = request.form.get("confirmation")
if not password or not confirmation: return apology("Password fields cannot be empty!", 400)
# check whether the password is the same as confirmation if password != confirmation: return apology("Passwords do not match!", 400)
# save the username, and hash the password username = request.form.get("username") hash = generate_password_hash(password)
# check if the username has been taken rows = db.execute("SELECT * FROM users WHERE username = ?", username) if len(rows) != 0: return apology("Username already exists!", 400)
# insert username and hash into database db.execute("INSERT INTO users (username, hash) VALUES (?, ?)", username, hash)
# create new session for the user id = db.execute("SELECT id FROM users WHERE username = ?", username) session["user_id"] = id[0]["id"]
# redirect to login page return redirect("/login")
else: return render_template("register.html")
@app.route("/sell", methods=["GET", "POST"])@login_requireddef sell(): """Sell shares of stock"""
# if method=GET, send sell.html to the user if request.method == "GET": # get the user's current stock portfolio = db.execute("SELECT symbol FROM portfolio WHERE userid = ?", session["user_id"]) return render_template("sell.html", portfolio=portfolio)
# if method=POST, sell stocks else: # Save stock symbol, number of shares, and quote dict from form symbol = request.form.get("symbol") shares = request.form.get("shares")
# check for symbol if not symbol: return apology("Must provide valid stock symbol!", 400)
quote = lookup(symbol) if quote is None: return apology("Invalid stock symbol!", 400)
rows = db.execute("SELECT * FROM portfolio WHERE userid = ? AND symbol = ?", session["user_id"], symbol.upper())
# check for errors if len(rows) != 1: return apology("Must provide valid stock symbol!", 400) if not shares: return apology("Must provide number of shares!", 400)
# validate shares: must be a positive integer try: shares = float(shares) if shares != int(shares): return apology("Shares must be a whole number (no fractions)!", 400) shares = int(shares) if shares <= 0: return apology("Shares must be a positive number!", 400) except (ValueError, TypeError): return apology("Shares must be a numeric value!", 400)
# get the old shares oldshares = rows[0]['shares']
# check for errors if shares > oldshares: return apology("Shares sold can't exceed shares owned", 400)
# calculate sold money # Ensure quote['price'] is a number for calculations try: price = float(quote['price']) except (ValueError, TypeError): return apology("Invalid stock price data!", 400)
sold = price * shares
# get the current cash of the user cash_result = db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"]) cash = cash_result[0]['cash']
# calculate the new amount of cash cash = cash + sold
# update the database db.execute("UPDATE users SET cash = ? WHERE id = ?", cash, session["user_id"])
# calcualte new shares newshares = oldshares - shares
# if there's shares remain, update portfolio if newshares > 0: db.execute("UPDATE portfolio SET shares = ? WHERE userid = ? AND symbol = ?", newshares, session["user_id"], symbol) else: db.execute("DELETE FROM portfolio WHERE symbol = ? AND userid = ?", symbol, session["user_id"])
# update history db.execute("INSERT INTO history (userid, symbol, shares, method, price) VALUES (?, ?, ?, ?, ?)", session["user_id"], symbol, shares, 'Sell', price)
flash(f"Sold {shares} shares of {symbol.upper()} for {usd(sold)}") return redirect("/"){% extends "layout.html" %}
{% block title %} Buy{% endblock %}
{% block main %} <h2>Buy Shares</h2> <form action="/buy" method="post"> <div> <input name="symbol" placeholder="Symbol" type="text" autocomplete="off" autofocus> <input name="shares" placeholder="Shares" type="number" autocomplete="off" autofocus min="0"> <button type="submit">Buy</button> </div> </form>{% endblock %}{% extends "layout.html" %}
{% block title %} C$50 Finance: Portfolio{% endblock %}
{% block main %} <table class="table table-bordered table-striped"> <thead class="thead-light"> <tr> <th>Symbol</th> <th>Shares</th> <th>Method</th> <th>Price</th> <th>Transacted</th> </thead> <tbody> {% for row in rows %} <tr> <td>{{ row['symbol'] }}</td> <td>{{ row['shares'] }}</td> <td>{{ row['method'] }}</td> <td>{{ row['price'] | usd }}</td> <td>{{ row['transacted'] }}</td> </tr> {% endfor %} </tbody> </table>{% endblock %}{% extends "layout.html" %}
{% block title %} C$50 Finance: Portfolio{% endblock %}
{% block main %} <table class="table table-bordered table-striped"> <thead class="thead-light"> <tr> <th>Symbol</th> <th>Name</th> <th>Shares</th> <th>Price</th> <th>Total Value</th> </thead> <tbody> {% for row in rows %} <tr> <td>{{ row['symbol'] }}</td> <td>{{ row['name'] }}</td> <td>{{ row['shares'] }}</td> <td>{{ row['price_usd'] }}</td> <td>{{ row['total_usd'] }}</td> </tr> {% endfor %} <tr> <td colspan="4" align="right">Cash</td> <td>{{ cash }}</td> </tr> <tr> <td colspan="4" align="right">Total Value</td> <td>{{ sum }}</td> </tr> </tbody> </table>{% endblock %}{% extends "layout.html" %}
{% block title %} Log In{% endblock %}
{% block main %} <form action="/login" method="post"> <div class="mb-3"> <input autocomplete="off" autofocus class="form-control mx-auto w-auto" name="username" placeholder="Username" type="text"> </div> <div class="mb-3"> <input class="form-control mx-auto w-auto" name="password" placeholder="Password" type="password"> </div> <button class="btn btn-primary" type="submit">Log In</button> </form>{% endblock %}{% extends "layout.html" %}
{% block title %} Quote{% endblock %}
{% block main %} <h2>Get a stock quote</h2> <form action="/quote" method="post"> <input autofocus name="symbol" placeholder="Symbol" type="text"> <button type="submit">Get Quote</button> </form>{% endblock %}{% extends "layout.html" %}
{% block title %} Quote{% endblock %}
{% block main %} A share of {{ symbol['name']}} ({{symbol['symbol']}}) costs {{ symbol['price'] | usd}}{% endblock %}{% extends "layout.html" %}
{% block title %} Register{% endblock %}
{% block main %} <form action="/register" method="post"> <div class="mb-3"> <input autocomplete="off" autofocus class="form-control mx-auto w-auto" name="username" placeholder="Username" type="text"> </div> <div class="mb-3"> <input class="form-control mx-auto w-auto" name="password" placeholder="Password" type="password"> </div> <div class="mb-3"> <input class="form-control mx-auto w-auto" name="confirmation" placeholder="Confirmation" type="password"> </div> <button class="btn btn-primary" type="submit">Register</button> </form>{% endblock %}{% extends "layout.html" %}
{% block title %} Sell{% endblock %}
{% block main %} <h2>Sell Shares</h2> <form action="/sell" method="post"> <div> <select class="form-control" name="symbol" type="text"> <option value="" disabled selected>Symbol</option> {% for row in portfolio %} <option value="{{ row['symbol'] }}">{{ row['symbol'] }}</option> {% endfor %} </select> </div> <div> <input name="shares" placeholder="Shares" type="number" min="1"> </div> <button type="submit">Sell</button> </form>{% endblock %} CS50 Problem Set 9
https://tech.kinghua0629.com/posts/cs50-problem-set-9/