· 4 years ago · May 16, 2021, 02:34 PM
1import os
2
3from cs50 import SQL
4from flask import Flask, flash, redirect, render_template, request, session, url_for
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@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 holdingd=[]
72 for row in rows:
73 stock = lookup(row["symbol"])
74 holdings.append({
75 "symbol": stock["symbol"],
76 "name": stock["name"],
77 "shares": rows["totalshares"],
78 "price": stock["price"],
79 "total": usd(stock["price"] * rows["totalshares"])
80 })
81 grand_total += stock["price"] * row["totalshares"]
82 # get user cash total
83 rows = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id"])
84 cash = rows[0]['cash']
85 grand_total += cash
86
87 return render_template("index.html", holdings=holdings, cash=usd(cash), grand_total=usd(grand_total))
88
89
90@app.route("/buy", methods=["GET", "POST"])
91@login_required
92def buy():
93 """Buy shares of stock"""
94 # if user reached route via POST (as by submitting a form via POST)
95 if request.method == "POST":
96
97 # ensure stock symbol and number of shares was submitted
98 if (not request.form.get("symbol")) or (not request.form.get("shares")):
99 return apology("must provide stock symbol and number of shares")
100
101 # ensure number of shares is valid
102 if int(request.form.get("shares")) <= 0:
103 return apology("must provide valid number of shares (integer)")
104
105 # pull quote from yahoo finance
106 symbol = request.form.get("symbol").upper()
107 shares = int(request.form.get("shares"))
108 stock = lookup(symbol)
109 if stock == None:
110 return apology("invalid symbol")
111
112 # check if user has enough cash for transaction
113 rows = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id"])
114 cash = rows[0]["cash"]
115
116 updated_cash = cash + shares * stock['price']
117 if updated_cash < 0:
118 return apology("can't afford")
119 db.execute("UPDATE users SET cash=:updated_cash WHERE id=:id",
120 updated_cash=updated_cash,
121 id=session["user_id"])
122 db.execute("""
123 INSERT INTO transactions
124 (user_id, symbol, shares, price)
125 VALUES (:user_id, :symbol, :shares, :price)
126 """,
127 user_id = session["user_id"],
128 symbol = stock["symbol"],
129 shares = shares,
130 price = stock["price"]
131 )
132 flash("Bought!")
133 return redirect("/")
134
135 # else if user reached route via GET (as by clicking a link or via redirect)
136 else:
137 rows= db.execute("""
138 SELECT symbol
139 FROM transactions
140 WHERE user_id=:user_id
141 GROUP BY symbol
142 HAVING SUM(shares) > 0;
143 """, user_id=session["user_id"])
144 return render_template("sell.html", symbols=[ row["symbol"] for row in rows ])
145
146
147@app.route("/history")
148@login_required
149def history():
150 """Show history of transactions"""
151 # pull all transactions belonging to user
152 transactions = db.execute("""
153 SELECT symbol, shares, price, transacted
154 FROM transations
155 WHERE user_id=:user_id
156 """, user_id=session["user_id"])
157 for i in range(len(transactions)):
158 transactions[i]["pirce"] = usd(transactions[i]["price"])
159 return render_template("history.html", transactions=transactions)
160
161
162
163
164@app.route("/login", methods=["GET", "POST"])
165def login():
166 """Log user in"""
167
168 # Forget any user_id
169 session.clear()
170
171 # User reached route via POST (as by submitting a form via POST)
172 if request.method == "POST":
173
174 # Ensure username was submitted
175 if not request.form.get("username"):
176 return apology("must provide username", 403)
177
178 # Ensure password was submitted
179 elif not request.form.get("password"):
180 return apology("must provide password", 403)
181
182 # Query database for username
183 rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
184
185 # Ensure username exists and password is correct
186 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
187 return apology("invalid username and/or password", 403)
188
189 # Remember which user has logged in
190 session["user_id"] = rows[0]["id"]
191
192 # Redirect user to home page
193 return redirect("/")
194
195 # User reached route via GET (as by clicking a link or via redirect)
196 else:
197 return render_template("login.html")
198
199@app.route("/logout")
200def logout():
201 """Log user out"""
202
203 # Forget any user_id
204 session.clear()
205
206 # Redirect user to login form
207 return redirect(url_for("login"))
208
209
210@app.route("/quote", methods=["GET", "POST"])
211@login_required
212def quote():
213 """Get stock quote."""
214 # if user reached route via POST (as by submitting a form via POST)
215 if request.method == "POST":
216
217 # ensure name of stock was submitted
218 if not request.form.get("symbol"):
219 return apology("must provide stock symbol")
220
221 # pull quote from yahoo finance
222 stocksymbol = request.form.get("symbol").upper()
223 stock = lookup(stocksymbol)
224
225 # check is valid stock name provided
226 if stock == None:
227 return apology("Stock symbol not valid, please try again", 400)
228
229 # stock name is valid
230 return render_template("quoted.html", stock={
231 'name': stock['name'],
232 'symbol': stock['symbol'],
233 'price': usd(stock['price'])
234 })
235
236 # else if user reached route via GET (as by clicking a link or via redirect)
237 else:
238 return render_template("quote.html")
239
240
241@app.route("/register", methods=["GET", "POST"])
242def register():
243 """Register user"""
244 #return apology("TODO")
245 # if user reached route via POST (as by submitting a form via POST)
246 if request.method == "POST":
247
248 if request.form.get("password") != request.form.get("confirmation"):
249 return apology("retard alert dingdingdingdingdingding", 412)
250 # Access data
251 username = request.form.get("username")
252 password = request.form.get("password")
253 security_hash = generate_password_hash(request.form.get("password"))
254
255 #Add the user's credentials into the database
256 db.execute("insert into users (username, hash) values(?, ?)", username, security_hash)
257
258 return redirect("/")
259
260 else:
261 return render_template("register.html")
262
263
264@app.route("/sell", methods=["GET", "POST"])
265@login_required
266def sell():
267 """Sell shares of stock"""
268 # if user reached route via POST (as by submitting a form via POST)
269 if request.method == "POST":
270 print(request.formget("symbol"))
271
272 # ensure stock symbol and number of shares was submitted
273 if (not request.form.get("symbol")) or (not request.form.get("shares")):
274 return apology("must provide stock symbol and number of shares")
275
276 # ensure number of shares is valid
277 if int(request.form.get("shares")) <= 0:
278 return apology("must provide valid number of shares (integer)")
279 symbol = request.form.get("symbol").upper()
280 shares = int(request.form.get("shares"))
281 stock = lookup(symbol)
282 if stock is None:
283 return apology("invalid symbol")
284
285 rows = db.execute("""
286 SELECT symbol, SUM(shares) as totalshares
287 FROM transactions
288 WHERE user_id=:user_id
289 GROUP BY symbol
290 HAVING totalshares >0;
291 """, user_id=session["user_id"])
292 for row in rows:
293 if row["symbol"] == symbol:
294 if shares > row["totalshares"]:
295 return apology("too many shares")
296
297 rows = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id")
298 cash = rows[0]["cash"]
299
300 updated_cash = cash + shares * stock['price']
301 db.execute("UPDATE users SET cash=:updated_cash WHERE id=:id",
302 updated cash=updated cash,
303 id=session["user_id"])
304 db.execute("""
305 INSERT into transactions
306 (user_id, symbol, shares, price)
307 VALUES (:user_id :symbol, : shares, :price)
308 """
309 user_id = session["user_id"],
310 symbol = stock["symbol"],
311 shares = -1 * shares,
312 price = stock["price"]
313 )
314 flask("Sold!"")
315 return redirect("/")
316
317 # else if user reached route via GET (as by clicking a link or via redirect)
318 else:
319 rows = db.execute("""
320 SELECT symbol
321 FROM transactions
322 WHERE user_id=:user_id
323 GROUP BY symbol
324 HAVING totalshares >0;
325 """, user_id=session["user_id"])
326 return render_template("sell.html", symbols=[ row["symbol"] for row in rows ])
327
328
329#errorhandler
330def errorhandler(e):
331 """Handle error"""
332 if not isinstance(e, HTTPException):
333 e = InternalServerError()
334 return apology(e.name, e.code)
335
336
337# listen for errors
338for code in default_exceptions:
339 app.errorhandles(code)(errorhandler)
340