· 4 years ago · Jun 27, 2021, 12:48 PM
1import os
2from cs50 import SQL
3from flask import Flask, flash, redirect, render_template, request, session, url_for
4from flask_session import Session
5from tempfile import mkdtemp
6from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
7from werkzeug.security import check_password_hash, generate_password_hash
8
9from helpers import apology, login_required, lookup, usd
10
11# Configure application
12app = Flask(__name__)
13
14# Ensure templates are auto-reloaded
15app.config["TEMPLATES_AUTO_RELOAD"] = True
16
17
18# Ensure responses aren't cached
19@app.after_request
20def after_request(response):
21 response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
22 response.headers["Expires"] = 0
23 response.headers["Pragma"] = "no-cache"
24 return response
25
26
27# Custom filter
28app.jinja_env.filters["usd"] = usd
29
30# Configure session to use filesystem (instead of signed cookies)
31app.config["SESSION_FILE_DIR"] = mkdtemp()
32app.config["SESSION_PERMANENT"] = False
33app.config["SESSION_TYPE"] = "filesystem"
34Session(app)
35
36# Configure CS50 Library to use SQLite database
37db = SQL("sqlite:///finance.db")
38
39# Make sure API key is set
40if not os.environ.get("API_KEY"):
41 raise RuntimeError("API_KEY not set")
42
43
44@app.route("/add_cash", methods=["GET", "POST"])
45@login_required
46def add_cash():
47 if request.method == "POST":
48 db.execute("""
49 UPDATE users
50 SET cash = cash + :amount
51 WHERE id=:user_id
52 """, amount=request.form.get("cash"),
53 user_id=session["user_id"])
54 flash("added cash!")
55 return redirect("/")
56 else:
57 return render_template("add_cash.html")
58
59
60@app.route("/")
61@login_required
62def index():
63 """Show portfolio of stocks"""
64 rows = db.execute("""
65 SELECT symbol, SUM(shares) as totalshares
66 FROM transactions
67 WHERE user_id = :user_id
68 GROUP BY symbol
69 HAVING totalshares > 0;
70 """, user_id=session["user_id"])
71 holdings = []
72 grand_total = 0
73 for row in rows:
74 stock = lookup(row["symbol"])
75 holdings.append({
76 "symbol": stock["symbol"],
77 "name": stock["name"],
78 "shares": row["totalshares"],
79 "price": stock["price"],
80 "total": usd(stock["price"] * row["totalshares"])
81 })
82 grand_total += stock["price"] * row["totalshares"]
83
84 # get user cash total
85 rows = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id"])
86 cash = rows[0]['cash']
87 grand_total += cash
88
89 return render_template("index.html", holdings=holdings, cash=usd(cash), grand_total=usd(grand_total))
90
91
92@app.route("/buy", methods=["GET", "POST"])
93@login_required
94def buy():
95 """Buy shares of stock"""
96 # if user reached route via POST (as by submitting a form via POST)
97 if request.method == "POST":
98
99 # ensure stock symbol and number of shares was submitted
100 if (not request.form.get("symbol")) or (not request.form.get("shares")):
101 return apology("must provide stock symbol and number of shares")
102 try:
103 shares = int(request.form.get("shares"))
104 except:
105 return apology("round number")
106 # ensure number of shares is valid
107 if shares <= 0:
108 return apology("must provide valid number of shares (integer)")
109
110 # pull quote from yahoo finance
111 symbol = request.form.get("symbol").upper()
112
113 stock = lookup(symbol)
114 if stock == None:
115 return apology("invalid symbol")
116
117 # check if user has enough cash for transaction
118 rows = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id"])
119 cash = rows[0]["cash"]
120
121 updated_cash = cash - (shares * stock['price'])
122 if updated_cash < 0:
123 return apology("can't afford")
124
125 db.execute("UPDATE users SET cash=:updated_cash WHERE id=:id",
126 updated_cash=updated_cash,
127 id=session["user_id"])
128 db.execute("""
129 INSERT INTO transactions
130 (user_id, symbol, shares, price)
131 VALUES (:user_id, :symbol, :shares, :price)
132 """,
133 user_id=session["user_id"],
134 symbol=stock["symbol"],
135 shares=shares,
136 price=stock["price"]
137 )
138
139 flash("Bought!")
140 # doorsturen naar index-pagina TODO
141 return redirect(url_for('.index'))
142
143 # else if user reached route via GET (as by clicking a link or via redirect)
144 else:
145 rows = db.execute("""
146 SELECT symbol
147 FROM transactions
148 WHERE user_id=:user_id
149 GROUP BY symbol
150 HAVING SUM(shares) > 0;
151 """, user_id=session["user_id"])
152 return render_template("buy.html", symbols=[row["symbol"] for row in rows])
153
154
155@app.route("/history")
156@login_required
157def history():
158 """Show history of transactions"""
159 # pull all transactions belonging to user
160 transactions = db.execute("""
161 SELECT symbol, shares, price, transacted
162 FROM transactions
163 WHERE user_id=:user_id
164 """, user_id=session["user_id"])
165 for i in range(len(transactions)):
166 transactions[i]["pirce"] = usd(transactions[i]["price"])
167
168 return render_template("history.html", transactions=transactions)
169
170
171@app.route("/login", methods=["GET", "POST"])
172def login():
173 """Log user in"""
174
175 # Forget any user_id
176 session.clear()
177
178 # User reached route via POST (as by submitting a form via POST)
179 if request.method == "POST":
180
181 # Ensure username was submitted
182 if not request.form.get("username"):
183 return apology("must provide username", 403)
184
185 # Ensure password was submitted
186 elif not request.form.get("password"):
187 return apology("must provide password", 403)
188
189 # Query database for username
190 rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
191
192 # Ensure username exists and password is correct
193 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
194 return apology("invalid username and/or password", 403)
195
196 # Remember which user has logged in
197 session["user_id"] = rows[0]["id"]
198
199 # Redirect user to home page
200 return redirect("/")
201
202 # User reached route via GET (as by clicking a link or via redirect)
203 else:
204 return render_template("login.html")
205
206
207@app.route("/logout")
208def logout():
209 """Log user out"""
210
211 # Forget any user_id
212 session.clear()
213
214 # Redirect user to login form
215 return redirect(url_for("login"))
216
217
218@app.route("/quote", methods=["GET", "POST"])
219@login_required
220def quote():
221 """Get stock quote."""
222 # if user reached route via POST (as by submitting a form via POST)
223 if request.method == "POST":
224
225 # ensure name of stock was submitted
226 if not request.form.get("symbol"):
227 return apology("must provide stock symbol")
228
229 # pull quote from yahoo finance
230 stocksymbol = request.form.get("symbol").upper()
231 stock = lookup(stocksymbol)
232
233 # check is valid stock name provided
234 if stock == None:
235 return apology("Stock symbol not valid, please try again", 400)
236
237 # stock name is valid
238 return render_template("quoted.html", stock={
239 'name': stock['name'],
240 'symbol': stock['symbol'],
241 'price': usd(stock['price'])
242 })
243
244 # else if user reached route via GET (as by clicking a link or via redirect)
245 else:
246 return render_template("quote.html")
247
248
249@app.route("/register", methods=["GET", "POST"])
250def register():
251 """Register user"""
252 # return apology("TODO")
253 # if user reached route via POST (as by submitting a form via POST)
254 if request.method == "POST":
255 username = request.form.get("username")
256 password = request.form.get("password")
257
258 if not username:
259 return apology("must provide username")
260
261 elif not password:
262 return apology("must provide password")
263
264 if request.form.get("password") != request.form.get("confirmation"):
265 return apology("retard alert dingdingdingdingdingding", 400)
266
267 # Access data
268 security_hash = generate_password_hash(request.form.get("password"))
269
270 # Add the user's credentials into the database
271 try:
272 db.execute("insert into users (username, hash) values(?, ?)", username, security_hash)
273 except:
274 return apology("username already exists")
275 return redirect("/")
276
277 else:
278 return render_template("register.html")
279#HTML wordt niet geschreven> alleen python en SQL.
280# Sql injection werkt niet, want gebruik van vraagteken
281
282@app.route("/sell", methods=["GET", "POST"])
283@login_required
284def sell():
285 """Sell shares of stock"""
286 # if user reached route via POST (as by submitting a form via POST)
287 if request.method == "POST":
288 print(request.form.get("symbol"))
289
290 # ensure stock symbol and number of shares was submitted
291 if (not request.form.get("symbol")) or (not request.form.get("shares")):
292 return apology("must provide stock symbol and number of shares")
293
294 # ensure number of shares is valid
295 if int(request.form.get("shares")) <= 0:
296 return apology("must provide valid number of shares (integer)")
297
298 symbol = request.form.get("symbol").upper()
299 shares = int(request.form.get("shares"))
300 stock = lookup(symbol)
301
302 if stock is None:
303 return apology("invalid symbol")
304
305 rows = db.execute("""
306 SELECT symbol, SUM(shares) AS totalshares
307 FROM transactions
308 WHERE user_id=:user_id
309 GROUP BY symbol
310 HAVING SUM(shares) > 0;
311 """, user_id=session["user_id"])
312
313 for row in rows:
314 if row["symbol"] == symbol:
315 if shares > row["totalshares"]:
316 return apology("too many shares")
317
318 rows = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id"])
319 cash = rows[0]["cash"]
320
321 updated_cash = cash + (shares * stock['price'])
322 db.execute("UPDATE users SET cash=:updated_cash WHERE id=:id",
323 updated_cash=updated_cash,
324 id=session["user_id"])
325
326 db.execute("""
327 INSERT INTO transactions
328 (user_id, symbol, shares, price)
329 VALUES (:user_id, :symbol, :shares, :price)
330 """,
331 user_id=session["user_id"],
332 symbol=stock["symbol"],
333 shares=-1 * shares,
334 price=stock["price"]
335 )
336 flash("Sold!")
337 return redirect(url_for('.index'))
338
339 # else if user reached route via GET (as by clicking a link or via redirect)
340 else:
341 rows = db.execute("""
342 SELECT symbol
343 FROM transactions
344 WHERE user_id=:user_id
345 GROUP BY symbol
346 HAVING shares >0;
347 """, user_id=session["user_id"])
348 return render_template("sell.html", symbols=[row["symbol"] for row in rows])
349
350
351# errorhandler
352def errorhandler(e):
353 """Handle error"""
354 if not isinstance(e, HTTPException):
355 e = InternalServerError()
356 return apology(e.name, e.code)
357
358
359# listen for errors
360for code in default_exceptions:
361 app.errorhandler(code)(errorhandler)
362