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