· 5 years ago · Feb 24, 2021, 05:14 PM
1import os
2
3from cs50 import SQL
4from flask import Flask, flash, redirect, render_template, request, session
5from flask_session import Session
6from tempfile import mkdtemp
7from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
8from werkzeug.security import check_password_hash, generate_password_hash
9
10from helpers import apology, login_required, lookup, usd
11
12# Configure application
13app = Flask(__name__)
14
15# Ensure templates are auto-reloaded
16app.config["TEMPLATES_AUTO_RELOAD"] = True
17
18
19# Ensure responses aren't cached
20@app.after_request
21def after_request(response):
22 response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
23 response.headers["Expires"] = 0
24 response.headers["Pragma"] = "no-cache"
25 return response
26
27
28# Custom filter
29app.jinja_env.filters["usd"] = usd
30
31# Configure session to use filesystem (instead of signed cookies)
32app.config["SESSION_FILE_DIR"] = mkdtemp()
33app.config["SESSION_PERMANENT"] = False
34app.config["SESSION_TYPE"] = "filesystem"
35Session(app)
36
37# Configure CS50 Library to use SQLite database
38db = SQL("sqlite:///finance.db")
39
40# Make sure API key is set
41if not os.environ.get("API_KEY"):
42 raise RuntimeError("API_KEY not set")
43
44
45@app.route("/")
46@login_required
47def index():
48 """Show portfolio of stocks"""
49 return apology("TODO")
50
51
52@app.route("/buy", methods=["GET", "POST"])
53@login_required
54def buy():
55 if request.method == "GET":
56 return render_template("buy.html")
57
58 if request.method == "POST":
59
60 shares = int(request.form.get("shares"))
61 symbol = lookup(request.form.get("symbol"))
62
63 if symbol == None:
64 return apology("no symbol entered")
65
66 if shares <= 0:
67 return apology("no shares entered")
68
69 rows = db.execute("SELECT cash FROM users WHERE id = :user_id", user_id = session["user_id"])
70
71 cash_owned = rows[0]["cash"]
72 per_share_price = symbol["price"]
73
74 total_cost = shares * per_share_price
75 if total_cost > cash_owned:
76 return apology("not enough cash")
77
78 db.execute("UPDATE users SET cash = cash - :price WHERE id = :user_id", user_id = session["user_id"], price = total_cost)
79 db.execute("INSERT INTO transactions (user_id, symbol, shares, per_share_price) VALUES(:user_id, :symbol, :shares, :price)",
80 user_id = session["user_id"], symbol = symbol, shares = shares, price = per_share_price)
81
82 return render_template("TODO")
83
84@app.route("/history")
85@login_required
86def history():
87 """Show history of transactions"""
88 return apology("TODO")
89
90
91@app.route("/login", methods=["GET", "POST"])
92def login():
93 """Log user in"""
94
95 # Forget any user_id
96 session.clear()
97
98 # User reached route via POST (as by submitting a form via POST)
99 if request.method == "POST":
100
101 # Ensure username was submitted
102 if not request.form.get("username"):
103 return apology("must provide username", 403)
104
105 # Ensure password was submitted
106 elif not request.form.get("password"):
107 return apology("must provide password", 403)
108
109 # Query database for username
110 rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
111
112 # Ensure username exists and password is correct
113 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
114 return apology("invalid username and/or password", 403)
115
116 # Remember which user has logged in
117 session["user_id"] = rows[0]["id"]
118
119 # Redirect user to home page
120 return redirect("/")
121
122 # User reached route via GET (as by clicking a link or via redirect)
123 else:
124 return render_template("login.html")
125
126
127@app.route("/logout")
128def logout():
129 """Log user out"""
130
131 # Forget any user_id
132 session.clear()
133
134 # Redirect user to login form
135 return redirect("/")
136
137
138@app.route("/quote", methods=["GET", "POST"])
139@login_required
140def quote():
141 if request.method == "GET":
142 return render_template("quote.html")
143
144 if request.method == "POST":
145
146 if not request.form.get("symbol"):
147 return apology("no symbol entered")
148
149 quote = lookup(request.form.get("symbol"))
150
151 if quote == None:
152 return apology("invalid symbol")
153 return render_template("quote.html")
154
155 else:
156 return render_template("quoted.html", quote=quote)
157
158@app.route("/register", methods=["GET", "POST"])
159def register():
160 if request.method == "POST":
161
162 # If username was not submitted
163 if not request.form.get("username"):
164 return apology("must provide username", 403)
165
166 # If password was not submitted
167 if not request.form.get("password"):
168 return apology("must provide password", 403)
169
170 # If password and confirmation not submitted
171 if not request.form.get("password") or not request.form.get("confirmation"):
172 return apology("must provide password and confirmation")
173
174 # If confirmation and password don't match
175 if request.form.get("password") != request.form.get("confirmation"):
176 return apology("confirmation does not match provided password")
177
178 # Check for existinguser
179 existinguser = db.execute("SELECT * FROM users WHERE username = :username", username = request.form.get("username"))
180
181 # If user already exists, show error
182 if len(existinguser) == 1:
183 return apology("user already exists")
184
185 # Generate password hash if after error checking
186 hashp = generate_password_hash(request.form.get("password"))
187
188 # Create query to pass into session
189 createuser = db.execute("INSERT INTO users (username, hash) VALUES (:username, :hash)", username = request.form.get("username"), hash = hashp)
190
191 # Create session for current user and login
192 session["user_id"] = createuser
193 return redirect("/")
194
195 else:
196 return render_template("register.html")
197
198
199@app.route("/sell", methods=["GET", "POST"])
200@login_required
201def sell():
202 """Sell shares of stock"""
203 return apology("TODO")
204
205
206def errorhandler(e):
207 """Handle error"""
208 if not isinstance(e, HTTPException):
209 e = InternalServerError()
210 return apology(e.name, e.code)
211
212
213# Listen for errors
214for code in default_exceptions:
215 app.errorhandler(code)(errorhandler)
216