· 5 years ago · Mar 03, 2021, 06:14 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 portfolio = db.execute("SELECT stock, SUM(quantity) FROM transactions WHERE id = ? GROUP BY stock", session["user_id"])
50 user_cash = db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"])[0]['cash']
51
52 print(user_cash)
53 portfolio_total = 0
54 for stock in portfolio:
55 stock['name'] = lookup(stock['stock'])['name']
56 stock['cmp'] = lookup(stock['stock'])['price']
57 stock['total'] = stock['cmp'] * stock['SUM(quantity)']
58 portfolio_total += stock['total']
59
60 portfolio_total += user_cash
61
62 #stock_symbol = portfolio["stock"]
63 #stock_price = lookup(request.form.get("symbol"))['price']
64 #stock_name = lookup(request.form.get("symbol"))['price']
65 #print(portfolio)
66 return render_template("index.html", portfolio = portfolio, portfolio_total = portfolio_total, user_cash = user_cash, alert_type = "d-none", alert_text = "")
67
68
69@app.route("/buy", methods=["GET", "POST"])
70@login_required
71def buy():
72 """Buy shares of stock"""
73 if request.method == "POST":
74 # TODO
75 if not (request.form.get("symbol")):
76 return apology("Please enter a valid symbol and retry.")
77
78 if lookup(request.form.get("symbol")) == None:
79 return apology("Stock Symbol doesn't exist. Check for typos and retry.")
80
81 if not (request.form.get("shares") and int(float(request.form.get("shares"))) >= 1):
82 return apology("Please enter a correct number of shares to buy.")
83
84 # if not isinstance(int(request.form.get("shares"), int)):
85 # return apology("Please enter a correct number of shares to buy.")
86
87 stock_symbol = request.form.get("symbol").upper()
88
89 stock_price = lookup(request.form.get("symbol"))['price']
90
91 number_shares = int(request.form.get("shares"))
92
93 user_cash = (db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"]))[0]["cash"]
94
95 if ((stock_price * number_shares) > user_cash):
96 return apology("Insufficient Funds")
97
98 # If everything is in order, initiate purchase transaction by deducting user's cash and updating the db
99 user_cash -= round(stock_price * number_shares, 2)
100 db.execute("UPDATE users SET cash = ? WHERE id = ?", user_cash, session["user_id"])
101
102 # If everything is in order, add the purchase transaction into the transactions db
103 db.execute("INSERT INTO transactions(id, stock, quantity, transact_price, total_amount, date_time, buy_sell) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, ?)", session["user_id"], stock_symbol, number_shares, stock_price, round(number_shares*stock_price, 2), "BUY")
104
105 return render_template("buy.html", alert_type = "alert-success", alert_text = "Purchase Successful!")
106
107 else:
108 return render_template("buy.html")
109
110
111@app.route("/history")
112@login_required
113def history():
114 """Show history of transactions"""
115 trans_history = db.execute("SELECT stock, quantity, transact_price, date_time, buy_sell FROM transactions WHERE id = ?", session["user_id"])
116
117 return render_template("history.html", trans_history = trans_history)
118
119
120@app.route("/login", methods=["GET", "POST"])
121def login():
122 """Log user in"""
123
124 # Forget any user_id
125 session.clear()
126
127 # User reached route via POST (as by submitting a form via POST)
128 if request.method == "POST":
129
130 # Ensure username was submitted
131 if not request.form.get("username"):
132 return apology("must provide username", 403)
133
134 # Ensure password was submitted
135 elif not request.form.get("password"):
136 return apology("must provide password", 403)
137
138 # Query database for username
139 rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
140
141 # Ensure username exists and password is correct
142 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
143 return apology("invalid username and/or password", 403)
144
145 # Remember which user has logged in
146 session["user_id"] = rows[0]["id"]
147
148 # Redirect user to home page
149 return redirect("/")
150
151 # User reached route via GET (as by clicking a link or via redirect)
152 else:
153 return render_template("login.html")
154
155
156@app.route("/logout")
157def logout():
158 """Log user out"""
159
160 # Forget any user_id
161 session.clear()
162
163 # Redirect user to login form
164 return redirect("/")
165
166
167@app.route("/quote", methods=["GET", "POST"])
168@login_required
169def quote():
170 """Get stock quote."""
171 if request.method == "POST":
172 if not request.form.get("symbol"):
173 return apology("must enter a valid stock symbol", 400)
174
175 if lookup(request.form.get("symbol")) == None:
176 return apology("Stock Symbol doesn't exist. Check for typos and retry.")
177
178 quoteapi = lookup(request.form.get("symbol"))
179
180 # Format the stock price in USD
181 quoteapi['price'] = usd(quoteapi['price'])
182
183 return render_template("quoted.html", stockquotehtml = quoteapi)
184 else:
185 return render_template("quote.html")
186
187
188@app.route("/register", methods=["GET", "POST"])
189def register():
190 """Register user"""
191
192 # Forget any user_id
193 session.clear()
194
195 # User reached route via POST (as by submitting a form via POST)
196 if request.method == "POST":
197
198 # Ensure username was submitted
199 if not request.form.get("username"):
200 return apology("must provide username", 400)
201
202 # Ensure password was submitted
203 elif not request.form.get("password"):
204 return apology("must provide password", 400)
205
206 # password was confirmed
207 elif not request.form.get("confirmation") or not (request.form.get("password") == request.form.get("confirmation")):
208 return apology("must confirm password properly", 400)
209
210 # Query database for username. If it already exists, ask user to use another name.
211 rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
212
213 if len(rows) != 0:
214 return apology("The username is already taken. Please choose another one.", 400)
215
216 # Once the user input is validated, register the user by adding the details into the db after hashing the password.
217 db.execute("INSERT INTO users (username, hash) VALUES (?,?)", request.form.get("username"), generate_password_hash(request.form.get("password")))
218
219 # Take user to the home screen
220 return redirect("/")
221
222 else:
223 return render_template("register.html")
224
225
226@app.route("/sell", methods=["GET", "POST"])
227@login_required
228def sell():
229 """Sell shares of stock"""
230
231 portfolio_stocklist = []
232
233 if request.method == "POST":
234 stock_symbol = request.form.get("symbol")
235 print(stock_symbol)
236 sell_shares = int(request.form.get("shares"))
237
238 portfolio_stocklist_db = db.execute("SELECT stock FROM transactions WHERE id = ? GROUP BY stock HAVING SUM(quantity) > 0", session["user_id"])
239
240 for stock in portfolio_stocklist_db:
241 portfolio_stocklist.extend(list(stock.values()))
242
243 if not stock_symbol or stock_symbol not in portfolio_stocklist:
244 return apology("Please choose a stock you want to sell from your portfolio.")
245
246 if not sell_shares or not (sell_shares > 0):
247 return apology("Please enter a correct number of shares to sell.")
248
249 user_shares = db.execute("SELECT SUM(quantity) FROM transactions WHERE id = ? AND stock = ? GROUP BY stock", session["user_id"], stock_symbol)[0]['SUM(quantity)']
250
251 print(user_shares)
252
253 if sell_shares > user_shares:
254 return apology("Number of shares selected is more than shares in portfolio.")
255
256 stock_price = lookup(stock_symbol)['price']
257
258 # If all the above conditions are met, initiate the sale process
259 db.execute("INSERT INTO transactions(id, stock, quantity, transact_price, total_amount, date_time, buy_sell) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, ?)", session["user_id"], stock_symbol, -sell_shares, stock_price, round(-sell_shares*stock_price, 2), "SELL")
260
261 # Credit the user's cash by the sale amount and update it in the database
262 user_cash = (db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"]))[0]["cash"] + round(sell_shares * stock_price, 2)
263 db.execute("UPDATE users SET cash = ? WHERE id = ?", user_cash, session["user_id"])
264
265 return render_template("sell.html", alert_type = "alert-success", alert_text = "Sale Successful!")
266
267 else:
268 portfolio_stocklist_db = db.execute("SELECT stock FROM transactions WHERE id = ? GROUP BY stock HAVING SUM(quantity) > 0", session["user_id"])
269
270 for stock in portfolio_stocklist_db:
271 portfolio_stocklist.extend(list(stock.values()))
272
273 return render_template("sell.html", stock_list = portfolio_stocklist)
274
275@app.route("/topup", methods=["GET", "POST"])
276@login_required
277def topup():
278
279 if request.method == "POST":
280 topup = int(request.form.get("amount"))
281
282 if not topup or not (topup > 0):
283 return apology("Please enter a correct amount to top up.")
284
285 # Add money to user's account
286 user_cash = (db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"]))[0]["cash"]
287 db.execute("UPDATE users SET cash = ? WHERE id = ?", user_cash + topup, session["user_id"])
288
289 return render_template("topup.html", alert_type = "alert-success", alert_text = usd(topup) + " successfully added to your account.")
290
291 else:
292 return render_template("topup.html")
293
294
295def errorhandler(e):
296 """Handle error"""
297 if not isinstance(e, HTTPException):
298 e = InternalServerError()
299 return apology(e.name, e.code)
300
301
302# Listen for errors
303for code in default_exceptions:
304 app.errorhandler(code)(errorhandler)
305