· 4 years ago · Jun 15, 2021, 03:18 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# Key is = pk_6b509ceccabf4f3aab16e654d1abafe5
45
46
47@app.route("/")
48@login_required
49def index():
50 """Show portfolio of stocks"""
51
52 rows = db.execute("""
53 SELECT symbol, SUM(shares) as totalshares
54 FROM transactions
55 WHERE user_id = ?
56 GROUP BY symbol
57 HAVING totalshares > 0;
58 """, session["user_id"])
59 holdings = []
60 grand_total = 0
61 for row in rows:
62 stock = lookup(row["symbol"])
63 holdings.append({
64 "symbol": stock["symbol"],
65 "name": stock["name"],
66 "shares": row["totalshares"],
67 "price": stock["price"],
68 "total": usd(stock["price"] * row["totalshares"])
69 })
70 grand_total += stock["price"] * row["totalshares"]
71
72 # get user cash total
73 rows = db.execute("SELECT cash FROM users WHERE id=?", session["user_id"])
74 cash = rows[0]['cash']
75 grand_total += cash
76
77 return render_template("index.html", holdings=holdings, cash=usd(cash), grand_total=usd(grand_total))
78
79
80@app.route("/buy", methods=["GET", "POST"])
81@login_required
82def buy():
83 """Buy shares of stock"""
84
85 if request.method == "POST":
86 lookup_response = lookup(request.form.get("symbol").upper())
87 if lookup_response is None:
88 return apology("Could not find symbol.")
89
90 rows = db.execute("SELECT * FROM users WHERE id = ?",
91 session["user_id"])
92
93 try:
94 shares = int(request.form.get('shares'))
95 except:
96 return apology("Give a round number")
97
98 if shares <= 0:
99 return apology("Give a positive integer")
100
101 total_cost = lookup_response['price'] * shares
102 user_cash = rows[0]['cash']
103 if user_cash < total_cost:
104 return apology("Insufficient cash.")
105
106 else:
107 db.execute("INSERT INTO transactions (user_id, action, symbol, shares, price) VALUES (?, 'purchase', ?, ?, ?)",
108 session["user_id"], request.form.get("symbol").upper(), shares, lookup_response['price'])
109 db.execute("UPDATE users SET cash=? WHERE id=?",
110 user_cash-total_cost, session["user_id"])
111 flash("Bought!")
112 return redirect("/")
113 else:
114 return render_template("buy.html")
115
116
117@app.route("/history")
118@login_required
119def history():
120 """Show history of transactions"""
121 History = db.execute("SELECT * FROM transactions WHERE user_id=?", session["user_id"])
122 return render_template("history.html", history=history)
123
124
125@app.route("/login", methods=["GET", "POST"])
126def login():
127 """Log user in"""
128
129 # Forget any user_id
130 session.clear()
131
132 # User reached route via POST (as by submitting a form via POST)
133 if request.method == "POST":
134
135 # Ensure username was submitted
136 if not request.form.get("username"):
137 return apology("must provide username", 400)
138
139 # Ensure password was submitted
140 elif not request.form.get("password"):
141 return apology("must provide password", 400)
142
143 # Query database for username
144 rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
145
146 # Ensure username exists and password is correct
147 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
148 return apology("invalid username and/or password", 400)
149
150 # Remember which user has logged in
151 session["user_id"] = rows[0]["id"]
152
153 # Redirect user to home page
154 return redirect("/")
155
156 # User reached route via GET (as by clicking a link or via redirect)
157 else:
158 return render_template("login.html")
159
160
161@app.route("/logout")
162def logout():
163 """Log user out"""
164
165 # Forget any user_id
166 session.clear()
167
168 # Redirect user to login form
169 return redirect("/")
170
171
172@app.route("/quote", methods=["GET", "POST"])
173@login_required
174def quote():
175 """Get stock quote."""
176 if request.method == "GET":
177 return render_template("quote.html")
178
179 if request.method == "POST":
180 lookup_response = lookup(request.form.get("symbol"))
181 if lookup_response is None:
182 return apology("Could not find symbol.")
183 else:
184 return render_template("quoted.html", lookup_response={
185 'name': lookup_response['name'],
186 'symbol': lookup_response['symbol'],
187 'price': usd(lookup_response['price'])
188 })
189
190
191@app.route("/register", methods=["GET", "POST"])
192def register():
193 """Register user"""
194 if request.method == "GET":
195 return render_template("register.html")
196
197 if request.method == "POST":
198
199 # Ensure username was submitted
200 if not request.form.get("username"):
201 return apology("must provide username", 400)
202
203 # Query database for username
204 rows = db.execute("SELECT * FROM users WHERE username = ?",
205 request.form.get("username"))
206
207 # Check if a user with that username already exists
208 if len(rows) != 0:
209 return apology("username already exists.", 400)
210
211 # Ensure password was submitted and matches the confirmation
212 if not request.form.get("password"):
213 return apology("must provide password", 400)
214
215 if request.form.get("password") != request.form.get("confirmation"):
216 return apology("confirmation must match password", 400)
217
218 else:
219 db.execute("INSERT INTO users (username, hash) VALUES (?, ?)",
220 request.form.get("username"), generate_password_hash(request.form.get("password")))
221 return render_template("login.html")
222
223# html wordt niet geschreven-> alleen python en sql.
224# Sql injection werkt niet, want gebruik van vraagteken.
225
226@app.route("/sell", methods=["GET", "POST"])
227@login_required
228def sell():
229 """Sell shares of stock"""
230 #
231
232 if request.method == "POST":
233 print(request.form.get("symbol"))
234
235 # ensure stock symbol and number of shares was submitted
236 if (not request.form.get("symbol")) or (not request.form.get("shares")):
237 return apology("must provide stock symbol and number of shares")
238
239 # ensure number of shares is valid
240 if int(request.form.get("shares")) <= 0:
241 return apology("must provide valid number of shares (integer)")
242
243 symbol = request.form.get("symbol").upper()
244 shares = int(request.form.get("shares"))
245 stock = lookup(symbol)
246
247 if stock is None:
248 return apology("invalid symbol")
249
250 rows = db.execute("""
251 SELECT symbol, SUM(shares) AS totalshares
252 FROM transactions
253 WHERE user_id=?
254 GROUP BY symbol
255 HAVING SUM(shares) > 0;
256 """, session["user_id"])
257
258 for row in rows:
259 if row["symbol"] == symbol:
260 if shares > row["totalshares"]:
261 return apology("too many shares")
262
263 rows = db.execute("SELECT cash FROM users WHERE id=?", session["user_id"])
264 cash = rows[0]["cash"]
265
266 updated_cash = cash + (shares * stock['price'])
267 db.execute("UPDATE users SET cash=? WHERE id=?",
268 updated_cash,
269 session["user_id"])
270
271 db.execute("""
272 INSERT INTO transactions
273 (user_id, action, symbol, shares, price)
274 VALUES (?, 'sale', ?, ?, ?)
275 """,
276 session["user_id"],
277 stock["symbol"],
278 -1 * shares,
279 stock["price"]
280 )
281 flash("Sold!")
282 return redirect("/")
283
284 # else if user reached route via GET (as by clicking a link or via redirect)
285 else:
286 rows = db.execute("""
287 SELECT symbol
288 FROM transactions
289 WHERE user_id=?
290 GROUP BY symbol
291 HAVING shares >0;
292 """, session["user_id"])
293 return render_template("sell.html", symbols=[row["symbol"] for row in rows])
294
295
296def errorhandler(e):
297 """Handle error"""
298 if not isinstance(e, HTTPException):
299 e = InternalServerError()
300 return apology(e.name, e.code)
301
302
303# Listen for errors
304for code in default_exceptions:
305 app.errorhandler(code)(errorhandler)
306