CS50 "Finance" problem, "Buy" function - Harvard CS50 course - python

Currently I am receiving the following errors for the "buy" section of the code. The code will run successfully and handles "buy" orders successfully, however check50 is returning these errors and I can't figure out why they are occurring or how to resolve them.
:( buy handles fractional, negative, and non-numeric shares
application raised an exception (see the log for more details)
:( buy handles valid purchase
expected to find "112.00" in page, but it wasn't found
Here is the code:
#app.route("/buy", methods=["GET", "POST"])
#login_required
def buy():
"""Buy shares of stock"""
if request.method == "GET":
return render_template("buy.html")
else:
symbol = request.form.get("symbol")
shares = int(request.form.get("shares"))
if not symbol:
return apology("Must provide ticker")
stock = lookup(symbol.upper())
if stock == None:
return apology("Ticker does not exist")
if shares < 1:
return apology("Minimum purchase is 1 share")
transaction_value = shares * stock["price"]
user_id = session["user_id"]
user_cash_db = db.execute("SELECT cash FROM users WHERE id = :id", id=user_id)
user_cash = user_cash_db[0]["cash"]
if user_cash < transaction_value:
return apology("Not enough funds available")
uptd_cash = user_cash - transaction_value
# update the SQL database
db.execute("UPDATE users SET cash = ? WHERE id = ?", uptd_cash, user_id)
date = datetime.datetime.now()
db.execute("INSERT INTO transactions (user_id, symbol, shares, price, date) VALUES (?, ?, ?, ?, ?)", user_id, stock["symbol"], shares, stock["price"], date)
flash("Bought!")
# redirect to main page
return redirect("/")
And the HTML:
{% extends "layout.html" %}
{% block title %}
Buy
{% endblock %}
{% block main %}
<h1>Buy</h1>
<form action="/buy" method="post">
<div class="mb-3">
<input autocomplete="off" autofocus class="form-control mx-auto w-auto" name="symbol" placeholder="Ticker" type="text">
</div>
<div class="mb-3">
<input autocomplete="off" autofocus class="form-control mx-auto w-auto" name="shares" placeholder="Shares" type="number">
</div>
<button class="btn btn-primary" type="submit">Buy</button>
</form>
{% endblock %}
I have tried using the isdigit() method instead of forcing the shared variable to be an int, but this creates a conflict when ensuring the value of the shares is an int which is greater than 0 which breaks the code.

In app.py use "usd()" function when you pass share price or total price to "render_template()" and in index.html use {{ value | usd}} format to show price of share and total price of share (As suggested in Hints of finance PSet)

Related

The requested URL was not found on the server error while trying to implement sell function with sqlite and flask

I am working on a function that has to access a portfolio of stocks, sell the requested number of stocks if owned at the current price and update the database.
I am able the select the stock and the number of shares but when I click the sell button, I am having the following message:
The requested URL was not found on the server. GET /sell%20method=?symbol=amzn&shares=1 HTTP/1.0" 404 - .
Please have a look at my code:
#app.route("/sell", methods=["GET", "POST"])
#login_required
def sell():
"""Sell shares of stock"""
#Access the current user
user_id= session["user_id"]
if request.method =="POST":
if not request.form.get("symbol") or not request.form.get("shares"):
return apology("Enter a valid symbol or number of shares")
#Define data
symbol=request.form.get("symbol")
shares=request.form.get("shares")
stock=lookup(symbol)
price=stock.get("price")
total_cost=int(shares)*stock["price"]
name=stock.get("name")
transaction_type="sale"
if stock is None:
return apology("Enter a valid symbol")
#Access existing data in DB
rows= db.execute("SELECT symbol, shares FROM stocks WHERE user_id=:user_id GROUP BY symbol", user_id=user_id)
#Validate if the current user owns the shares they are trying to sell
for row in rows:
if row["symbol"]==symbol:
if shares > row["shares"]:
return apology("Enter a valid number of shares")
users=db.execute("SELECT cash FROM users WHERE id=:user_id", user_id=user_id)
new_cash=user[0]["cash"]+total_cost
#Add transaction to the db
#Update DB cash of the user
db.execute ("UPDATE users SET cash=:new_cash WHERE id=:id", new_cash=new_cash, id=user_id)
db.execute("INSERT INTO stocks (user_id, symbol, name, shares, price, total_cost, transaction_type) VALUES(:user_id, :symbol, :name, :shares, :price, :total_cost, :transaction_type)", user_id=user_id, name=name, symbol=symbol, shares= -1*shares, price=price, total_cost=total_cost, transaction_type=transaction_type)
return redirect("/")
else:
share_symbols=[]
symbs = db.execute("SELECT symbol FROM stocks WHERE user_id=:user_id GROUP BY symbol",
user_id=user_id)
for symb in symbs:
share_symbols.append(symb)
return render_template("sell.html", share_symbols=share_symbols)
enter code here
INDEX and BUY functions
#app.route("/")
#login_required
def index():
user_id=session["user_id"]
user = db.execute("SELECT cash FROM users WHERE id=:user_id", user_id=user_id,)
cash = user[0]["cash"]
rows= db.execute("SELECT symbol, name, sum(shares) as shares, price, sum(price*shares) as total_cost, sum(total_cost) as total_cost_sum FROM stocks WHERE user_id=:user_id GROUP BY symbol", user_id=user_id)
total_cost_sum=0
for row in rows:
name=row["name"]
symbol=row["symbol"]
shares=int(row["shares"])
quoted_stock=lookup(symbol)
price=float(quoted_stock["price"])
total_cost=float(shares*price)
total_cost_sum+=total_cost
grand_total=total_cost_sum+cash
return render_template("index.html", cash=cash, rows=rows, grand_total=grand_total)
#app.route("/buy", methods=["GET", "POST"])
#login_required
def buy():
"""Buy shares of stock"""
if request.method =="POST":
symbol = request.form.get("symbol")
stock = lookup(symbol)
shares= int(request.form.get("shares"))
price = stock.get("price")
total_cost= shares*stock["price"]
name= stock.get("name")
transaction_type="purchase"
#Validations
if not symbol:
return apology("Choose a stock to buy!")
if stock is None:
return apology ("Enter a valid symbol")
if not shares or shares < 1:
return apology("Enter a valid number of shares to buy!")
#validating that the current user is the one who bought the shares and who sees the portfolio
user_id = session["user_id"]
user = db.execute("SELECT cash FROM users WHERE id=:user_id", user_id=user_id)
balance = user[0]["cash"]-total_cost
if total_cost > balance:
return apology("Not enough funds")
else:
db.execute ("UPDATE users SET cash=:balance WHERE id=:id", balance=balance, id=user_id)
db.execute("INSERT INTO stocks(user_id, symbol, name, shares, price, total_cost, transaction_type ) VALUES(:user_id, :symbol, :name, :shares, :price, :total_cost, :transaction_type)", user_id=user_id, name=name, symbol=symbol, shares=shares, price=price, total_cost=total_cost, transaction_type=transaction_type)
return redirect("/")
else:
return render_template("buy.html")
SELL HTML
{% extends "layout.html" %}
{% block title %}
Sell
{% endblock %}
{% block main %}
<form action="/sell method="post">
<div class="form-group">
<select name="symbol">
<option disabled selected value=""> Symbol </option>
{% for share_symbol in share_symbols %}
<option value="{{share_symbol.symbol}}"> {{share_symbol.symbol}} </option>
{% endfor %}
</select>
</div>
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="shares" placeholder="Shares" type="text">
</div>
<button class="btn btn-primary" type="submit">Sell</button>
</form>
{% endblock %}
You are missing " from action attribute on <form> tag.
Change <form action="/sell method="post"> to <form action="/sell" method="post">

Flask sending info with GET when POST specified

I am creating an online bookstore and am having trouble with one specific entry. When a user purchases a book they are able to rate it and this functionality works except for the first book they purchased. The function works with the POST method and the form passes in a book_id and a rating which I then enter into a database using SQLAlchemy. The HTML code to submit the form is as such
<form action="/add_rating" method="POST">
<select name="rating" id="rating">
<option value="1">1</option>
<option value="3">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
<button class="btn btn-success" id='adding_rating' name="book_id" type="submit" value='{{display_cart[book].book_id}}'>Rating</button>
</form>
As you can see the method is set to POST. The background function/route to enter the rating into the database is as follows:
#app.route('/add_rating',methods=["POST"])
def add_rating():
if "cart" not in session:
flash("There is nothing in your cart.")
return render_template("cart.html", display_cart = {}, total = 0)
dict_of_books = {}
## getting user info
user = User.query.get(session['user_id'])
user_name = user.first_name + ' ' + user.last_name
##Get order history by user_id
order = get_order_history(session['user_id'])
dict_of_books = {}
total_price = 0
for i in range(len(order)):
total_price += order[i].total
book = get_book_by_id(order[i].book_id)
details = {'book_id': order[i].book_id, "title": book.title, "quantity": order[i].quantity, 'total': order[i].total}
dict_of_books[i] = details
book_id = request.form.get('book_id')
ratings = request.form.get('rating')
user_id = session['user_id']
add_rating = create_rating(ratings,book_id,user_id)
return render_template("rating.html", display_cart = dict_of_books, total = total_price, user_info = user_name)
Now when I rate the first book it is sending the information through GET into the URL, but all the other purchases are sending through POST. The output flask/sqlalchemy gives for the first book is "GET /rating?rating=3&book_id=4 HTTP/1.1" 200 - but for the rest of the books it is "POST /add_rating HTTP/1.1" 200 -. The /rating route is basically just getting information about purchased books and displaying to the page and I do not specify GET/POST in the route. In case the route is needed I will post below, but I cant imagine it being of use to this issue.
#app.route('/rating')
def rate_book():
if "cart" not in session:
flash("There is nothing in your cart.")
return render_template("cart.html", display_cart = {}, total = 0)
dict_of_books = {}
## getting user info
user = User.query.get(session['user_id'])
user_name = user.first_name + ' ' + user.last_name
##Get order history by user_id
order = get_order_history(session['user_id'])
dict_of_books = {}
total_price = 0
for i in range(len(order)):
total_price += order[i].total
book = get_book_by_id(order[i].book_id)
details = {'book_id': order[i].book_id, "title": book.title, "quantity": order[i].quantity, 'total': order[i].total}
dict_of_books[i] = details
return render_template("rating.html", display_cart = dict_of_books, total = total_price, user_info = user_name)
I figured out the issue, it seems that I have added a "parent" form tag and within it added another form tag to handle the button press. By changing the first form (one without method="POST") to just a div tag, it fixed the issue.
Example,
Original:
<div class="container h-100">
<div class="row align-items-center h-100">
<div class="col-6 mx-auto">
<div style="display: flex; align-items: center;">
<form>
...
<form action="/add_rating" method="POST">
...
</form>
</div>
</div>
</div>
</div>
To
<div class="container h-100">
<div class="row align-items-center h-100">
<div class="col-6 mx-auto">
<div style="display: flex; align-items: center;">
<div>
...
<form action="/add_rating" method="POST">
...
</div>
</div>
</div>
</div>
</div>

CS50 Finance writing to database issue

I am having another issue with CS50 finance and would really appreciate some help.
So I am working on the BUY section, and I have implemented an extra step, where you type in what stock you want, and how much of it you want, then it takes you to a new html page to confirm that you want to buy the stock. This pages tells you how many shares you will buy, how much it costs, and what your cash balance will be after you buy the shares.
My issue is that when I go to actually buy the stock on the confirmation page, I get errors that say my variables (specifically balance, shares and price_per_share) are undefined and I cannot figure out why. I will attached the relevant portions of my code below. Thanks again.
Application.PY:
#app.route("/buy", methods=["GET", "POST"])
#login_required
def buy():
if request.method == "POST":
quote = lookup(request.form.get("symbol"))
if quote == None:
return apology("invalid symbol", 400)
try:
shares = int(request.form.get("shares"))
except:
return apology("shares must be a positive integer", 400)
if shares <= 0:
return apology("can't buy less than or 0 shares", 400)
users= db.execute("SELECT cash FROM users WHERE id= :user_id", user_id=session["user_id"])
cash_remaining = users[0]["cash"]
price_per_share = quote["price"]
total_price = price_per_share * shares
balance = cash_remaining-total_price
symbol=quote["symbol"]
return render_template ("confirmation.html", cash_remaining=cash_remaining, price_per_share=price_per_share, total_price=total_price,shares=shares, symbol=symbol, balance=balance)
else:
return render_template ("buy.html")
#app.route("/confirmation", methods=["GET", "POST"])
#login_required
def confirmation():
if request.method == "POST":
db.execute("UPDATE users SET cash = cash = :balance WHERE id = :user_id", balance=balance, user_id=session["user_id"])
db.execute("INSERT INTO transactions (user_id, symbol, shares, price_per_share) VALUES(:user_id, :symbol, :shares, :price_per_share)",
user_id=session["user_id"],
symbol=request.form.get("symbol"),
shares=shares,
price_per_share=price_per_share)
flash ("Bought!")
return render_template("index.html")
else:
return render_template("confirmation.html")
here is my buy.html code in case needed:
{% extends "layout.html" %}
{% block title %}
Quote
{% endblock %}
{% block main %}
<form action="/buy" method="post">
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="symbol" placeholder="Symbol" type="text" required/>
</div>
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="shares" placeholder="Shares Wanted" type="number" min="1" required />
</div>
<button class="btn btn-primary" type="submit">Get Price</button>
</form>
{% endblock %}
and finally my confirmation.html:
{% extends "layout.html" %}
{% block title %}
Quote
{% endblock %}
{% block main %}
<form action="/confirmation" method="post">
<p>Stock Symbol ={{symbol}}</p>
<p>Shares to be purchased = {{shares}}</p>
<p>Total Transaction Cost {{total_price | usd}}</p>
<p>Current Cash = {{cash_remaining | usd}}</p>
<p>Balance after transaction = {{balance |usd}}</p>
<button class="btn btn-primary" type="submit">Buy</button>
</form>
{% endblock %}
There are no values submitted with the form in confirmation.html. Some options:
add the data to the session array
create hidden input controls to carry the data back to the server
give the button element a name, and a json encoded string of the values that confirmation will need as its value.
The confirmation route doesn't (try to) get any values from the form (eg request.form.get).

Connecting data from one route to another in Flask and inputting the data into the SQL table with SQL-alchemy

I'm creating a Flask web app. I'm running into a hiccup connecting the data captured from one page to the next page. I can input data for the address captured from 'address.html' through the notice route into table 1("locations"). But I can't seem to pull the data from the 'notice.html', which captures a submit type button value and is supposed to input it into table 2 ("notice_types") along with the foreign key. Immediately below are the SQL tables and further down are the routes and html pages.
class Address (db.Model):
__tablename__="locations"
id = db.Column(db.Integer, primary_key=True)
address = db.Column(db.String, nullable=False)
notice_types = db.relationship("Notice", backref="locations", lazy=True)
def add_notice(self, notice):
n = Notice(notice=notice, address_id=self.id)
db.session.add(n)
db.session.commit()
class Notice (db.Model):
__tablename__="notice_types"
id = db.Column(db.Integer, primary_key=True)
notice = db.Column(db.String, nullable=False)
address_id = db.Column(db.Integer, db.ForeignKey("locations.id"), nullable=False
Routes:
#app.route('/')
def location():
return render_template('address.html')
#app.route('/notice', methods = ["GET", "POST"])
def notice():
location = Address.query.all()
address = request.form.get('address')
if not address:
return render_template("error.html")
location = Address(address=address)
db.session.add(location)
db.session.commit()
return render_template('notice.html', address=address, location=location)
#app.route('/action', methods = ["GET", "POST"])
def action():
notice = request.form.get('first_n')
try:
address_id = request.args.get("address", None)
except ValueError:
return render_template("error.html")
address = Address.query.get(address_id)
if not address:
return render_template("error.html")
notice_type = Notice(notice=notice, address=address)
db.session.add(location)
db.session.commit()
return render_template('thirtysixty.html', address=address)
Below are samples of the two html pages:
<form action="{{ url_for('notice') }}" method="post">
<div class="form-group">
<label align='left' class='thick'>Address: </label>
<br>
<input list="locations" name="address" id="address" type="text" placeholder="street,
city, state, zip code">
</div>
<p class='a'> 123 Example Southeast, Oakland, CA, 94590</p>
<div class="form-group">
<button type="submit" class="btn btn-primary" minlength="6">Submit</button>
<form action="{{ url_for('action') }}" method="post">
<div class="row">
<div class="form-group">
<input type="submit" class='button' name="first_n" value="3/6 Notice" id="go">
</div>
</form>
Solved my own question:
The SQL tables should stay as they are. Instead I needed to execute the correct db query in the notice route and use jina2 properly in the highlighted template. Below is the fixed code; again it goes over how to properly move form data from one route to another through the use of templates and jinja2. Using these lines I was able to input the data into both SQL tables along with the correct keys.
#app.route('/')
def location():
return render_template('address.html')
#app.route("/notice", methods = ["GET", "POST"])
def notice():
address = request.form.get("address")
if not address:
return render_template("error.html")
db.execute("INSERT INTO locations (address) VALUES (:address)",
{"address": address})
db.commit()
addresses = db.execute("SELECT * FROM locations).fetchall()
return render_template("notice.html", addresses=addresses)
#app.route('/action', methods = ["GET", "POST"])
def action():
notice = request.form.get('first_n')
try:
address_id = request.form.get("address_id")
except ValueError:
return render_template("error.html")
db.execute("INSERT INTO notice_types (notice, address_id) VALUES
(:notice, :address_id)", {"notice":notice, "address_id":address_id})
db.commit()
return render_template("action.html")
The fixed HTML:
The first template highlighted in the question is fine. Below is the template that needed to be fixed.
<form action="{{ url_for('action') }}" method="post">
<div class="form-group">
{% for address in addresses %}
<input type="hidden" name="address_id" value="{{address.id}}">
{% endblock %}
<div class="form-group">
<input type="submit" class='button' name="first_n" value="3/6
Notice" data-value="3/6 notice">
</div>
</form>

How do I update my shopping cart with the product ID using an HTML form?

I am building an e-commerce website with Python, Flask and SQLAlchemy and I am trying to update the quantity of a product on user's cart. I can retrieve the product quantity easily (which is printed on my command line) but I can't get the product ID (which I ultimately need to update my SQL cart table). On the cart page both the quantity and the product ID are properly showing
class Products(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
price = db.Column(db.Integer, nullable=False)
#app.route("/cart", methods=["GET", "POST"])
#login_required
def cart():
cart = Products.query.join(Cart).filter_by(buyer=current_user).all()
if request.method == "POST":
qty = request.form.get("qty")
idpd = request.form.get("idpd")
print("qty",qty)
print("id",idpd)
{% for item in cart %}
<span>
{{item.name}}
</span>
<div>
${{item.price}}
</div>
<form action="{{ url_for('cart') }}" method="post">
<div class="form-group">
<p name="idpd">{{item.id}}</p>
<select class="form-control" name="qty">
<option disabled="Quantity">Qty</option>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
<button type="submit">
Update
</button>
{% endfor %}
When I select the option 2 for the quantity of the product id 1 and click on Update, I expect the output of qty 2 and id 1 but the actual output is:
qty 2
id None
You need to make
<p name="idpd">{{item.id}}</p>
to
<input name="idpd" value="{{item.id}}" hidden>

Categories

Resources