· 6 years ago · Apr 08, 2020, 12:32 PM
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# 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
45def index():
46 """Show portfolio of stocks"""
47
48
49 return render_template("index.html")
50
51
52@app.route("/buy", methods=["GET", "POST"])
53@login_required
54def buy():
55 """Buy shares of stock"""
56 if request.method == "GET":
57 return render_template("buy.html")
58 else:
59 symbol = request.form.get("symbol")
60
61 if not symbol:
62 return apology("Enter a symbol", 400)
63
64 symlkup = lookup(symbol)
65
66 if not symlkup:
67 return apology("Invalid Symbol", 400)
68
69 shares = int(request.form.get("shares"))
70
71 if shares < 0:
72 return apology("Number of shares must be a positive interger", 400)
73
74 price = symlkup["price"]
75
76 count = 0
77 count += 1
78
79 cash = db.execute("SELECT cash FROM users WHERE id=:id", id = session["user_id"])[0]["cash"]
80
81 amount = price * shares
82 if amount>cash:
83 return apology("Insufficient Funds", 400)
84 balance = cash - amount
85
86 username = db.execute("SELECT username FROM users WHERE id=:name", name = session["user_id"])[0]["username"]
87
88 # insert into txn and update finance database
89 db.execute("INSERT INTO btxn (username, stock, shares, price) VALUES (:username, :stock, :shares, :price);", username=username, stock=symbol, shares=shares, price=price)
90
91 db.execute("UPDATE users SET cash = :balance WHERE id = :id1", balance=balance, id1 = session["user_id"])
92
93 return apology("Bought", count)
94
95
96
97@app.route("/history")
98@login_required
99def history():
100 """Show history of transactions"""
101 return apology("TODO")
102
103
104@app.route("/login", methods=["GET", "POST"])
105def login():
106 """Log user in"""
107
108 # Forget any user_id
109 session.clear()
110
111 # User reached route via POST (as by submitting a form via POST)
112 if request.method == "POST":
113
114 # Ensure username was submitted
115 if not request.form.get("username"):
116 return apology("must provide username", 403)
117
118 # Ensure password was submitted
119 elif not request.form.get("password"):
120 return apology("must provide password", 403)
121
122 # Query database for username
123 rows = db.execute("SELECT * FROM users WHERE username = :username",
124 username=request.form.get("username"))
125
126 # Ensure username exists and password is correct
127 if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
128 return apology("invalid username and/or password", 403)
129
130 # Remember which user has logged in
131 session["user_id"] = rows[0]["id"]
132
133 # Redirect user to home page
134 return redirect("/")
135
136 # User reached route via GET (as by clicking a link or via redirect)
137 else:
138 return render_template("login.html")
139
140
141@app.route("/logout")
142def logout():
143 """Log user out"""
144
145 # Forget any user_id
146 session.clear()
147
148 # Redirect user to login form
149 return redirect("/")
150
151
152@app.route("/quote", methods=["GET", "POST"])
153@login_required
154def quote():
155 """Get stock quote."""
156
157 # User enters the /quote route using any link or via redirect..
158 if request.method == "GET":
159 return render_template("quote.html")
160
161 # User enters the /quote route by "POST" (by submitting a form via POST (i.e a form with method="post"))
162 else:
163 symbol = request.form.get("symbol")
164
165 if not symbol:
166 return apology("Enter a symbol", 400)
167
168 symlkup = lookup(symbol)
169
170 if not symlkup:
171 return apology("Invalid Symbol", 400)
172 else:
173 return render_template("quoted.html", name = symlkup["name"],
174 price = usd(symlkup["price"]), symbol = symlkup["symbol"])
175
176
177@app.route("/register", methods=["GET", "POST"])
178def register():
179
180 """Register user"""
181
182 session.clear()
183
184 if request.method == "POST":
185
186 if not request.form.get("username"):
187 return apology("must provide username", 400)
188
189 rows = db.execute("SELECT * FROM users WHERE username = :username", username=request.form.get("username"))
190
191 if len(rows) == 1:
192 return apology("Username is Already Taken", 400)
193
194 if not request.form.get("password"):
195 return apology("must enter password", 400)
196
197 elif request.form.get("password") != request.form.get("confirmation"):
198 return apology("Passwords do not match.", 400)
199
200 hsh = generate_password_hash(request.form.get("password"))
201
202 db.execute("INSERT INTO users(username, hash) VALUES (:username, :hasher);", username=request.form.get("username"), hasher=hsh)
203
204 return redirect("/")
205
206 else:
207 return render_template("register.html")
208
209
210@app.route("/sell", methods=["GET", "POST"])
211@login_required
212def sell():
213 """Sell shares of stock"""
214 return apology("TODO")
215
216
217
218def errorhandler(e):
219 """Handle error"""
220 if not isinstance(e, HTTPException):
221 e = InternalServerError()
222 return apology(e.name, e.code)
223
224
225# Listen for errors
226for code in default_exceptions:
227 app.errorhandler(code)(errorhandler)