· 4 years ago · May 17, 2021, 09:40 AM
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@app.route("/login", methods=["GET", "POST"])
163def login():
164 """Log user in"""
165
166 # Forget any user_id
167 session.clear()
168
169 # User reached route via POST (as by submitting a form via POST)
170 if request.method == "POST":
171
172 # Ensure username was submitted
173 if not request.form.get("username"):
174 return apology("must provide username", 403)
175
176 # Ensure password was submitted
177 elif not request.form.get("password"):
178 return apology("must provide password", 403)
179
180 # Query database for username
181 rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
182
183 # Ensure username exists and password is correct
184 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
185 return apology("invalid username and/or password", 403)
186
187 # Remember which user has logged in
188 session["user_id"] = rows[0]["id"]
189
190 # Redirect user to home page
191 return redirect("/")
192
193 # User reached route via GET (as by clicking a link or via redirect)
194 else:
195 return render_template("login.html")
196
197@app.route("/logout")
198def logout():
199 """Log user out"""
200
201 # Forget any user_id
202 session.clear()
203
204 # Redirect user to login form
205 return redirect(url_for("login"))
206
207
208@app.route("/quote", methods=["GET", "POST"])
209@login_required
210def quote():
211 """Get stock quote."""
212 # if user reached route via POST (as by submitting a form via POST)
213 if request.method == "POST":
214
215 # ensure name of stock was submitted
216 if not request.form.get("symbol"):
217 return apology("must provide stock symbol")
218
219 # pull quote from yahoo finance
220 stocksymbol = request.form.get("symbol").upper()
221 stock = lookup(stocksymbol)
222
223 # check is valid stock name provided
224 if stock == None:
225 return apology("Stock symbol not valid, please try again", 400)
226
227 # stock name is valid
228 return render_template("quoted.html", stock={
229 'name': stock['name'],
230 'symbol': stock['symbol'],
231 'price': usd(stock['price'])
232 })
233
234 # else if user reached route via GET (as by clicking a link or via redirect)
235 else:
236 return render_template("quote.html")
237
238
239@app.route("/register", methods=["GET", "POST"])
240def register():
241 """Register user"""
242 #return apology("TODO")
243 # if user reached route via POST (as by submitting a form via POST)
244 if request.method == "POST":
245
246 if request.form.get("password") != request.form.get("confirmation"):
247 return apology("retard alert dingdingdingdingdingding", 412)
248 # Access data
249 username = request.form.get("username")
250 password = request.form.get("password")
251 security_hash = generate_password_hash(request.form.get("password"))
252
253 #Add the user's credentials into the database
254 db.execute("insert into users (username, hash) values(?, ?)", username, security_hash)
255
256 return redirect("/")
257
258 else:
259 return render_template("register.html")
260
261
262@app.route("/sell", methods=["GET", "POST"])
263@login_required
264def sell():
265 """Sell shares of stock"""
266 # if user reached route via POST (as by submitting a form via POST)
267 if request.method == "POST":
268 print(request.formget("symbol"))
269
270 # ensure stock symbol and number of shares was submitted
271 if (not request.form.get("symbol")) or (not request.form.get("shares")):
272 return apology("must provide stock symbol and number of shares")
273
274 # ensure number of shares is valid
275 if int(request.form.get("shares")) <= 0:
276 return apology("must provide valid number of shares (integer)")
277 symbol = request.form.get("symbol").upper()
278 shares = int(request.form.get("shares"))
279 stock = lookup(symbol)
280 if stock is None:
281 return apology("invalid symbol")
282
283 rows = db.execute("""
284 SELECT symbol, SUM(shares) as totalshares
285 FROM transactions
286 WHERE user_id=:user_id
287 GROUP BY symbol
288 HAVING totalshares >0;
289 """, user_id=session["user_id"])
290 for row in rows:
291 if row["symbol"] == symbol:
292 if shares > row["totalshares"]:
293 return apology("too many shares")
294
295 rows = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id")
296 cash = rows[0]["cash"]
297
298 updated_cash = cash + shares * stock['price']
299 db.execute("UPDATE users SET cash=:updated_cash WHERE id=:id",
300 updated cash=updated cash,
301 id=session["user_id"])
302 db.execute("""
303 INSERT into transactions
304 (user_id, symbol, shares, price)
305 VALUES (:user_id :symbol, : shares, :price)
306 """
307 user_id = session["user_id"],
308 symbol = stock["symbol"],
309 shares = -1 * shares,
310 price = stock["price"]
311 )
312 flask("Sold!"")
313 return redirect("/")
314
315 # else if user reached route via GET (as by clicking a link or via redirect)
316 else:
317 rows = db.execute("""
318 SELECT symbol
319 FROM transactions
320 WHERE user_id=:user_id
321 GROUP BY symbol
322 HAVING totalshares >0;
323 """, user_id=session["user_id"])
324 return render_template("sell.html", symbols=[ row["symbol"] for row in rows ])
325
326
327#errorhandler
328def errorhandler(e):
329 """Handle error"""
330 if not isinstance(e, HTTPException):
331 e = InternalServerError()
332 return apology(e.name, e.code)
333
334
335# listen for errors
336for code in default_exceptions:
337 app.errorhandles(code)(errorhandler)
338