· 4 years ago · Sep 02, 2021, 09:16 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
45@app.route("/")
46@login_required
47def index():
48 """Show portfolio of stocks"""
49 user_id = session["user_id"]
50
51 stocks = db.execute("SELECT symbol, name, price, SUM(shares) as totalShares FROM transactions WHERE user_id = ? GROUP BY symbol", user_id)
52 cash = db.execute("SELECT cash FROM users WHERE id = ?", user_id)[0]["cash"]
53
54 total = cash
55
56 for stock in stocks:
57 total += stock["price"] * stock["totalShares"]
58
59
60 return render_template("index.html", stocks=stocks, cash=cash, usd=usd, total=total)
61
62@app.route("/buy", methods=["GET", "POST"])
63@login_required
64def buy():
65 """Buy shares of stock"""
66 if request.method == "POST":
67 symbol = request.form.get("symbol").upper()
68 item = lookup(symbol)
69
70 if not symbol:
71 return apology("Please enter a symbol!")
72 elif not item:
73 return apology("Invalid symbol!")
74
75 try:
76 shares = int(request.form.get("shares"))
77 except:
78 return apology("Shares must be integer!")
79
80 if shares <= 0:
81 return apology("Shares must be a positive integer!")
82
83 user_id = session["user_id"]
84 cash = db.execute("SELECT cash FROM users WHERE id = ?", user_id)[0]["cash"]
85
86 item_name = item["name"]
87 item_price = item["price"]
88 total_price = item_price * shares
89
90 if cash < total_price:
91 return apology("Not enough cash!")
92 else:
93 db.execute("UPDATE users SET cash = ? WHERE id = ?", cash - total_price, user_id)
94 db.execute("INSERT INTO transactions (user_id, name, shares, price, type, symbol) VALUES (?, ?, ?, ?, ?, ?)",
95 user_id, item_name, shares, item_price, 'buy', symbol)
96
97 return redirect('/')
98 else:
99 return render_template("buy.html")
100
101
102@app.route("/history")
103@login_required
104def history():
105 """Show history of transactions"""
106 user_id = session["user_id"]
107 transactions = db.execute("SELECT type, symbol, price, shares, time FROM transactions WHERE user_id = ?", user_id)
108
109 return render_template("history.html", transactions=transactions, usd=usd)
110
111
112@app.route("/login", methods=["GET", "POST"])
113def login():
114 """Log user in"""
115
116 # Forget any user_id
117 session.clear()
118
119 # User reached route via POST (as by submitting a form via POST)
120 if request.method == "POST":
121
122 # Ensure username was submitted
123 if not request.form.get("username"):
124 return apology("must provide username", 403)
125
126 # Ensure password was submitted
127 elif not request.form.get("password"):
128 return apology("must provide password", 403)
129
130 # Query database for username
131 rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
132
133 # Ensure username exists and password is correct
134 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
135 return apology("invalid username and/or password", 403)
136
137 # Remember which user has logged in
138 session["user_id"] = rows[0]["id"]
139
140 # Redirect user to home page
141 return redirect("/")
142
143 # User reached route via GET (as by clicking a link or via redirect)
144 else:
145 return render_template("login.html")
146
147
148@app.route("/logout")
149def logout():
150 """Log user out"""
151
152 # Forget any user_id
153 session.clear()
154
155 # Redirect user to login form
156 return redirect("/")
157
158
159@app.route("/quote", methods=["GET", "POST"])
160@login_required
161def quote():
162 """Get stock quote."""
163 if request.method == "POST":
164 symbol = request.form.get("symbol")
165
166 if not symbol:
167 return apology("Please enter a symbol!")
168
169 item = lookup(symbol)
170
171 if not item:
172 return apology("Invalid symbol!")
173
174 return render_template("quoted.html", item=item, usd_function=usd)
175 else:
176 return render_template("quote.html")
177
178
179@app.route("/register", methods=["GET", "POST"])
180def register():
181 """Register user"""
182 if (request.method == "POST"):
183 username = request.form.get('username')
184 password = request.form.get('password')
185 confirmation = request.form.get('confirmation')
186
187 if not username:
188 return apology('Username is required!')
189 elif not password:
190 return apology('Password is required!')
191 elif not confirmation:
192 return apology('Password confirmation is required!')
193
194 if password != confirmation:
195 return apology('Password do not match!')
196
197 hash = generate_password_hash(password)
198
199
200 try:
201 db.execute("INSERT INTO users (username, hash) VALUES (?, ?)", username, hash)
202 return redirect('/')
203 except:
204 return apology('Username has already been registered!')
205 else:
206 return render_template("register.html")
207
208
209@app.route("/sell", methods=["GET", "POST"])
210@login_required
211def sell():
212 """Sell shares of stock"""
213
214 if request.method == "POST":
215 user_id = session["user_id"]
216 symbol = request.form.get("symbol")
217 shares = int(request.form.get("shares"))
218
219 if shares <= 0:
220 return apology("Shares must be a positive number!")
221
222 item_price = lookup(symbol)["price"]
223 item_name = lookup(symbol)["name"]
224 price= shares * item_price
225
226 shares_owned = db.execute("SELECT shares FROM transactions WHERE user_id = ? AND symbol=? GROUP BY symbol", user_id, symbol)[0]["shares"]
227
228 if shares_owned < shares:
229 return apology("You do not have enough shares!")
230
231 current_cash = db.execute("SELECT cash FROM users WHERE id = ?", user_id)[0]["cash"]
232
233 db.execute("UPDATE users SET cash = ? WHERE id = ?", current_cash + price, user_id)
234 db.execute("INSERT INTO transactions (user_id, name, shares, price, type, symbol) VALUES (?, ?, ?, ?, ?, ?)",
235 user_id, item_name, -shares, item_price, "sell", symbol)
236 return redirect('/')
237 else:
238 user_id = session["user_id"]
239 symbols = db.execute("SELECT symbol FROM transactions WHERE user_id = ? GROUP BY symbol", user_id)
240 return render_template("sell.html", symbols=symbols)
241
242
243def errorhandler(e):
244 """Handle error"""
245 if not isinstance(e, HTTPException):
246 e = InternalServerError()
247 return apology(e.name, e.code)
248
249
250# Listen for errors
251for code in default_exceptions:
252 app.errorhandler(code)(errorhandler)
253