· 6 years ago · Jan 03, 2020, 11:30 AM
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#1. 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
45#2.TODO
46def index():
47 """Show portfolio of stocks"""
48 # look up the current user
49 user = db.execute("SELECT * FROM users WHERE id = :id", id = session["user_id"])
50 user = round(user[0]['cash'])
51 transactions = db.execute("SELECT * FROM history WHERE id = :id", id = session["user_id"])
52 #save in empty list
53 stocks = []
54
55 # getting all data and putting in dict
56 for trans in transactions:
57 print(trans.keys())
58 symbol = trans["symbol"]
59 shares = trans["shares"]
60 stock_data = lookup(symbol)
61
62
63 temp_dict = {
64 "symbol": symbol,
65 "name": stock_data["name"],
66 "shares": shares,
67 "price": round(stock_data["price"], 2),
68 "total": round(stock_data["price"] * shares, 2)
69 }
70 stocks.append(temp_dict)
71
72 #getting user current wealth
73 total_money = round(user + sum([stock["total"] for stock in stocks]), 2)
74
75 return render_template("index.html", stocks = stocks, money = total_money, user = user)
76
77@app.route("/buy", methods=["GET", "POST"])
78@login_required
79#3. TODO
80def buy():
81 """Buy shares of stock"""
82 if request.method == "GET":
83 return render_template("buy.html")
84 else:
85 symbol = request.form.get("symbol")
86 shares = request.form.get("shares")
87
88 if not shares.isdigit() or float(shares) < 1:
89 return apology("Must be an integer and at least 1")
90
91 stock = lookup(symbol)
92
93 # when stock doesnt exist
94 if stock is None or symbol == "":
95 return apology("stock doesnt exist")
96
97 #Looking if user has enough money
98 final_price = float(shares) * stock["price"]
99 user_cash = db.execute("SELECT cash FROM users WHERE id = :id", id = session["user_id"])[0]["cash"]
100
101 if final_price > user_cash:
102 return apology("Insufficient amount of money for this transaction")
103
104 # complete purchasing
105 money_left = user_cash - final_price
106 db.execute(f"UPDATE users SET cash = {money_left} WHERE id = :id", id = session["user_id"])
107
108 #Look if stock already exists
109 stocks = db.execute("SELECT * FROM purchases WHERE stock=:symbol AND id = :id", symbol=symbol, id = session["user_id"])
110
111 if len(stocks) == 0:
112 db.execute("INSERT INTO purchases (id, price, shares, stock) VALUES (:id, :price, :shares, :stock)",
113 id=session["user_id"], price=stock["price"], shares=shares, stock=stock["symbol"])
114 else:
115 db.execute(f"UPDATE purchases set shares = {stocks[0]['shares'] + shares}, price = {stock['price']} WHERE id = :id, stock = :stock", id = session["user_id"], stock = symbol)
116
117 # adding to history
118 db.execute("INSERT INTO history(symbol, price, dt, id, shares, buy) VALUES (:symbol, :price, DATETIME('now'), :id, :shares, :buy)", symbol = symbol, price = stock["price"], id = session["user_id"], shares = shares, buy = 1)
119 return redirect("/")
120
121
122@app.route("/check", methods=["GET"])
123def check():
124 """Return true if username available, else false, in JSON format"""
125 username = request.args.get("username")
126 if len(username) < 1:
127 return jsonify(False)
128
129 check_username = db.execute("SELECT username FROM users WHERE username = :username", username = username)
130
131 if len(check_username) == 0:
132 return jsonify(True)
133
134 else:
135 return jsonify(False)
136
137
138@app.route("/history")
139@login_required
140#3. TODO
141def history():
142 """Show history of transactions"""
143
144 #get user history
145 hist = db.execute("SELECT * FROM history WHERE id = :id ORDER BY dt DESC", id = session["user_id"])
146
147 #creste list store history
148 transactions = []
149
150 for trans in hist:
151 temp_dict ={
152 "date": trans["dt"],
153 "symbol": trans["symbol"],
154 "transcation": "BOUGHT" if trans["buy"] == 1 else "SOLD",
155 "shares": trans["shares"],
156 "price": trans["price"],
157 "total": trans["price"] * trans["shares"]
158 }
159 transactions.append(temp_dict)
160
161 return render_template("history.html", transactions = transactions)
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
183 # Query database for username
184 rows = db.execute("SELECT * FROM users WHERE username = :username",
185 username=request.form.get("username"))
186
187 # Ensure username exists and password is correct
188 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
189 return apology("invalid username and/or password", 403)
190
191 # Remember which user has logged in
192 session["user_id"] = rows[0]["id"]
193
194 # Redirect user to home page
195 return redirect("/")
196
197 # User reached route via GET (as by clicking a link or via redirect)
198 else:
199 return render_template("login.html")
200 return apology("TODO")
201
202
203@app.route("/logout")
204def logout():
205 """Log user out"""
206
207 # Forget any user_id
208 session.clear()
209
210 # Redirect user to login form
211 return redirect("/")
212
213
214@app.route("/quote", methods=["GET", "POST"])
215@login_required
216# 4. TODO
217def quote():
218 """Get stock quote."""
219 if request.method == "GET":
220 return render_template("quote.html")
221 else:
222 try:
223 stock = lookup(request.form.get("symbol"))
224 except:
225 return apology ("There is no stock with that name")
226
227 if stock is None:
228 return apology ("There is no stock with that name")
229
230 return render_template("quoted.html", stock = stock)
231 return apology("TODO")
232
233@app.route("/change_password", methods = ["GET", "POST"])
234@login_required
235def change_password():
236 if request.method == "GET":
237 return render_template("password.html")
238 else:
239 password = request.form.get("password")
240
241 if password == "":
242 return apology("Password must be one character")
243
244 if password < 4:
245 return apology ("Password must be greater than 4 characters")
246
247 pass_hash = generate_password_hash(password)
248
249 db.execute("UPDATE users SET hash = :pw WHERE id = :id", pw = pass_hash, id = session["user_id"])
250
251 return redirect("/")
252
253
254@app.route("/register", methods=["GET", "POST"])
255def register():
256#1. Register route implementation
257 """Register user"""
258
259 # Forget any user_id
260 session.clear()
261
262 # User reached route via POST (as by submitting a form via POST)
263 if request.method == "POST":
264
265 #getting the args
266 username = request.form.get("username")
267 password = request.form.get("password")
268 confirmation = request.form.get("confirmation")
269
270 #selceting rows from database
271 rows = db.execute("SELECT * FROM users WHERE username = :username", username = username)
272
273 #check for username(already exist) and blank input
274 if username == "":
275 return apology("Please provide a username")
276 elif len(rows) == 1:
277 return apology("username already taken")
278
279 #check for password and confirmation
280 if password == "":
281 return apology("you need to enter password")
282 elif password != confirmation:
283 return apology("Entered password doesn't matches")
284
285 hashed = generate_password_hash(request.form.get("password"))
286 #add to database
287 results = db.execute("INSERT INTO users (username, hash) VALUES (:username, :password)", username = username, password = hashed)
288 flash("Registration successful!")
289 session["user_id"] = results
290 print(session)
291
292 #redirects user to homepage
293 return redirect("/login")
294
295 else:
296 return render_template("register.html")
297
298 return apology
299
300
301@app.route("/sell", methods=["GET", "POST"])
302@login_required
303#5.TODO
304def sell():
305 """Sell shares of stock"""
306
307 if request.method == "GET":
308 all_stocks = db.execute(
309 "SELECT stock FROM purchases WHERE id = :id", id = session["user_id"])
310
311 return render_template("sell.html", stocks = all_stocks)
312
313 else:
314
315 #getting all values
316 symbol = request.form.get("symbol")
317 shares = int(request.form.get("shares"))
318 user_shares = db.execute(
319 "SELECT shares FROM purchases WHERE id = :id AND stock = :symbol", id = session["user_id"], symbol = symbol)[0]["shares"]
320 user_cash = db.execute(
321 "SELECT cash FROM users WHERE id = :id", id = session["user_id"])[0]["cash"]
322 current_price = lookup(symbol)["price"]
323
324 #error check
325 if symbol == "" or shares == "":
326 return apology("You need to enter a valid stock and share amount")
327
328 if shares < 0:
329 return apology("You need to provide a positive integer value")
330
331 if user_shares <shares:
332 return apology("You do not have enough amount of shares to sell")
333
334 #update database
335 db.execute(f"UPDATE purchases SET shares = {user_shares - shares} WHERE id = :id AND stock = :symbol", id = session["user_id"], symbol = symbol)
336
337 db.execute(f"UPDATE users SET cash = {user_cash + (current_price * shares)} WHERE id = :id", id = session["user_id"])
338
339 db.execute("INSERT INTO history(symbol, price, dt, id, shares, buy) VALUES(:symbol, :price, DATETIME('now), :id, :shares, :buy)",
340 symbol = symbol, price = current_price, id = session["user_id"], shares = shares, buy = 0)
341
342 return redirect("/")
343
344 return apology("TODO")
345
346
347def errorhandler(e):
348 """Handle error"""
349 if not isinstance(e, HTTPException):
350 e = InternalServerError()
351 return apology(e.name, e.code)
352
353
354# Listen for errors
355for code in default_exceptions:
356 app.errorhandler(code)(errorhandler)