Skip to content

Commit 2ac2019

Browse files
committed
Finance APP
1 parent f094229 commit 2ac2019

17 files changed

+748
-0
lines changed

pset8/finance/application.py

+347
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
import os
2+
import requests
3+
4+
from cs50 import SQL
5+
from datetime import datetime
6+
from flask import Flask, flash, jsonify, redirect, render_template, request, session
7+
from flask_session import Session
8+
from tempfile import mkdtemp
9+
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
10+
from werkzeug.security import check_password_hash, generate_password_hash
11+
12+
13+
from helpers import apology, login_required, lookup, usd
14+
15+
# Configure application
16+
app = Flask(__name__)
17+
18+
# Ensure templates are auto-reloaded
19+
app.config["TEMPLATES_AUTO_RELOAD"] = True
20+
21+
# Ensure responses aren't cached
22+
@app.after_request
23+
def after_request(response):
24+
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
25+
response.headers["Expires"] = 0
26+
response.headers["Pragma"] = "no-cache"
27+
return response
28+
29+
# Custom filter
30+
app.jinja_env.filters["usd"] = usd
31+
32+
# Configure session to use filesystem (instead of signed cookies)
33+
app.config["SESSION_FILE_DIR"] = mkdtemp()
34+
app.config["SESSION_PERMANENT"] = False
35+
app.config["SESSION_TYPE"] = "filesystem"
36+
Session(app)
37+
38+
# Configure CS50 Library to use SQLite database
39+
db = SQL("sqlite:///finance.db")
40+
41+
# set API Key variable
42+
API_KEY = os.environ.get("API_KEY")
43+
44+
# Make sure API key is set
45+
if not API_KEY:
46+
raise RuntimeError("API_KEY not set")
47+
48+
49+
@app.route("/")
50+
@login_required
51+
def index():
52+
"""Show portfolio of stocks"""
53+
shares = db.execute("SELECT symbol, shares, name, price, total FROM shares join users on id = user_id where id = ? and shares > 0;", session["user_id"])
54+
55+
currentCash = db.execute("SELECT cash FROM users where id = ?;", session["user_id"])
56+
57+
stocksTotalQuery = db.execute("SELECT sum(total) as total FROM shares join users on id = user_id where id = ? and shares > 0;", session["user_id"])
58+
59+
stocksTotal = stocksTotalQuery[0]["total"];
60+
61+
return render_template("index.html", shares = shares, currentCash = currentCash, stocksTotal = 0 if stocksTotal is None else stocksTotal)
62+
63+
64+
@app.route("/buy", methods=["GET", "POST"])
65+
@login_required
66+
def buy():
67+
"""Buy shares of stock"""
68+
69+
if request.method == "POST":
70+
71+
# get the symbol and values from the form
72+
symbol = request.form.get("symbol")
73+
shares = int(request.form.get("shares"))
74+
75+
if not symbol:
76+
return apology("You must provide a symbol", 400)
77+
78+
if not shares:
79+
return apology("You must provide the number of shares greater than 0", 400)
80+
81+
try:
82+
# make a request to fetch the most up to date
83+
req = requests.get(f'https://cloud.iexapis.com/stable/stock/{symbol}/quote?token={API_KEY}&filter=symbol,companyName,latestPrice').json()
84+
85+
# get the current price of the share
86+
price = req["latestPrice"]
87+
88+
# get the company name
89+
companyName = req["companyName"]
90+
91+
# get the current date and time
92+
dateTimeNow = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
93+
94+
# Query database for user cash
95+
row = db.execute("SELECT cash FROM users WHERE id = ?", session["user_id"])
96+
97+
# set the cash variable
98+
cash = row[0]["cash"]
99+
100+
# calculate the shares * price value
101+
sharesTimesPrice = shares*price
102+
103+
if sharesTimesPrice > cash:
104+
return apology("CAN'T AFFORD", 400)
105+
106+
# query for the stocks you already own
107+
stocks = db.execute("select name, shares from shares where user_id = ? and symbol = ?;", session["user_id"], symbol)
108+
109+
# check if oyu already have that stock, if returns 1 then you already have it and you increment the number of shares
110+
if len(stocks) == 1 and stocks[0]['shares'] > 0:
111+
# update the existing stocks
112+
newShares = stocks[0]["shares"] + shares
113+
db.execute("UPDATE shares SET shares = ?, total = ? WHERE user_id = ?", newShares, newShares*price, session["user_id"])
114+
115+
#update the history table
116+
db.execute("INSERT INTO history (user_id, symbol, shares, price, date_tm) VALUES(?, ?, ?, ?, ?)",
117+
session["user_id"], symbol, shares, price, dateTimeNow)
118+
119+
# you don't have any stocks for that symbol, add it to the db
120+
else:
121+
# execute insert statement to shares table
122+
db.execute("INSERT INTO shares (user_id, name, symbol, shares, price, total, date_tm) VALUES(?, ?, ?, ?, ?, ?, ?)",
123+
session["user_id"], companyName, symbol, shares, price, sharesTimesPrice, dateTimeNow)
124+
125+
#update the history table
126+
db.execute("INSERT INTO history (user_id, symbol, shares, price, date_tm) VALUES(?, ?, ?, ?, ?)",
127+
session["user_id"], symbol, shares, price, dateTimeNow)
128+
129+
# update the amount of cash the user has after the purchase
130+
db.execute("UPDATE users SET cash = ? WHERE id = ?", cash-sharesTimesPrice, session["user_id"])
131+
132+
return redirect("/")
133+
except:
134+
return apology("Invalid symbol, try a different one or amount of shares", 403)
135+
136+
else:
137+
return render_template("buy.html")
138+
139+
140+
@app.route("/history")
141+
@login_required
142+
def history():
143+
"""Show history of transactions"""
144+
145+
# Query database for transactions history
146+
rows = db.execute("SELECT * FROM history WHERE user_id = ? and shares <> 0;", session["user_id"])
147+
148+
return render_template("history.html", rows=rows)
149+
150+
151+
@app.route("/login", methods=["GET", "POST"])
152+
def login():
153+
"""Log user in"""
154+
155+
# Forget any user_id
156+
session.clear()
157+
158+
# User reached route via POST (as by submitting a form via POST)
159+
if request.method == "POST":
160+
161+
# Ensure username was submitted
162+
if not request.form.get("username"):
163+
return apology("must provide username", 403)
164+
165+
# Ensure password was submitted
166+
elif not request.form.get("password"):
167+
return apology("must provide password", 403)
168+
169+
# Query database for username
170+
rows = db.execute("SELECT * FROM users WHERE username = :username",
171+
username=request.form.get("username"))
172+
173+
# Ensure username exists and password is correct
174+
if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
175+
return apology("invalid username and/or password", 403)
176+
177+
# Remember which user has logged in
178+
session["user_id"] = rows[0]["id"]
179+
180+
# Redirect user to home page
181+
return redirect("/")
182+
183+
# User reached route via GET (as by clicking a link or via redirect)
184+
else:
185+
return render_template("login.html")
186+
187+
188+
@app.route("/logout")
189+
def logout():
190+
"""Log user out"""
191+
192+
# Forget any user_id
193+
session.clear()
194+
195+
# Redirect user to login form
196+
return redirect("/")
197+
198+
199+
@app.route("/quote", methods=["GET", "POST"])
200+
@login_required
201+
def quote():
202+
"""Get stock quote."""
203+
204+
# User reached route via POST (as by submitting a form via POST)
205+
if request.method == "POST":
206+
207+
symbol = request.form.get("symbol")
208+
209+
try:
210+
req = requests.get(f'https://cloud.iexapis.com/stable/stock/{symbol}/quote?token={API_KEY}&filter=symbol,companyName,latestPrice').json()
211+
except:
212+
return apology("Invalid symbol, try a different one", 403)
213+
214+
return render_template("quotePrice.html", req=req)
215+
216+
else:
217+
return render_template("quote.html")
218+
219+
220+
@app.route("/register", methods=["GET", "POST"])
221+
def register():
222+
"""Register user"""
223+
224+
# User reached route via POST (as by submitting a form via POST)
225+
if request.method == "POST":
226+
227+
username = request.form.get("username")
228+
password = request.form.get("password")
229+
passwordAgain = request.form.get("passwordAgain")
230+
231+
# Ensure username was submitted
232+
if not username:
233+
return apology("must provide username", 403)
234+
235+
# Ensure password was submitted
236+
elif not password:
237+
return apology("must provide password", 403)
238+
239+
# Ensure passwordAgain was submitted
240+
elif not passwordAgain:
241+
return apology("must provide password the same password in this field", 403)
242+
243+
# Ensure password and passwordAgain are equal
244+
elif password != passwordAgain:
245+
return apology("passwords must match", 403)
246+
247+
# Query database for username
248+
rows = db.execute("SELECT * FROM users WHERE username = ?",
249+
username)
250+
251+
# check for existing user
252+
if len(rows) == 1:
253+
return apology("Username Already exist", 403)
254+
else:
255+
db.execute("INSERT INTO users (username, hash) VALUES(?, ?)",
256+
username, generate_password_hash(password))
257+
258+
# Redirect user to home page
259+
return redirect("/")
260+
else:
261+
return render_template("register.html")
262+
263+
264+
@app.route("/sell", methods=["GET", "POST"])
265+
@login_required
266+
def sell():
267+
"""Sell shares of stock"""
268+
# User reached route via POST (as by submitting a form via POST)
269+
if request.method == "POST":
270+
# get the symbol and amount of shares from the fom
271+
symbol = request.form.get("symbol")
272+
shares = int(request.form.get("shares"))
273+
274+
# make a request to fetch the most up to date price
275+
req = requests.get(f'https://cloud.iexapis.com/stable/stock/{symbol}/quote?token={API_KEY}&filter=latestPrice').json()
276+
277+
# set the price variable to the value from the request
278+
price = req["latestPrice"]
279+
280+
# query for the current cash
281+
currentCash = db.execute("SELECT cash FROM users where id = ?;", session["user_id"])
282+
283+
newCash = (shares*price)+currentCash[0]["cash"]
284+
285+
# query for the current shares
286+
currentShares = db.execute("select shares from shares where user_id = ? and symbol = ?", session["user_id"], symbol)
287+
288+
if(shares > currentShares[0]['shares']):
289+
return apology("Trying to sell more shares than what you have, huh?", 400)
290+
291+
# update the number of available shares to the user
292+
db.execute("UPDATE shares SET shares = ? where user_id = ? and symbol = ?;", currentShares[0]['shares']-shares, session["user_id"], symbol)
293+
294+
# update the new cash value from having sold N amount of shares
295+
db.execute("UPDATE users SET cash = ? where id = ?;", newCash, session["user_id"])
296+
297+
currentShares = db.execute("select shares from shares where user_id = ? and symbol = ?", session["user_id"], symbol)
298+
299+
# update the total you own of the share
300+
db.execute("UPDATE shares SET total = ? where user_id = ? and symbol = ?;", currentShares[0]['shares']*price, session["user_id"], symbol)
301+
302+
# get the current date and time
303+
dateTimeNow = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
304+
305+
#update the history table
306+
db.execute("INSERT INTO history (user_id, symbol, shares, price, date_tm) VALUES(?, ?, ?, ?, ?)",
307+
session["user_id"], symbol, (-1*shares), price, dateTimeNow)
308+
309+
return redirect("/")
310+
else:
311+
# Query database for symbols
312+
symbols = db.execute("SELECT symbol FROM shares WHERE user_id = ? and shares > 0;", session["user_id"])
313+
314+
return render_template("sell.html", symbols = symbols)
315+
316+
@app.route("/funds", methods=["GET", "POST"])
317+
@login_required
318+
def funds():
319+
"""Add more funds to your account"""
320+
321+
cash = db.execute("SELECT cash FROM users where id = ?;", session["user_id"])
322+
currentCash = cash[0]['cash']
323+
324+
# User reached route via POST (as by submitting a form via POST)
325+
if request.method == "POST":
326+
# get the currentFunds and newFunds from the form
327+
newFunds = int(request.form.get("newFunds"))
328+
329+
# update the amount of cash the user has after the purchase
330+
db.execute("UPDATE users SET cash = ? WHERE id = ?", currentCash+newFunds, session["user_id"])
331+
332+
return redirect("/")
333+
else:
334+
335+
return render_template("funds.html", currentCash = currentCash)
336+
337+
338+
def errorhandler(e):
339+
"""Handle error"""
340+
if not isinstance(e, HTTPException):
341+
e = InternalServerError()
342+
return apology(e.name, e.code)
343+
344+
345+
# Listen for errors
346+
for code in default_exceptions:
347+
app.errorhandler(code)(errorhandler)

pset8/finance/finance.db

6 KB
Binary file not shown.

0 commit comments

Comments
 (0)