CS50 Finance writing to database issue - python

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).

Related

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

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)

How can I find the data which written with for loop in Flask

I'm trying to make a database project using with MYSQL and I'm new in Flask. In this project, the user should be able to increase and decrease the number of the products in the database. I print the items in my product table with for loop. The problem is I couldn't select the item which the user selected.
Html:
{% for mydata in data %}
<div class="items filterDiv {{ mydata[1] }}">
<img src="../static/foto/atayumurta.png" alt="">
<br><br>
<label for="item"> {{ mydata[3] }}</label>
<br><br>
<label class="Number">Number: <input class="numb" min="1" name="number" type="number"></label>
<br><br>
<input class="addbutton" type="submit" name="submit_button" value="Add">
<input class="removebutton" type="submit" name="removebutton" value="Remove">
</div>
{% endfor %}
Flask:
#app.route('/mainpage', methods=['POST','GET'])
def mainpage():
cursor.execute("SELECT * FROM product")
fetchdata = cursor.fetchall()
if request.method == 'POST':
if request.form['submit_button'] == 'Add':
number = request.form.get("number")
print(number)
return render_template('main.html', data=fetchdata)
else:
return render_template('main.html', data = fetchdata)
First, I wanted to see the number that the user has selected. So tried to print it but it prints null. As I said im new in flask, open for your suggestions.
Actually, the template contains a for loop for data.
In other words, the number field is not unique. So you need to modify the code. For example
{% for i in range(len(data)) %}
<div class="items filterDiv {{ data[i][1] }}">
<img src="../static/foto/atayumurta.png" alt="">
<br><br>
<label for="item"> {{ data[i][3] }}</label>
<br><br>
<label class="Number">Number: <input class="numb" min="1" name="number{{i}}" type="number"></label>
<br><br>
<input class="addbutton" type="submit" name="submit_button" value="Add">
<input class="removebutton" type="submit" name="removebutton" value="Remove">
</div>
{% endfor %}
Flask:
#app.route('/mainpage', methods=['POST','GET'])
def mainpage():
cursor.execute("SELECT * FROM product")
fetchdata = cursor.fetchall()
if request.method == 'POST':
if request.form['submit_button'] == 'Add':
number = request.form.get("number1")
print(number1)
return render_template('main.html', data=fetchdata)
else:
return render_template('main.html', data = fetchdata)

How to render posts by date using a button to scroll day by day

The app I'm working on it meant to manage food orders. The user log in, creates an order using a simple form, the order is saved to a database,
The home page, by default, takes the current date and displays the orders of the day.
I added some buttons at the end of the page to be able to move to the next day or the previous day, to be able to see past or future orders.
I was able to manage the date handling using two simple functions that takes the day and add +1 or -1 to move to the next/previous day.
GOAL: the goal would be to click those buttons and be able to see tomorrow's orders, yesterday's orders etc.
ISSUE: When I click the buttons the date is displayed correctly but if I move to a day that supposed to have orders, none of those are displayed (the page displays only the date and the message that supposed to be displayed when there are no orders).
I checked the database and the orders are saved correctly and retrieved correctly by the query so, I'm assuming, I'm making a mistake in the way I'm using the routes. I tried to find similar situations but I couldn't find much.
routes:
main = Blueprint('main', __name__)
#main.route("/home", methods=['GET', 'POST'])
#login_required
def home():
session['date'] = date.today().strftime("%d/%m/%Y")
today = session.get('date')
page = request.args.get('page', 1, type=int)
posts = Post.query.filter_by(date=today).paginate(page=page, per_page=5)
return render_template('home.html', posts=posts, date=today)
#main.route("/timeline/<string:when>", methods=['GET', 'POST'])
#login_required
def timeline(when):
session_date = session.get('date')
main_date = datetime.strptime(session_date, '%d/%m/%Y')
if when == 'next':
new_date = next_day(main_date)
session['date'] = new_date
if when == 'prev':
new_date = prev_day(main_date)
session['date'] = new_date
page = request.args.get('page', 1, type=int)
posts = Post.query.filter_by(date=new_date).paginate(page=page, per_page=5)
return render_template('home.html', posts=posts, date=new_date)
home page html:
{% extends "layout.html" %}
{% block content %}
{% if posts.items %}
{% for post in posts.items %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ url_for('static', filename='profile_pics/' + post.author.image_file) }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{{ url_for('users.user_posts', username=post.author.username) }}">{{ post.author.username }}</a>
<small><b>{{ post.date }}</b></small>
</div>
<h2><a class="article-title" href="{{ url_for('posts.post', post_id=post.id) }}">{{ post.name }}</a></h2>
<h5>{{ post.phone_number }}</h5>
<p class="article-content">{{ post.order }}</p>
<p class="article-content">{{ post.post_code }}</p>
<p><small class="text-muted">Order taken on: {{ post.date_posted.strftime('%d/%m/%Y') }}</small></p>
</div>
</article>
{% endfor %}
{% else %}
<h2>{{ date }} <br> No orders recorded so far.</h2>
<p>To add a new order, select the voice "New Order" on the navigation bar above.</p>
{% endif %}
{% for page_num in posts.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
{% if page_num %}
{% if posts.page == page_num %}
<a class="btn btn-info mb-4" href="{{ url_for('main.home', page=page_num) }}">{{ page_num }}</a>
{% else %}
<a class="btn btn-outline-info mb-4" href="{{ url_for('main.home', page=page_num) }}">{{ page_num }}</a>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
<nav aria-label="Date navigation">
<ul class="pagination justify-content-center">
<li class="page-item"><a class="page-link" href="{{ url_for('main.timeline', when='prev') }}">Prev Day</a></li>
<li class="page-item"><a class="page-link" href="{{ url_for('main.timeline', when='next') }}">Next Day</a></li>
</ul>
</nav>
{% endblock content %}
Thanks in advance to whoever can help, I hope is clear what the issue is.
Cheers.
EDIT 1 - FIRST TRY USING THE APPROACH SUGGESTED BY ANDREW CLARK ANSWER, STILL NOT WORKING:
main = Blueprint('main', __name__)
#main.route("/home/", defaults={'view_date': date.today().strftime("%d-%m-%Y")})
#main.route("/home/<string:view_date>", methods=['GET', 'POST'])
#login_required
def home(view_date):
session['date'] = view_date
today = session.get('date')
page = request.args.get('page', 1, type=int)
posts = Post.query.filter_by(date=today).paginate(page=page, per_page=5)
return render_template('home.html', posts=posts, date=today)
#main.route("/redirect_home/<string:when>", methods=['GET', 'POST'])
#login_required
def redirect_home(when):
session_date = session.get('date')
main_date = datetime.strptime(session_date, '%d-%m-%Y')
if when == 'next':
new_date = next_day(main_date)
session['date'] = new_date
if when == 'prev':
new_date = prev_day(main_date)
session['date'] = new_date
return redirect(url_for('main.home', view_date=new_date))
When you change the date, also redirect to your desired route w/ the new date as a parameter
return redirect(url_for('route_name', date_parameter=XXX))
Edit1:
And make sure you use that parameter date inside your route as an input value into your other functions, form population, etc.
Edit 2: replace XXX with your string date
return redirect(url_for('timeline', when=XXX))
Edit3 (ACTUAL SOLUTION):
Okay you need your home route to have date parameter.
How I have handled this, if you want it to default to current date.
In your base jinja layout, instead of having the home button direct to 'home', have it direct to a new route you create called 'redirect_home'.
#main.route("/redirect_home", methods=['GET', 'POST'])
#login_required
def redirect_home():
session['date'] = date.today().strftime("%d/%m/%Y")
today = session.get('date')
# now that you have today's date, redirect to home route
return redirect(url_for('home', view_date=today))
Then you need to change your home route so that you're no longer finding today's date. You are just accepting a parameter for the date.
#main.route("/home/<view_date>", methods=['GET', 'POST'])
#login_required
def home(view_date):
today = view_date
page = request.args.get('page', 1, type=int)
posts = Post.query.filter_by(date=today).paginate(page=page, per_page=5)
return render_template('home.html', posts=posts, date=today)
And then whenever you are pressing previous or next, find the new date, make it a string, and send it as a parameter w/ a redirect back to home.
This is the solution. Cheers
[SOLVED]
BUG:
The problem was in how the date format was handled by the two functions named next_day() and prev_day(): after doing the math to get the next (or the previous) day, the integers are converted back in strings to be able to return a date string.
The problem is that in the query, the table is expecting dates with zeros, like 07-04-2021, but instead the string returned was without zeros (7-4-2021). Because of that the variable posts is empty, therefore nothing would be visualized in the html page.
SOLUTION:
the two functions are being changed as follow:
def next_day(date): # function takes a datetime object
date = date + timedelta(days=1)
return date.strftime('%d-%m-%Y')
def prev_day(date): # function takes a datetime object
date = date - timedelta(days=1)
return date.strftime('%d-%m-%Y')
Thanks to the user Andrew Clark for the help.
Cheers

How to get dynamic html table entries in a form to flask?

I am trying to create a form with an embedded table that the user can dynamically add and remove table rows while entering content into the cell inputs.
HTML
<form id="myForm" action="{{ url_for('hello_world') }}" method="POST">
<div class="form-row text-left">
<div class="col-1 text-left">
<input type="checkbox" id="skills" name="skills" value="Yes">
</div>
<div class = "col-11 text-left">
<h2>TECHNICAL SKILLS</h2>
</div>
</div><!--form-row-->
<div class="form-row">
<table id="myTable" name="skillsTable">
<tr>
<th>Category</th>
<th>Entries</th>
</tr>
</table>
</div><!--form-row-->
<br>
<button type="button" onclick="addSkill()">Add Row</button>
<button type="button" onclick="deleteSkill()">Delete row</button>
<hr>
<input type="submit" value="Submit" onclick="submit()" />
</form>
As you can see in the screenshot [![screenshot of the user form][1]][1] the name attribute is correctly being appended to added cell.
The goal is to have a way to get the table values dynamically created by the user over to the flask template where they can be displayed.
Javascript
<script>
var c1=0;
var c2=0;
function addSkill() {
var table = document.getElementById("myTable");
var row = table.insertRow(-1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
cell1.innerHTML = "<input type='text' value=' ' name=cell1_"+c1.toString()+"> ";
cell2.innerHTML = "<input type='text' value=' ' name=cell2_"+c2.toString()+"> ";
c1++;
c2++;
}
function deleteSkill() {
document.getElementById("myTable").deleteRow(-1);
}
</script>
I have tried setting the name attribute for each newly created cell using a counter, but this still does not show up rendered in the flask template:
flask
#app.route('/hello_world', methods=['GET', 'POST'])
def hello_world():
if request.method == 'POST':
result = {}
try:
skills = request.form['skills']
result['skills'] = skills
result['value'] = request.form['cell1_1']
except:
pass
return render_template("result.html",result = result)
result.html
{% if result.skills %}
<p>{{ result.value }}</p>
{% endif %}
In this example, I would expect to see "Language" show up on rendered after submitting the form if the checkbox is selected.
How can I refer to the table in the form from flask and loop through the <input> elements if they are dynamically created? Thx
[1]: https://i.stack.imgur.com/samhG.png
result.html
{% if result.skills %}
{% set skillsTable = result.skillsTable %}
<h2>TECHNICAL SKILLS</h2>
<table>
{% for skill in skillsTable %}
{% if loop.index|int % 2 == 0 %}
<tr><td>{{ skillsTable.pop(0) }}:</td><td>{{ skillsTable.pop(0) }}</td></tr>
{% else %}
<tr><td>{{ skillsTable.pop(0) }}:</td><td>{{ skillsTable.pop(0) }}</td></tr>
{% endif %}
{% endfor %}
{% endif %}
flask
#app.route('/hello_world', methods=['GET', 'POST'])
def hello_world():
if request.method == 'POST':
result = {}
try:
skills = request.form['skills']
result['skills'] = skills
result['skillsTable'] = []
form = request.form
for key, value in form.items():
if key.startswith("cell"):
result['skillsTable'].append(value)
except:
pass
return render_template("result.html",result = result)

Multiple buttons in html, same view, different values

I'm trying to have my html display a session.counter on an HTML page. The counter should increment by a random number, with 4 different options for the range at which it increments (based on buttons). So far, I have all their actions routing to the same view, but I'm not sure how to code the values into the view! Can I nest multiple if-checks into the view? If so, do I need to redirect immediately after each check, or can it be after the if-checks?
EDIT: I've tried adding hidden inputs to make my view more efficient. I'm still a novice, so if the more descriptive your feedback, the easier it will be for me to implement changes and understand why!
Here's the html:
<div id="goldCount">
<h2>Gold Count: {{ session.request.goldCount }}</h2>
</div>
<div id="goldNode">
<h2>Blood Lotus Master</h2>
<h5>(earns 50 to 200 gold coins)</h5>
<form action='/process_gold' method="post">
{% csrf_token %}
<input type="hidden" name="earn" value="reap">
<button type="submit" name="reap">収める - Reap</button>
</form>
</div>
<div id="goldNode">
<h2>Shadow Dance Troupe</h2>
<h5>(earns 35 to 50 gold coins)</h5>
<form action='/process_gold' method = "post">
{% csrf_token %}
<input type="hidden" name="earn" value="weave">
<button type = "submit" name = "weave">織る - Weave</button>
</form>
</div>
<div id="goldNode">
<h2>Necromancy</h2>
<h5>(earns -200 to 200 gold coins)</h5>
<form action='/process_gold' method="post">
{% csrf_token %}
<input type="hidden" name="earn" value="summon">
<button type = "submit" name="summon">召喚 - Summon</button>
</form>
</div>
<div id="goldNode">
<h2>Clan War!</h2>
<h5>(earns -2000 to 2000 gold coins)</h5>
<form action='/process_gold' method="post">
{% csrf_token %}
<input type="hidden" name="earn" value="summon">
<button type="submit" name="war">影の戦争 - Shadow War</button>
</form>
</div>
and here is the views.py file
def index(request):
if 'goldCount' not in request.session:
request.session['goldCount'] = 0
return render(request, 'index.html')
def process_gold(request):
reap = random.randint(50,200)
weave = random.randint(35,50)
summon = random.randint(-200,200)
war = random.randint(-2000,2000)
print(request.POST)
if request.POST['earn'] == 'reap':
request.session['goldCount'] += random.randint(50,200)
if request.POST['earn'] == 'weave':
request.session['goldCount'] += random.randint(35,50)
if request.POST['earn'] == 'summon':
request.session['goldCount'] += random.randint(-200,200)
if request.POST['earn'] == 'war':
request.session['goldCount'] += random.randint(-2000,2000)
return redirect('/')
If you want to send goldCount through to HTML you need to use a render_template rather than a redirect. Store the goldCount into session then just call {{request.session.goldCount}} in your HTML.
Also, the correct if statement would be:
if 'goldCount' not in request.session:
request.session['goldCount'] = 0

Categories

Resources