· 5 years ago · Jun 09, 2020, 08:06 PM
1import os
2
3from cs50 import SQL
4from flask import Flask, flash, jsonify, 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# 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# Custom filter
27app.jinja_env.filters["usd"] = usd
28
29# Configure session to use filesystem (instead of signed cookies)
30app.config["SESSION_FILE_DIR"] = mkdtemp()
31app.config["SESSION_PERMANENT"] = False
32app.config["SESSION_TYPE"] = "filesystem"
33Session(app)
34
35# Configure CS50 Library to use SQLite database
36db = SQL("sqlite:///finance.db")
37
38# Make sure API key is set
39if not os.environ.get("API_KEY"):
40 raise RuntimeError("API_KEY not set")
41
42
43@app.route("/")
44@login_required
45def index():
46
47 # DELETE AND UPDATE stock row if user has 0 shares
48 db.execute("DELETE FROM shares WHERE symbol IN (SELECT symbol FROM shares WHERE share_id = ? GROUP BY symbol HAVING sum(qty) = 0) ", session['user_id'])
49
50 # Pull information form db.
51 rows = []
52 rows = db.execute("SELECT symbol, company, SUM(qty) FROM shares WHERE share_id = ? GROUP BY company ",
53 session["user_id"])
54 row_cash = db.execute("SELECT cash FROM users WHERE id = ?", session['user_id'])
55 cash = row_cash[0]['cash']
56
57
58
59 # Add full information in list of dictionaries
60 for row in rows:
61 symbol = row['symbol']
62 qty = row['SUM(qty)']
63 result = lookup(symbol)
64 price = result["price"]
65 total = price * qty
66 row.update({'price': usd(price)})
67 row.update({'total': total})
68
69
70 cash_user = {
71 "symbol": 'CASH',
72 "company": " ",
73 "qty": " ",
74 "price": " ",
75 "total": int(cash),
76 }
77 rows.append(cash_user)
78
79 # TOTAL
80 result = usd(sum(item['total'] for item in rows))
81
82
83 return render_template("index.html", rows=rows, result=result)
84
85
86
87
88@app.route("/buy", methods=["GET", "POST"])
89@login_required
90def buy():
91
92 if request.method == "POST":
93
94 # Set Variables
95 symbol = request.form.get("symbol_buy")
96 qty = request.form.get("share_buy")
97 result = lookup(symbol)
98
99 # check symbol
100 if not result:
101 return apology("Wrong Symbol")
102
103 # check number of shares
104 elif not qty:
105 return apology("Enter Number of Shares")
106
107 # get user's cash info from db
108 rows_cash = db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"])
109 cash = rows_cash[0]["cash"]
110
111
112 real_price = result['price']
113 total = real_price * float(qty)
114 company = result["name"]
115
116
117 # check if user got enough cash
118 if total > cash:
119 return apology("not sufficient funds") # ALERT AND FORM TO ADD MORE CASH
120
121 # Insert purchase in db
122 db.execute("INSERT INTO shares (symbol, qty, company, share_id) VALUES (?, ?, ?, ?)",
123 symbol.upper(), int(qty), company, session['user_id'])
124
125 # update cash value in db
126 db.execute("UPDATE users SET cash = ? WHERE id = ?",
127 (cash - total), session["user_id"])
128
129 # Insert transaction information in history db
130 db.execute("INSERT INTO history (symbol, share, price, trans_id) VALUES ( ?, ?, ?, ?)",
131 symbol.upper(), int(qty), float(real_price), session['user_id'] )
132
133 # Flash message
134 if int(qty) > 1:
135 flash(f"You bought {qty} shares of {company} !")
136 else:
137 flash(f"You bought {qty} share of {company} !")
138 return redirect("/")
139
140
141 else:
142 return render_template("buy.html")
143
144
145
146
147@app.route("/history")
148@login_required
149def history():
150
151 # Pull info about all transactions from history.db
152 row_history = db.execute("SELECT symbol, share, price, date FROM history WHERE trans_id = ?", session['user_id'])
153
154
155 return render_template("history.html", row_history = row_history)
156
157@app.route("/login", methods=["GET", "POST"])
158def login():
159 """Log user in"""
160
161 # Forget any user_id
162 session.clear()
163
164 # User reached route via POST (as by submitting a form via POST)
165 if request.method == "POST":
166
167 # Ensure username was submitted
168 if not request.form.get("username"):
169 return apology("must provide username", 403)
170
171 # Ensure password was submitted
172 elif not request.form.get("password"):
173 return apology("must provide password", 403)
174
175 # Query database for username
176 rows = db.execute("SELECT * FROM users WHERE username = :username",
177 username=request.form.get("username"))
178
179 # Ensure username exists and password is correct
180 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
181 return apology("invalid username and/or password", 403)
182
183 # Remember which user has logged in
184 session["user_id"] = rows[0]["id"]
185
186 # Redirect user to home page
187 flash("You Logged In!")
188 return redirect("/")
189
190 # User reached route via GET (as by clicking a link or via redirect)
191 else:
192 return render_template("login.html")
193
194
195@app.route("/logout")
196def logout():
197 """Log user out"""
198
199 # Forget any user_id
200 session.clear()
201
202 flash("You Log Out! ")
203 # Redirect user to login form
204 return redirect("/")
205
206
207@app.route("/quote", methods=["GET", "POST"])
208@login_required
209def quote():
210
211 if request.method == "POST":
212
213 symbol = request.form.get("symbol")
214
215 result = lookup(symbol)
216
217 # check symbol
218 if not result:
219 return apology("Wrong Symbol")
220
221 price = usd(result["price"])
222
223 # Return to new html with information
224 return render_template("quoted.html", result=result, price=price )
225
226 else:
227 return render_template("quote.html")
228
229
230@app.route("/register", methods=["GET", "POST"])
231def register():
232
233 # Clear session
234 session.clear()
235
236 if request.method == "POST":
237
238 user = request.form.get("username_register")
239 password = request.form.get("password_register")
240 password2 = request.form.get("password_register2")
241
242 # check user input
243 if not user:
244 return apology("must provide username")
245
246 # check password input
247 elif not password:
248 return apology("must provide password", 403)
249
250 # check if passwords match
251 elif password != password2:
252 return apology("must use same password", 403)
253
254 # pull information from db if there is same username
255 row_check = db.execute("SELECT username FROM users WHERE username = ?", user)
256
257 # hash password
258 if not row_check:
259 hash_password = generate_password_hash(password, method='pbkdf2:sha256', salt_length=8)
260
261 # Add hash value in db
262 db.execute("INSERT INTO users (username, hash) VALUES (?, ?)",
263 user, hash_password)
264
265 flash("Registred!")
266 return redirect("/")
267
268 else:
269 return apology("Username already in use", 403)
270
271
272 else:
273 return render_template("register.html")
274
275
276
277
278@app.route("/sell", methods=["GET", "POST"])
279@login_required
280def sell():
281
282 # Pull information about owned shares
283 rows = db.execute("SELECT symbol, SUM(qty) FROM shares WHERE share_id = ? GROUP BY company", session['user_id'])
284
285
286
287 if request.method == "POST":
288
289 row_cash = db.execute("SELECT cash FROM users WHERE id = ?", session['user_id'])
290 cash = row_cash[0]['cash']
291
292 symbol = request.form.get("symbol")
293 qty = request.form.get("shares")
294
295
296 # check symbol
297 if not symbol:
298 return apology("Select symbol")
299
300 # check shares input
301 elif not qty:
302 return apology("select shares")
303 else:
304
305 # check if user has enough shares
306 for row in rows:
307 if symbol == row['symbol'] and int(qty) > row['SUM(qty)']:
308 return apology('not enough shares')
309
310 result = lookup(symbol)
311 price = result['price']
312 total = float(qty) * float(price) + cash
313
314 # Insert in db transaction information
315 db.execute("INSERT INTO history (symbol, share, trans_id, price) VALUES (?, ?, ?, ?)",
316 symbol.upper(), -int(qty), session['user_id'], float(price))
317
318 # Insert in db sell information
319 db.execute("INSERT INTO shares (symbol, company, qty, share_id) VALUES (?, ?, ?, ?)",
320 symbol.upper(), result['name'], -int(qty), session['user_id'])
321
322 # Update user's cash
323 db.execute("UPDATE users SET cash = ? WHERE id = ?",
324 total, session['user_id'])
325
326 if int(qty) > 1:
327 flash(f"You sold {qty} shares of {result['name']} !")
328 else:
329 flash(f"You sold {qty} share of {result['name']} !")
330 return redirect ("/")
331
332 else:
333 return render_template("sell.html", rows=rows)
334
335@app.route("/add", methods=["GET", "POST"])
336@login_required
337def add():
338
339
340
341 if request.method == "POST":
342
343 amount = request.form.get("add")
344
345 # Update user's cash
346 db.execute("UPDATE users SET cash = cash + ? WHERE id = ?", amount, session['user_id'])
347
348 flash(f"${amount} Added successfully!")
349 return redirect("/")
350 else:
351 return render_template("add.html")
352
353
354def errorhandler(e):
355 """Handle error"""
356 if not isinstance(e, HTTPException):
357 e = InternalServerError()
358 return apology(e.name, e.code)
359
360
361# Listen for errors
362for code in default_exceptions:
363 app.errorhandler(code)(errorhandler)