I have been stuck on this problem set involving Flask, SQL, HTML regarding a finance website which should display a table of the user's portfolio on its homepage. More information regarding the problem set here.
I have been stuck on the homepage, as I manage to display a table, yet some table data does not appear. I have tried to understand why, and it does not seem that the "current share price" is the issue, as it calculates the total correctly at the bottom. I do not know where the issue arises from HTML or Flask, and if anyone could help me it would be greatly appreciated.
Here is my HTML code (index.html):
<table class="table table-striped" style="width:100%">
<h2> My Portfolio </h2>
<br>
<tr>
<th>Stocks Owned</th>
<th>Shares Owned</th>
<th>Current Price</th>
<th>Total Value</th>
</tr>
{% for stock in stocks %}
<tr>
<td>{{ stock.symbol }}</td>
<td>{{ stock.shares }}</td>
<td>{{ stocks.price }}</td>
<td>{{ stocks.total }}</td>
</tr>
{% endfor %}
<tr>
<td>CASH</td>
<td></td>
<td></td>
<td>{{ cash }}</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td><b>{{ grand_total }}</b></td>
</tr>
</table>
Here is my Flask Code:
#app.route("/")
#login_required
def index():
"""Show portfolio of stocks"""
stocks = db.execute("SELECT symbol, shares FROM transactions where user_id =:id", id = session["user_id"])
updated_cash = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id"])
total_cash = float(updated_cash[0]['cash'])
grand_total = total_cash
for stock in stocks:
symbol = str(stock["symbol"])
shares = int(stock["shares"])
quote = lookup(symbol)
price = float(quote["price"])
total = float(shares * price)
grand_total = grand_total + total
return render_template("index.html", stocks=stocks, cash=total_cash, grand_total= grand_total)
Here is also a picture of the table which outcomes, and the missing fields (see hyperlink):
As you can see, the "Current Price" and "Total Value" fields are empty
Any help would be greatly appreciated, thanks a lot for any input thought!
Consider building a list of dictionaries that you can iterate in Jinja with period qualified names. Right now only stock and shares from query call are the only known elements of stocks. Specifically, replace for loop with list/dictionary comprehension:
def index():
"""Show portfolio of stocks"""
stocks = db.execute("SELECT symbol, shares FROM transactions where user_id =:id", id = session["user_id"])
updated_cash = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id"])
total_cash = float(updated_cash[0]['cash'])
# BUILD LIST OF DICTIONARIES
data_list = [{'symbol': stock["symbol"],
'shares': stock["shares"],
'price': round(lookup(stock["symbol"])["price"], 2),
'total': round(stock["shares"] * lookup(stock["symbol"])["price"], 2)}
for stock in stocks]
# SUM ALL total ELEMENTS
grand_total = total_cash + sum(item['total'] for item in data_list)
return render_template("index.html", stocks=data_list,
cash=total_cash, grand_total=grand_total)
So what you're basically doing here is resetting your value every loop in the for stock in stocks loop. So every time you iterate over it, you're setting a new value for symbol, shares, quote, etc. What you want to do is append them to a list, and send that over.
I think something like this should work:
symbol = shares = quote = price = total = []
for stock in stocks:
symbol.append(str(stock["symbol"])
shares.append(int(stock["shares"])
....
Better yet would be to create a dictionary for each stock that holds the specific values, ensuring that all the relevant values stick together.
Something like this I guess
stocks = {
"AAPL": {
"shares": ,
"quote": ,
"price": ,
"total"
},
"TSLA": {
...
},
...
}
Related
I have been dabbling in some flask and flask WTForms this weekend to make a DYI meal planner and on the whole there has been great guides online. What I have managed to do is set up a flask webapp with a home page, a second page that shows all the meals stored in the database (SQLite), and a page to edit 1 meal at a time, or to add a new meal. This all works which is awesome.
My problem is on the home page I want to show the what meal is planned for the next 7 days for both lunch and dinner. Not only show it, but make it editable which would then write the new value back to the database. After this I am going to build in a "smart" random generator so I don't have to worry about what to cook, just to follow instructions. However I want the ability to manually change some values, hence the editable feature.
I have tried a number of different things online so my codes a bit of a combo, but namely:
How to edit multiple values from database in python flask form
https://prettyprinted.com/tutorials/how-to-use-fieldlist-in-flask-wtf
https://python-adv-web-apps.readthedocs.io/en/latest/flask_db3.html
This is the current table that I generate through the WTform.
Current "form" structure
background info
I have a form set up to capture the different data values (I have tried both form and FlaskForm), and a second form (FlaskForm) set up to read that first form with FieldList(FormField()). I have then prefilled this with a db query, and iterated it using jinja2. Relevant code snippits below.
Form classes
class PlannerForm(Form):
id = HiddenField()
date = StringField('Date', render_kw={'style':'width: 12ch'}, validators=[DataRequired()])
dow = StringField('DoW', render_kw={'style':'width: 12ch'}, validators=[DataRequired()])
lunchname = StringField('lunch', render_kw={'style':'width: 20ch'})
lunchid = HiddenField()
lunchnum = IntegerField('Num lunch', render_kw={'style':'width: 4ch'})
dinnername = StringField('dinner', render_kw={'style':'width: 20ch'})
dinnerid = HiddenField()
dinnernum = IntegerField('Num dinner', render_kw={'style':'width: 4ch'})
#RadioField('Meal Preference (0 - 3)', choices = [0,1,2,3], validators=[DataRequired()]) submit = SubmitField('Submit')
class PlannerSet(FlaskForm):
planset = FieldList(FormField(PlannerForm), min_entries=0)
db class
class Plan(db.Model):
__tablename__ = 'mealplan'
planid = db.Column(db.String, primary_key=True)
date = db.Column(db.Date)
dow = db.Column(db.String)
lunch_name = db.Column(db.String)
lunch_mealid = db.Column(db.String)
lunch_num = db.Column(db.Integer)
dinner_name = db.Column(db.String)
dinner_mealid = db.Column(db.String)
dinner_num = db.Column(db.Integer)
def __init__(self, planid, date, dow, lunch_name, lunch_mealid,lunch_num, dinner_name, dinner_mealid, dinner_num):
self.planid = planid
self.date = date
self.dow = dow
self.lunch_name = lunch_name
self.lunch_mealid = lunch_mealid
self.lunch_num = lunch_num
self.dinner_name = dinner_name
self.dinner_mealid = dinner_mealid
self.dinner_num = dinner_num
home route/function
# Home template
#app.route('/', methods=['GET','POST'])
def home():
today = dt.date.today()
next_week = today + dt.timedelta(days=7)
mealplan = Plan.query.filter(Plan.date >= today).filter(Plan.date <= next_week).all()
message = ''
planform = PlannerSet()
day = namedtuple('Day',['id','date','dow','lunchname','lunchid','lunchnum','dinnername','dinnerid','dinnernum'])
data ={}
## append each day of data
for p in mealplan:
dayform = PlannerForm()
dayform.id.data = p.planid
dayform.date.data = p.date
dayform.dow.data = p.dow
dayform.lunchname.data = p.lunch_name
dayform.lunchid.data = p.lunch_mealid
dayform.lunchnum = p.lunch_num
dayform.dinnername.data = p.dinner_name
dayform.dinnerid.data = p.dinner_mealid
dayform.dinnernum = p.dinner_num
planform.planset.append_entry(dayform)
if planform.validate_on_submit():
print(planform)
data['planset'] = []
for i in planform.planset:
print(i.date.data)
print(i.dow.data)
print(i.lunchname.data)
print(i.lunchnum.data)
print(i.lunchid.data)
data['planset'].append(day(i.id.data, i.date.data, i.dow.data, i.lunchname.data,
i.lunchid.data, i.lunchnum.data, i.dinnername.data,
i.dinnerid.data, i.dinnernum.data)
)
planform = PlannerSet(data = data)
return render_template('home.html', data=mealplan, form = planform, message = message)
form section from template.html
<form action="" method="post">
{{form.hidden_tag()}}
<h3> Current Plan </h3>
{% if message %}
<p class="pt-5"><strong>{{ message }}</strong></p>
{% endif %}
<div>
<table cellpadding = "2px" style="width:100%">
<thead>
<tr>
<th> Date </th>
<th> Day </th>
<th> Lunch </th>
<th> Number for lunch </th>
<th> Dinner </th>
<th> Number for dinner </th>
</tr>
<thead>
<tbody>
{% for day in form.planset %}
<tr>
<td>{{ day.date.data(class="form-control",readonly=true) }}</td>
<td>{{ day.dow.data(readonly=true) }}</td>
<td>{{ day.lunchname.data }}</td>
<td>{{ day.lunchnum }}</td>
<td>{{ day.dinnername.data }}</td>
<td>{{ day.dinnernum }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button> Submit </button>
</div>
</form>
Issue
So... The form pre-populates with the dummy data I have stored in the db which is great. I have a bit of work to do there to link the mealname field to be a drop down and return the mealid as well, but thats future me problem.
When I hit submit, nothing I do works. It fails on validate_on_submit, it overwrites the form values to None (all but the the number of lunch/dinner values actually). Which gives me the error
TypeError: 'NoneType' object is not callable
on line
{{ day.date.data(class="form-control",readonly=true) }}
I probably went overboard with details but figure its needed. Any help for me to:
get the updated form data back into the function, to;
store that data back in the database
Without needing to edit each row at a time ideally.
I am stuck trying to get my flask input function to work. I am trying to complete the CS50 2020 finance problem. Documentation for the problem can be found here. The code block bellow is the error message I get in the console when I try to use the index function is run. The api key I am using is valid and when inserting the whole url for the get request I get the expected information. Code may be somewhat messy, I have not cleaned it up and optimized it yet, because I can't get it to work.
DEBUG: SELECT quantity FROM oStocks WHERE userID = 5 AND stock = 'AAPL'
DEBUG: Starting new HTTPS connection (1): cloud.iexapis.com:443
DEBUG: https://cloud.iexapis.com:443 "GET /stable/stock/GOOGL-AF/quote?token=<MY_API_KEY> HTTP/1.1" 200 None
I have tried all I could think of to try and fix this error. changed and unchanged nearly everything about the lookup function. I can't seem to find what is wrong. Can someone please point me in the right direction. Thank you
flask index:
#app.route("/")
#login_required
def index():
userid = session["user_id"]
owned = db.execute("SELECT stock FROM oStocks WHERE userID = :userid", userid=userid)
OwnedStocks = []
for row in owned:
OwnedStocks.append(row["stock"])
stockInfo = {}
for item in OwnedStocks:
itemInfo = lookup(item)
tmpQuantity = db.execute("SELECT quantity FROM oStocks WHERE userID = :userid AND stock = :stock", userid=userid, stock=item)
quantity = tmpQuantity[0]["quantity"]
sharePrice = itemInfo["price"]
name = itemInfo["name"]
value = quantity * sharePrice
stockInfo[item] = {}
stockInfo[item]['symbol'] = item
stockInfo[item]['name'] = name
stockInfo[item]['shares'] = quantity
stockInfo[item]['price'] = sharePrice
stockInfo[item]['value'] = value
return render_template("portfolio.html", stocks=stockInfo)
Lookup Function:
def lookup(symbol):
"""Look up quote for symbol."""
# Contact API
try:
api_key = os.environ.get("API_KEY")
response = requests.get(f"https://cloud.iexapis.com/stable/stock/{urllib.parse.quote_plus(symbol)}/quote?token={api_key}")
response.raise_for_status()
except requests.RequestException:
return None
# Parse response
try:
quote = response.json()
return {
"name": quote["companyName"],
"price": float(quote["latestPrice"]),
"symbol": quote["symbol"]
}
except (KeyError, TypeError, ValueError):
return None
Portfolio html:
{% extends "layout.html" %}
{% block title %}
Portfolio
{% endblock %}
{% block main %}
<div>
<table class="table table-hover">
<thead>
<tr class="font-weight-bold">
<th scope="col">Symbol</th>
<th scope="col">Name</th>
<th scope="col">Shares</th>
<th scope="col">Price</th>
<th scope="col">Total</th>
</tr>
</thead>
<tbody>
{% for stock in stocks %}
<tr>
<th scope="row">{{ stock['symbol'] }}</th>
<td>{{ stock['name'] }}</td>
<td>{{ stock['shares'] }}</td>
<td>{{ stock['price'] }}</td>
<td>{{ stock['total'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
I have found the solution, now finding it I feel dumb having looked in all the wrong places so long. My mistake was in iterating and calling values from a dictionary passed into a flask template. After much search I ended up realizing and fixing my problem while going through camposha.
My original method was:
{% for stock in stocks %}
<tr>
<th scope="row">{{ stock['symbol'] }}</th>
<td>{{ stock['name'] }}</td>
<td>{{ stock['shares'] }}</td>
<td>{{ stock['price'] }}</td>
<td>{{ stock['total'] }}</td>
</tr>
{% endfor %}
However the proper way to achieve my goal was:
{% for stock, value in stocks.items() %}
<tr>
<th scope="row">{{ value['symbol']['stock'] }}</th>
<td>{{ value['name'] }}</td>
<td>{{ value['shares'] }}</td>
<td>{{ value['price'] }}</td>
<td>{{ value['value'] }}</td>
</tr>
{% endfor %}
I aslo made some adjustments to my index:
def index():
userid = session["user_id"]
owned = db.execute("SELECT stock, quantity FROM oStocks WHERE userID = :userid", userid=userid)
stockInfo = {}
for item in owned:
print(item)
itemInfo = lookup(item['stock'])
quantity = item['quantity']
name = itemInfo['name']
sharePrice = float(itemInfo['price'])
value = quantity * sharePrice
stockInfo[item['stock']] = {}
stockInfo[item['stock']]['symbol'] = item
stockInfo[item['stock']]['name'] = name
stockInfo[item['stock']]['shares'] = quantity
stockInfo[item['stock']]['price'] = sharePrice
stockInfo[item['stock']]['value'] = value
return render_template("portfolio.html", stocks=stockInfo)
Hi, sorry if it seems such simple but honestly the solution I find on google (or my implementation) is breaking my code :/
How can i limit the digits as 2 after decimal point. Even it's 2.99999999 I would like to display as 2.30.
TIA
Here my code for this page (working but displays lots of digits after decimal point):
#app.route("/")
#login_required
def index():
"""Show portfolio of stocks"""
# Update the latest price information for the existing stock
user_id = session["user_id"]
stock_list = db.execute("SELECT stock_id, stock_symbol, shares, unit_price, total_price FROM stocks WHERE owner_name == %s", session["user_id"])
# Iterate through dictionaries in the results (list of rows(dicts))
length = len(stock_list)
for i in range(length):
stock_id = stock_list[i]["stock_id"]
symbol = stock_list[i]["stock_symbol"]
amount = stock_list[i]["shares"]
price_dict = lookup(symbol)
price = price_dict["price"]
total = price * amount
# Update stocks table for the logged in user
db.execute("UPDATE stocks SET unit_price = ?, total_price = ? WHERE stock_id = ?", price, total, stock_id)
# Extract updated data and display in the template
rows = db.execute("SELECT stock_symbol, stock_name, shares, unit_price, total_price FROM stocks WHERE owner_name == %s", session["user_id"])
rows2 = db.execute("SELECT cash FROM users WHERE username == %s", session["user_id"])
assets_list = db.execute("SELECT total_price FROM stocks WHERE owner_name == %s", session["user_id"])
stock_assets = 0.00
cash_asset_list = db.execute("SELECT cash FROM users WHERE username == %s", session["user_id"])
cash_asset = cash_asset_list[0]["cash"]
for i in range(len(assets_list)):
stock_assets = stock_assets + assets_list[i]["total_price"]
net_assets = stock_assets + cash_asset
return render_template("index.html", rows=rows, rows2=rows2, net_assets=net_assets)
My HTML template for the page
{% extends "layout.html" %}
{% block title %}
Home
{% endblock %}
{% block main %}
<table class="table table-hover">
<tr class="table-info">
<th scope="col">Symbol</th>
<th scope="col">Company</th>
<th scope="col">Shares</th>
<th scope="col">Price</th>
<th scope="col">TOTAL</th>
</tr>
{% for row in rows %}
<tr class="table-light">
<td>{{ row.stock_symbol }}</td>
<td>{{ row.stock_name }}</td>
<td>{{ row.shares }}</td>
<td>{{ row.unit_price }}</td>
<td>{{ row.total_price }}</td>
</tr>
{% endfor %}
</table>
<table class="table table-hover">
<tr class="table-dark">
<th scope="col">Cash Balance</th>
<th scope="col">Total Assets</th>
</tr>
{% for row2 in rows2 %}
<tr class="table-light">
<td>{{ row2.cash }}</td>
<td>{{ net_assets }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
You can create a function:
def truncate(f):
return math.floor(f * 10 ** 2) / 10 ** 2
And it will return the value without rounding with 2 decimal places.
Then when you do total just set it to the function of the result.
In python you can use the function round to limit the decimal for number of places.
x = 1.4499999
you can use round(x, number of decimal places)
x = round(x, 2)
Final output will be
x = 1.50
this is my code
sell1.html
{% for stock in stocks %}
<tr>
<th scope="row" name="stock">{{ stock[0] }}</th>
<td>{{ stock[1] }}</td>
<td>{{ stock[2] }}</td>
<td>{{ stock[3] }}</td>
<td>{{ stock[4] }}</td>
<td>Sell </td>
</tr>
{% endfor %}
app.py
#app.route("/sell/<string:stock>", methods=["GET", "POST"])
#login_required
def sell1(stock):
"""Sell shares of stock"""
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# collect relevant informations
amount = int(request.form.get("amount"))
symbol = request.form.get("symbol")
price = lookup(symbol)["price"]
value = round(price * float(amount))
# Update stocks table
amount_before = db.execute("SELECT amount FROM stocks WHERE user_id = :user AND symbol = :symbol",
symbol=symbol, user=session["user_id"])[0]['amount']
amount_after = amount_before - amount
# delete stock from table if we sold every unit we had
if amount_after == 0:
db.execute("DELETE FROM stocks WHERE user_id = :user AND symbol = :symbol",
symbol=symbol, user=session["user_id"])
# stop the transaction if the user does not have enough stocks
elif amount_after < 0:
return apology("That's more than the stocks you own")
# otherwise update with new value
else:
db.execute("UPDATE stocks SET amount = :amount WHERE user_id = :user AND symbol = :symbol",
symbol=symbol, user=session["user_id"], amount=amount_after)
# calculate and update user's cash
cash = db.execute("SELECT cash FROM users WHERE id = :user",
user=session["user_id"])[0]['cash']
cash_after = cash + price * float(amount)
db.execute("UPDATE users SET cash = :cash WHERE id = :user",
cash=cash_after, user=session["user_id"])
# Update history table
db.execute("INSERT INTO transactions(user_id, symbol, amount, value) VALUES (:user, :symbol, :amount, :value)",
user=session["user_id"], symbol=symbol, amount=-amount, value=value)
# Redirect user to home page with success message
flash("Sold!")
return redirect("/")
# User reached route via GET (as by clicking a link or via redirect)
else:
# Add Symbol
# query database with the transactions history
rows = db.execute("SELECT symbol, amount FROM stocks WHERE (user_id = :user AND symbol = :stock)",
user=session["user_id"], stock=stock)
stocks = {}
for row in rows:
stocks[row['symbol']] = row['amount']
return render_template("sell1.html", stocks=stocks)
and
in index.html:
{% for stock in stocks %}
<tr>
<th scope="row" name="stock">{{ stock[0] }}</th>
<td>{{ stock[1] }}</td>
<td>{{ stock[2] }}</td>
<td>{{ stock[3] }}</td>
<td>{{ stock[4] }}</td>
<td>Sell </td>
</tr>
index page works fine as it send this Get:
"GET /sell1/AAPL HTTP/1.1" 404 -
But after I have issue as it is a 404 page with this url:
http://127.0.0.1:5000/sell1/AAPL
I tried to check sql query and it works fine.
this is schema of stocks table:
'stocks' ('id' integer PRIMARY KEY AUTOINCREMENT NOT NULL, 'user_id' integer NOT NULL, 'symbol' char(4) NOT NULL, 'amount' integer NOT NULL);
thanks for help as I m new in flask and python...
thanks to mouse tail,
In my route, I forgot sell1
#app.route("/sell1/<string:stock>", methods=["GET", "POST"])
I have 2 fields, total annual leaves and annual leaves taken,
using .aggregate(Sum()) I've calculated the of total annual leaves taken for an employee.
but now I want to calculate the remaining leaves available for the user, which is
total annual leaves - total annual leaves taken
but I'm having trouble doing it because the annual_taken is a string and not a float
Its giving me this error :
unsupported operand type(s) for -: 'float' and 'str'
How can I fix this? Any help is much appreciated
Below is my code :
model.py
class LeavesEntitlement(models.Model):
employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='+')
type = models.ForeignKey(LeavesType, on_delete=models.CASCADE, related_name='+')
days = models.FloatField(null=True, blank=True)
class Leave(models.Model):
employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='+')
type = models.ForeignKey(LeavesType, on_delete=models.CASCADE, related_name='+')
duration = models.FloatField()
View.py
def my_leaves_view(request):
annual = LeavesEntitlement.objects.all().filter(employee=request.user.profile.employee.id, type=1)
annual_taken = Leave.objects.all().filter(employee=request.user.profile.employee.id, type=1).aggregate(Sum('duration'))
for annual_field in annual:
for field in annual_taken:
annual_available = annual_field.days - field
context = {'form': form,
'annual': annual,
'annual_taken': annual_taken,
'annual_available': annual_available
}
return render(request, 'hrm/my_leaves/my_leaves_content.html', context)
HTML
<table id="my_entitlement_table" class="display table table-hover table-responsive" width="100%">
<thead>
<tr>
<th class="small text-muted text-uppercase"><strong>Leave Type</strong></th>
<th class="small text-muted text-uppercase"><strong>Total</strong></th>
<th class="small text-muted text-uppercase"><strong>Taken</strong></th>
<th class="small text-muted text-uppercase"><strong>Available</strong></th>
</tr>
</thead>
<tbody>
{% for annual_field in annual %}
<tr>
<td>{{annual_field.days}}</td>
<td>{{annual_taken.duration__sum}}</td>
<td>{{annual_available}}</td>
</tr>
{% endfor %
</tbody>
In field you have a dict, so
with some refactoring (don't need all if use filter, and more readable if once define employee) your code may look:
employee = request.user.profile.employee.id
annual = LeavesEntitlement.objects.filter(employee=employee, type=1)
annual_taken = Leave.objects.filter(employee=employee, type=1).aggregate(Sum('duration'))
for annual_line in annual:
annual_available = annual_line.days - annual_taken.get('duration__sum', 0)
Hope it helps.
The aggregation sum return a key value pair so you have to get the value from dict.
annual_taken = Leave.objects.all()
.filter(employee=request.user.profile.employee.id,
type=1).aggregate(Sum('duration'))
then
sum_taken = annual_taken.get('duration__sum')
Also this loop in your code
for field in annual_taken:
annual_available = annual_field.days - field
is wrong. You are trying to iterate over a dict. Removing this loop and using sum_taken will work no need to loop