Created
September 26, 2017 22:46
-
-
Save OxiBo/10d0e12705f10669caff106b4f00436c to your computer and use it in GitHub Desktop.
pset7 cs50 week9 CSFinance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Add_cash | |
{% endblock %} | |
{% block main %} | |
<form action="{{ url_for('add_cash') }}" method="post"> | |
<fieldset> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="add_cash" placeholder="Add cash" type="text"/> | |
</div> | |
<div class="form-group"> | |
<button class="btn btn-default" type="submit">Add cash</button> | |
</div> | |
</fieldset> | |
</form> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Apology | |
{% endblock %} | |
{% block main %} | |
<img alt="Sorry!" src="http://memegen.link/custom/{{ top | urlencode }}/{{ bottom | urlencode }}.jpg?alt=http://i.imgur.com/A3ZR65I.png"/> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from cs50 import SQL | |
from flask import Flask, flash, redirect, render_template, request, session, url_for | |
from flask_session import Session | |
from passlib.apps import custom_app_context as pwd_context | |
from tempfile import mkdtemp | |
from time import strftime | |
from helpers import * | |
# configure application | |
app = Flask(__name__) | |
# ensure responses aren't cached | |
if app.config["DEBUG"]: | |
@app.after_request | |
def after_request(response): | |
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" | |
response.headers["Expires"] = 0 | |
response.headers["Pragma"] = "no-cache" | |
return response | |
# custom filter | |
app.jinja_env.filters["usd"] = usd | |
# configure session to use filesystem (instead of signed cookies) | |
app.config["SESSION_FILE_DIR"] = mkdtemp() | |
app.config["SESSION_PERMANENT"] = False | |
app.config["SESSION_TYPE"] = "filesystem" | |
Session(app) | |
# configure CS50 Library to use SQLite database | |
db = SQL("sqlite:///finance.db") | |
@app.route("/") | |
@login_required | |
def index(): | |
"""Shows user's portfolio""" | |
# select values from db about user's portfolio | |
rows = db.execute("SELECT symbol, name, shares FROM stocks\ | |
WHERE user_id = :user_id order by symbol", \ | |
user_id = session["user_id"]) | |
# lookup current price for each stock and culculate total value of each stock | |
# add the informarion to rows to display on website | |
stock_total = 0 | |
for row in rows: | |
current_price = lookup(row["symbol"])["price"] | |
total = current_price*int(row["shares"]) | |
row["current_price"] = usd(current_price) | |
row["total"] = usd(total) | |
stock_total += total | |
# query table users for current cash available | |
cash = db.execute("SELECT cash FROM users WHERE id = :user_id",\ | |
user_id = session["user_id"])[0]["cash"] | |
total = cash + stock_total | |
return render_template("portfolio.html", rows = rows,\ | |
cash = usd(cash), total = usd(total)) | |
@app.route("/buy", methods = ["GET", "POST"]) | |
@login_required | |
def buy(): | |
"""Buy shares of stock.""" | |
# ensure request method is POST | |
if request.method == "POST": | |
# lookup a stock’s current price using | |
# function "lookup" implemented in helpers.py | |
quote = lookup(request.form.get("symbol")) | |
# ensure stock symbol was submitted | |
if not request.form.get("symbol"): | |
return apology("missing symbol") | |
# ensure that stock is valid | |
elif not quote: | |
return apology("invalid symbol") | |
# ensure amount of shares was submitted | |
elif not request.form.get("shares"): | |
return apology("missing shares") | |
# ensure inputed number of shares is not an alphabetical string | |
elif not str.isdigit(request.form.get("shares")): | |
return apology("invalid shares") | |
# ensure number of shares is a positive integer | |
elif int(request.form.get("shares")) <= 0: | |
return apology("invalid shares") | |
name = quote["name"] | |
# query database for username cash to check if user can afford buying shares | |
rows = db.execute("SELECT cash FROM users WHERE id = :id", id = session["user_id"] ) | |
price_per_share = quote["price"] | |
total_price = float(price_per_share)*int(request.form.get("shares")) | |
available_cash = float(db.execute("SELECT cash FROM users WHERE id = :user_id", user_id = session["user_id"])[0]["cash"]) | |
# check if user can afford buying the shares of stock | |
if available_cash < total_price: | |
return apology("not enought cash! can't afford") | |
else: | |
# update cash in users table for the user | |
db.execute("UPDATE users SET cash = :cash WHERE id = :id", cash = available_cash - total_price, id = session["user_id"] ) | |
datetime = strftime('%Y-%m-%d %H:%M:%S') | |
flash("Congratulations! Transaction is successful!") | |
# update user's history | |
db.execute("""INSERT INTO transactions ( | |
user_id, symbol, name, shares, price_per_share, total_price, transacted) | |
VALUES(:user_id, :symbol, :name, :shares, :price_per_share, :total_price, :transacted)""",\ | |
user_id = session["user_id"], symbol = str.upper(request.form.get("symbol")),\ | |
name = name, shares = int(request.form.get("shares")), price_per_share = price_per_share, \ | |
total_price = total_price, transacted = datetime) | |
# check if the stock in user's portfolio | |
instock = db.execute("SELECT symbol FROM stocks WHERE user_id = :user_id and symbol = :symbol", user_id = session["user_id"], \ | |
symbol = str.upper(request.form.get("symbol"))) | |
if not instock: | |
# update user's portfolio (insert values) | |
db.execute("""INSERT INTO stocks ( | |
user_id, symbol, name, shares) | |
VALUES(:user_id, :symbol, :name, :shares)""",\ | |
user_id = session["user_id"], symbol = str.upper(request.form.get("symbol")),\ | |
name = name, shares = int(request.form.get("shares"))) | |
else: | |
# update user's portfolio | |
rows = db.execute("SELECT shares FROM stocks WHERE user_id = :user_id and symbol = :symbol", \ | |
user_id = session["user_id"], symbol = str.upper(request.form.get("symbol"))) | |
db.execute("UPDATE stocks SET shares = :shares\ | |
WHERE user_id = :user_id and symbol = :symbol",\ | |
shares = int(rows[0]["shares"]) + int(request.form.get("shares")),\ | |
user_id = session["user_id"], symbol = str.upper(request.form.get("symbol"))) | |
return redirect(url_for("index")) | |
# else if user reached route via GET (as by clicking a link or via redirect) | |
else: | |
return render_template("buy.html") | |
@app.route("/history") | |
@login_required | |
def history(): | |
"""Show history of transactions.""" | |
# select values from db about user's transactions | |
rows = db.execute("SELECT symbol, name, shares, price_per_share, total_price, transacted FROM transactions\ | |
WHERE user_id = :user_id", user_id = session["user_id"]) | |
for row in rows: | |
row["price_per_share"] = usd(row["price_per_share"]) | |
row["total_price"] = usd(row["total_price"]) | |
return render_template("history.html", rows = rows) | |
@app.route("/login", methods=["GET", "POST"]) | |
def login(): | |
"""Log user in.""" | |
# forget any user_id | |
session.clear() | |
# if user reached route via POST (as by submitting a form via POST) | |
if request.method == "POST": | |
# ensure username was submitted | |
if not request.form.get("username"): | |
return apology("must provide username") | |
# ensure password was submitted | |
elif not request.form.get("password"): | |
return apology("must provide password") | |
# query database for username | |
rows = db.execute("SELECT * FROM users WHERE username = :username", username = request.form.get("username")) | |
# ensure username exists and password is correct | |
if len(rows) != 1 or not pwd_context.verify(request.form.get("password"), rows[0]["hash"]): | |
return apology("invalid username and/or password") | |
# remember which user has logged in | |
session["user_id"] = rows[0]["id"] | |
# redirect user to home page | |
return redirect(url_for("index")) | |
# else if user reached route via GET (as by clicking a link or via redirect) | |
else: | |
return render_template("login.html") | |
@app.route("/logout") | |
def logout(): | |
"""Log user out.""" | |
# forget any user_id | |
session.clear() | |
# redirect user to login form | |
return redirect(url_for("login")) | |
@app.route("/quote", methods=["GET", "POST"]) | |
@login_required | |
def quote(): | |
"""Get stock quote.""" | |
# renders template for user to look up a stock’s current price | |
if request.method == "POST": | |
# ensure symbol for quoting stock was submitted | |
if not request.form.get("quote"): | |
return apology("Missing symbol") | |
else: | |
# lookup a stock’s current price using | |
# function "lookup" implemented in helpers.py | |
quote=lookup(request.form.get("quote")) | |
# ensure that stock is valid | |
if not quote: | |
return apology("Invalid symbol") | |
else: | |
# return information about a stock’s current price using | |
return render_template("quoted.html", quote = quote, price = usd(quote["price"])) | |
else: | |
# redirect user to quote | |
return render_template("quote.html") | |
@app.route("/register", methods=["GET", "POST"]) | |
def register(): | |
"""Register user.""" | |
# forget any user_id | |
session.clear() | |
# ensure request method is POST | |
if request.method == "POST": | |
# ensure username was submitted | |
if not request.form.get("username"): | |
return apology("must provide username") | |
# ensure password was submitted | |
elif not request.form.get("password"): | |
return apology("must provide password") | |
# ensure password was subbmited again | |
elif not request.form.get("password (again)"): | |
return apology("must repeat password one more time") | |
# ensure passwords match | |
elif request.form.get("password") != request.form.get("password (again)"): | |
return apology("Passwords don't match. Try again!") | |
# query database for username to check if such username exsist | |
rows = db.execute("SELECT * FROM users WHERE username = :username", username = request.form.get("username")) | |
# ensure username does not exist already | |
if len(rows) == 1: | |
return apology("Username taken, try different username") | |
else: | |
db.execute("INSERT INTO users(username, hash) VALUES(:username, :hash)",\ | |
username = request.form.get("username"), hash = pwd_context.hash(request.form.get("password"))) | |
flash("Congratulations!You were successfully registered!") | |
# query database for user id | |
rows = db.execute("SELECT * FROM users WHERE username = :username", username = request.form.get("username")) | |
# remember which user has logged in | |
session["user_id"] = rows[0]["id"] | |
# redirect user to home page | |
return redirect(url_for("index")) | |
# else if user reached route via GET (as by clicking a link or via redirect) | |
else: | |
return render_template("register.html") | |
@app.route("/sell", methods=["GET", "POST"]) | |
@login_required | |
def sell(): | |
"""Sell shares of stock.""" | |
# ensure request methon is POST | |
if request.method == "POST": | |
# lookup a stock's current price using a function "lookup" implemented in helpers.py | |
quote = lookup(request.form.get("symbol")) | |
# ensure stock symbol was submitted | |
if not request.form.get("symbol"): | |
return apology("missing symbol") | |
# ensure that stock is valid | |
elif not quote: | |
return apology("invalid symbol") | |
# ensure amout of shares was submitted | |
elif not request.form.get("shares"): | |
return apology("missing shares") | |
# ensure number of shares is numeric | |
elif not str.isdigit(request.form.get("shares")): | |
return apology("invalid shares") | |
# ensure number of shares is a positive integer | |
elif int(request.form.get("shares")) <= 0: | |
return apology("invalid shares") | |
name = quote["name"] | |
# query database to check if user has shares of the stock | |
rows = db.execute("SELECT shares FROM stocks WHERE user_id = :id and symbol = :symbol", \ | |
id = session["user_id"], symbol = str.upper(request.form.get("symbol"))) | |
# check if user have enough shares of the stock to sell | |
if not rows or int(request.form.get("shares")) > int(rows[0]["shares"]): | |
return apology("don't have enough shares to sell") | |
else: | |
# delete stock from user's portfolio if user sold all shares of the stock | |
if int(rows[0]["shares"]) == int(request.form.get("shares")): | |
db.execute("DELETE FROM stocks WHERE user_id = :user_id and symbol = :symbol", user_id = session["user_id"], \ | |
symbol = str.upper(request.form.get("symbol"))) | |
# update user's portfolio after selling | |
db.execute("""UPDATE stocks SET shares = :shares WHERE user_id = :user_id and symbol = :symbol""",\ | |
shares = int(rows[0]["shares"]) - int(request.form.get("shares")),\ | |
user_id = session["user_id"], symbol = str.upper(request.form.get("symbol"))) | |
flash("Sold! Transaction is succesful!") | |
# calculate total price of the shares to sell | |
price_per_share = float(quote["price"]) | |
total_value = price_per_share*int(request.form.get("shares")) | |
datetime = strftime('%Y-%m-%d %H:%M:%S') | |
# update user's history | |
db.execute("""INSERT INTO transactions ( | |
user_id, symbol, name, shares, price_per_share, total_price, transacted) | |
VALUES(:user_id, :symbol, :name, :shares, :price_per_share, :total_price, :transacted)""",\ | |
user_id = session["user_id"], symbol = str.upper(request.form.get("symbol")),\ | |
name = name, shares = "-" + request.form.get("shares"), price_per_share = price_per_share, \ | |
total_price = total_value, transacted = datetime) | |
# update cash in users table for the user | |
rows = db.execute("SELECT cash FROM users WHERE id = :id", id = session["user_id"]) | |
db.execute("UPDATE users SET cash = :cash WHERE id = :id", cash = rows[0]["cash"] + total_value, id = session["user_id"]) | |
return redirect(url_for("index")) | |
# else if user reached rout via GET(as by clicking a link or via redirect) | |
else: | |
return render_template("sell.html") | |
@app.route("/add_cash", methods = ["GET", "POST"]) | |
@login_required | |
def add_cash(): | |
"""Add cash to the account.""" | |
# ensure request methon is POST | |
if request.method == "POST": | |
# ensure cash amout was submitted | |
if not request.form.get("add_cash"): | |
return apology("missing amount of cash") | |
# ensure amount of cash is numeric and greater then $0.01 and lesser then $100,000 | |
elif not str.isdigit(request.form.get("add_cash")) or float(request.form.get("add_cash")) < 0.01 \ | |
or float(request.form.get("add_cash")) > 100000: | |
return apology("invalid amount of cash") | |
# query database for username current cash | |
rows = db.execute("SELECT cash FROM users WHERE id = :id", id = session["user_id"]) | |
# update cash in users table for the user | |
db.execute("UPDATE users SET cash = :cash WHERE id = :id", cash = rows[0]["cash"] + float(request.form.get("add_cash")), \ | |
id = session["user_id"]) | |
return redirect(url_for("index")) | |
# else if user reached route via GET (as by clicking a link or via redirect) | |
else: | |
return render_template("add_cash.html") | |
@app.route("/change_password", methods=["GET", "POST"]) | |
@login_required | |
def change_password(): | |
"""Change user password""" | |
if request.method == "POST": | |
# query database for username | |
rows = db.execute("SELECT * FROM users WHERE username = :username", username = request.form.get("username")) | |
# ensure username was submitted | |
if not request.form.get("username"): | |
return apology("must provide username") | |
# ensure password was submitted | |
elif not request.form.get("password"): | |
return apology("must provide password") | |
# ensure username exists and password is correct | |
elif len(rows) != 1 or not pwd_context.verify(request.form.get("password"), rows[0]["hash"]): | |
return apology("invalid username and/or password") | |
# ensure new password was submitted | |
elif not request.form.get("new password"): | |
return apology("must provide new password") | |
# ensure new password does not repeat old password | |
elif request.form.get("password") == request.form.get("new password"): | |
return apology("new password can't repeat old password") | |
# ensure new password was subbmited again | |
elif not request.form.get("new password (again)"): | |
return apology("must repeat new password one more time") | |
# ensure passwords match | |
elif request.form.get("new password") != request.form.get("new password (again)"): | |
return apology("Passwords don't match. Try again!") | |
db.execute("UPDATE users SET hash = :hash WHERE id = :id",\ | |
hash = pwd_context.hash(request.form.get("new password")), id = session["user_id"]) | |
flash("Congratulations! Password has been changed!") | |
# redirect user to home page | |
return redirect(url_for("index")) | |
# else if user reached route via GET (as by clicking a link or via redirect) | |
else: | |
return render_template("change_password.html") | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Buy | |
{% endblock %} | |
{% block main %} | |
<form action="{{ url_for('buy') }}" method="post"> | |
<fieldset> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="symbol" placeholder="Symbol" type="text"/> | |
</div> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="shares" placeholder="Shares" type="text"/> | |
</div> | |
<div class="form-group"> | |
<button class="btn btn-default" type="submit">Buy</button> | |
</div> | |
</fieldset> | |
</form> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Change password | |
{% endblock %} | |
{% block main %} | |
<form action="{{ url_for('change_password') }}" method="post"> | |
<fieldset> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="username" placeholder="Username" type="text"/> | |
</div> | |
<div class="form-group"> | |
<input class="form-control" name="password" placeholder="Password" type="password"/> | |
</div> | |
<div class="form-group"> | |
<input class="form-control" name="new password" placeholder="New password" type="password"/> | |
</div> | |
<div class="form-group"> | |
<input class="form-control" name="new password (again)" placeholder="New password (again)" type="password"/> | |
</div> | |
<div class="form-group"> | |
<button class="btn btn-default" type="submit">Change password</button> | |
</div> | |
</fieldset> | |
</form> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import csv | |
import urllib.request | |
from flask import redirect, render_template, request, session, url_for | |
from functools import wraps | |
def apology(top="", bottom=""): | |
"""Renders message as an apology to user.""" | |
def escape(s): | |
""" | |
Escape special characters. | |
https://github.com/jacebrowning/memegen#special-characters | |
""" | |
for old, new in [("-", "--"), (" ", "-"), ("_", "__"), ("?", "~q"), | |
("%", "~p"), ("#", "~h"), ("/", "~s"), ("\"", "''")]: | |
s = s.replace(old, new) | |
return s | |
return render_template("apology.html", top=escape(top), bottom=escape(bottom)) | |
def login_required(f): | |
""" | |
Decorate routes to require login. | |
http://flask.pocoo.org/docs/0.11/patterns/viewdecorators/ | |
""" | |
@wraps(f) | |
def decorated_function(*args, **kwargs): | |
if session.get("user_id") is None: | |
return redirect(url_for("login", next=request.url)) | |
return f(*args, **kwargs) | |
return decorated_function | |
def lookup(symbol): | |
"""Look up quote for symbol.""" | |
# reject symbol if it starts with caret | |
if symbol.startswith("^"): | |
return None | |
# reject symbol if it contains comma | |
if "," in symbol: | |
return None | |
# query Yahoo for quote | |
# http://stackoverflow.com/a/21351911 | |
try: | |
url = "http://download.finance.yahoo.com/d/quotes.csv?f=snl1&s={}".format(symbol) | |
webpage = urllib.request.urlopen(url) | |
datareader = csv.reader(webpage.read().decode("utf-8").splitlines()) | |
row = next(datareader) | |
except: | |
return None | |
# ensure stock exists | |
try: | |
price = float(row[2]) | |
except: | |
return None | |
# return stock's name (as a str), price (as a float), and (uppercased) symbol (as a str) | |
return { | |
"name": row[1], | |
"price": price, | |
"symbol": row[0].upper() | |
} | |
def usd(value): | |
"""Formats value as USD.""" | |
return "${:,.2f}".format(value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
History | |
{% endblock %} | |
{% block main %} | |
<h2>HISTORY OF TRANSACTIONS</h2> | |
<table class="table table-striped"> | |
<thead> | |
<th>Stock Symbol</th> | |
<th>Company Name</th> | |
<th>Number of Shares</th> | |
<th>Price per Share</th> | |
<th>Total Price</th> | |
<th>Transacted</th> | |
</thead> | |
{% for row in rows %} | |
<tr> | |
<td>{{ row["symbol"] }}</td> | |
<td>{{ row["name"] }}</td> | |
<td>{{ row["shares"] }}</td> | |
<td>{{ row["price_per_share"] }}</td> | |
<td>{{ row["total_price"] }}</td> | |
<td>{{ row["transacted"] }}</td> | |
</tr> | |
{% endfor %} | |
</table> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<!-- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta --> | |
<meta charset="utf-8"/> | |
<meta content="initial-scale=1, width=device-width" name="viewport"/> | |
<!-- documentation at http://getbootstrap.com/, alternative themes at https://www.bootstrapcdn.com/bootswatch/ --> | |
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/> | |
<link href="{{ url_for('static', filename='styles.css') }}" rel="stylesheet"/> | |
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> | |
<title>C$50 Finance: {% block title %}{% endblock %}</title> | |
</head> | |
<body> | |
<div class="container"> | |
<nav class="navbar navbar-default"> | |
<div class="container-fluid"> | |
<div class="navbar-header"> | |
<button aria-expanded="false" class="navbar-toggle collapsed" data-target="#navbar" data-toggle="collapse" type="button"> | |
<span class="sr-only">Toggle navigation</span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
</button> | |
<a class="navbar-brand" href="{{ url_for('index') }}"><span class="blue">C</span><span class="red">$</span><span class="yellow">5</span><span class="green">0</span> <span class="red">Finance</span></a> | |
</div> | |
<div class="collapse navbar-collapse" id="navbar"> | |
{% if session.user_id %} | |
<ul class="nav navbar-nav"> | |
<li><a href="{{ url_for('quote') }}">Quote</a></li> | |
<li><a href="{{ url_for('buy') }}">Buy</a></li> | |
<li><a href="{{ url_for('sell') }}">Sell</a></li> | |
<li><a href="{{ url_for('history') }}">History</a></li> | |
</ul> | |
<ul class="nav navbar-nav navbar-right"> | |
<li><a href="{{ url_for('change_password') }}">Change password</a></li> | |
<li><a href="{{ url_for('logout') }}">Log Out</a></li> | |
</ul> | |
{% else %} | |
<ul class="nav navbar-nav navbar-right"> | |
<li><a href="{{ url_for('register') }}">Register</a></li> | |
<li><a href="{{ url_for('login') }}">Log In</a></li> | |
</ul> | |
{% endif %} | |
</div> | |
</div> | |
</nav> | |
{% if get_flashed_messages() %} | |
<header> | |
<div class="alert alert-info" role="alert"> | |
{{ get_flashed_messages() | join(" ") }} | |
</div> | |
</header> | |
{% endif %} | |
<main> | |
{% block main %}{% endblock %} | |
</main> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Log In | |
{% endblock %} | |
{% block main %} | |
<form action="{{ url_for('login') }}" method="post"> | |
<fieldset> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="username" placeholder="Username" type="text"/> | |
</div> | |
<div class="form-group"> | |
<input class="form-control" name="password" placeholder="Password" type="password"/> | |
</div> | |
<div class="form-group"> | |
<button class="btn btn-default" type="submit">Log In</button> | |
</div> | |
</fieldset> | |
</form> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Portfolio | |
{% endblock %} | |
{% block main %} | |
<h2>PORTFOLIO</h2> | |
<table class="table table-striped"> | |
<thead> | |
<th>Stock Symbol</th> | |
<th>Company Name</th> | |
<th>Number of Shares</th> | |
<th>Current Price</th> | |
<th>TOTAL</th> | |
<th>Buy Options</th> | |
<th>Sell Options</th> | |
</thead> | |
{% for row in rows %} | |
<tr> | |
<td>{{ row["symbol"] }}</td> | |
<td>{{ row["name"] }}</td> | |
<td>{{ row["shares"] }}</td> | |
<td>{{ row["current_price"] }}</td> | |
<td>{{ row["total"] }}</td> | |
<td><form action="{{ url_for('buy') }}" method="post"> | |
<div class="form-group"> | |
<input type="hidden" name="symbol" type="text" value="{{ row['symbol'] }}"/> | |
<input autocomplete="off" autofocus class="form-control" size="6" name="shares" placeholder="Shares" type="text"> | |
<button class="btn btn-default" type="submit" style="width:110px">Buy more</button> | |
</div> | |
<form></form> | |
</td> | |
<td><form action="{{ url_for('sell') }}" method="post"> | |
<div class="form-group"> | |
<input type="hidden" name="symbol" type="text" value="{{ row['symbol'] }}"/> | |
<input autocomplete="off" autofocus class="form-control" size="6" name="shares" placeholder="Shares" type="text"> | |
<button class="btn btn-default" type="submit" style="width:110px">Sell</button> | |
</div> | |
<form></form></td> | |
</tr> | |
{% endfor %} | |
<tr> | |
<td>CASH</td> | |
<td></td> | |
<td></td> | |
<td></td> | |
<td>{{ cash }}</td> | |
<td><a href="{{ url_for('add_cash') }}">Add cash</a></td> | |
<td></td> | |
</tr> | |
<tr> | |
<td></td> | |
<td></td> | |
<td></td> | |
<td></td> | |
<th>{{ total }}</th> | |
<td></td> | |
<td></td> | |
</tr> | |
</table> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Quote | |
{% endblock %} | |
{% block main %} | |
<form action="{{ url_for('quote') }}" method="post"> | |
<fieldset> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="quote" placeholder="Symbol" type="text"/> | |
</div> | |
<div class="form-group"> | |
<button class="btn btn-default" type="submit">Quote</button> | |
</div> | |
</fieldset> | |
</form> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Quoted | |
{% endblock %} | |
{% block main %} | |
<form action="{{ url_for('quote') }}" method="get"> | |
<fieldset> | |
<div class="form-group"> | |
<h1> | |
A share of {{ quote["name"] }} ({{ quote["symbol"]}}) costs {{ price }}. | |
</h1> | |
</div> | |
</fieldset> | |
</form> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Register | |
{% endblock %} | |
{% block main %} | |
<form action="{{ url_for('register') }}" method="post"> | |
<fieldset> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="username" placeholder="Username" type="text"/> | |
</div> | |
<div class="form-group"> | |
<input class="form-control" name="password" placeholder="Password" type="password"/> | |
</div> | |
<div class="form-group"> | |
<input class="form-control" name="password (again)" placeholder="Password (again)" type="password"/> | |
</div> | |
<div class="form-group"> | |
<button class="btn btn-default" type="submit">Register</button> | |
</div> | |
</fieldset> | |
</form> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Flask | |
Flask-Session | |
passlib | |
SQLAlchemy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "layout.html" %} | |
{% block title %} | |
Sell | |
{% endblock %} | |
{% block main %} | |
<form action="{{ url_for('sell') }}" method="post"> | |
<fieldset> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="symbol" placeholder="Symbol" type="text"/> | |
</div> | |
<div class="form-group"> | |
<input autocomplete="off" autofocus class="form-control" name="shares" placeholder="Shares" type="text"/> | |
</div> | |
<div class="form-group"> | |
<button class="btn btn-default" type="submit">Sell</button> | |
</div> | |
</fieldset> | |
</form> | |
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.container | |
{ | |
/* center contents */ | |
margin-left: auto; | |
margin-right: auto; | |
text-align: center; | |
} | |
/* colors for navbar */ | |
.navbar-default .navbar-brand .blue | |
{ | |
color: #537fbe; | |
} | |
.navbar-default .navbar-brand .red | |
{ | |
color: #ea433b; | |
} | |
.navbar-default .navbar-brand .yellow | |
{ | |
color: rgb(200, 200, 200); | |
} | |
.navbar-default .navbar-brand .green | |
{ | |
color: #2e944b; | |
} | |
main .form-control | |
{ | |
/* center form controls */ | |
display: inline-block; | |
/* override Bootstrap's 100% width for form controls */ | |
width: auto; | |
} | |
main img | |
{ | |
/* constrain images on small screens */ | |
max-width: 100%; | |
} | |
main td | |
{ | |
/* left-align tables' cells */ | |
text-align: left; | |
} | |
body | |
{ | |
background-color: rgb(240, 240, 240); | |
} | |
th | |
{ | |
background-color: rgb(200, 200, 200); | |
color: black; | |
} | |
td | |
{ | |
background-color: rgb(230, 230, 240); | |
color: black; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can't register