update the dropdown without using submit button - python

Here is a flask application
Use this link to run the application : (Replace XXX with your machine url) http://XXX.0.0.1:5050/?tickers_get=Company2&open_price_get=23
What i am trying is, when the user selects Company2 from the dropdown, the other dropdown should get updated to only 2 prices (22 and 40) without hitting submit button. Then when the user clicks on Submit button, the table should be displayed according
app.py
from flask import Flask, render_template, request
import pandas as pd
import sqlalchemy as sal
from sqlalchemy import create_engine
import pyodbc
import urllib
import numpy as np
app = Flask(__name__)
get_data_through = "manual_entry"
#app.route('/')
def index():
read_df = pd.DataFrame(
[
['Company1', 23, 10000],
['Company2', 22, 40000],
['Company2', 40, 40000]
],
columns=['new_sql_table','Open_price', 'volume']
)
names = set(read_df['new_sql_table'].tolist())
tickers = request.args.getlist('tickers_get')
#tickers_get_to_string = ''.join(tickers) # to convert to string
#open_price_to_filter = np.arange(1000).tolist()
open_price_to_filter = read_df['Open_price'].tolist()
open_price = request.args.getlist('open_price_get')
print(open_price)
open_price_get_to_number = ''.join(open_price)
data = read_df[read_df['new_sql_table'].isin(tickers)]
sum_of_volumns = format(sum(data['volume']), ',')
return render_template('template.html', **locals())
if __name__ == "__main__":
app.run(port = 5050)
template.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles/mainpage.css') }}">
<title>Stocks data</title>
</head>
<body>
<h2>Stocks data</h2><h4>Total Volume is : {{sum_of_volumns}} and selected open price is {{open_price_get_to_number}}</h4>
<form>
<label>Company:</label>
<select name="tickers_get">
{% for name in names %}
<option value="{{ name }}" {% if name in tickers %}selected{% endif %}>{{name}}</option>
{% endfor %}
</select>
<label>Open Price:</label>
<select name="open_price_get" style="width:75px">
{% for op in open_price_to_filter %}
<option value="{{ op }}" {% if op in open_price %}selected{% endif %}>{{op}}</option>
{% endfor %}
</select>
<button type="submit">Submit</button>
</form>
<hr/>
<table border="2" width="100%">
<thead>
<tr>
{% for column in data.columns %}
<th>{{ column }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in data.values %}
<tr style="text-align:center">
{% for cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
I am not able to get the dynamic dropdown in the second filter

In order to dynamically fill your second select box with data depending on the first, you need JavaScript.
In the following example, an event listener is registered for the first input field, which responds to events of type change. If the selection changes within this field, the required data is obtained asynchronously from a second endpoint and inserted into the second input field. The Fetch API is used for this, which receives the data in JSON format.
The ticker symbols will be sent in a comma separated string via GET request. This means that these are transferred as URL parameters, i.e. appended to the URL. With the query request.arg.get(...) the values are queried based on their name. The default is an empty list. In the lambda expression, the string is separated by commas and empty values are filtered out by a loop. You will get a list of ticker symbols.
symbols = request.args.get(
'symbols', # Name of parameter
[], # Default return value
type=lambda x: [y for y in x.split(',') if y] # Split by ',' and filter empty strings
)
All rows whose "Symbol" column is contained in the list are then filtered out of the DataFrame. The "Open Price" column is extracted from these rows and any duplicates are prevented by a set.
open_prices = set(df[df['Symbol'].isin(symbols)]['Open Price'].tolist())
Finally, the received prices are returned as a list in JSON format, contained within a nested structure called "items".
return jsonify(items=list(open_prices))
When the form is finally sent, the data required for display is filtered from the DataFrame and passed to the template.
from flask import (
Flask,
jsonify,
render_template,
request
)
import pandas as pd
df = pd.DataFrame(
[
['Company1', 23, 10000],
['Company2', 22, 40000],
['Company2', 40, 40000],
],
columns=['Symbol','Open Price', 'Volume']
)
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
symbols = set(df['Symbol'].tolist())
open_prices = set(df['Open Price'].tolist())
sel_symbols = symbols
sel_open_prices = open_prices
if request.method == 'POST':
sel_symbols = request.form.getlist('symbols')
sel_open_prices = request.form.getlist('open-prices', type=int)
open_prices = set(df[df['Symbol'].isin(sel_symbols)]['Open Price'].tolist())
data = df[df['Symbol'].isin(sel_symbols)][df['Open Price'].isin(sel_open_prices)]
total_volumes = sum(data['Volume'])
return render_template('index.html', **locals())
#app.route('/open-prices')
def open_prices():
symbols = request.args.get('symbols', [], type=lambda x: [y for y in x.split(',') if y])
open_prices = set(df[df['Symbol'].isin(symbols)]['Open Price'].tolist())
return jsonify(items=list(open_prices))
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Index</title>
<style type="text/css">
table, th, td {
border: 1px solid #9a9a9a;
border-collapse: collapse;
padding: .64rem;
}
th {
border-bottom: 2px solid #9a9a9a;
}
select {
width: 75px;
}
</style>
</head>
<body>
<form method="POST">
<label for="symbols">Symbol</label>
<select name="symbols" id="symbols" multiple>
{% for symbol in symbols -%}
<option value="{{ symbol }}" {% if symbol in sel_symbols %}selected{% endif %}>{{ symbol }}</option>
{% endfor -%}
</select>
<label for="open-prices">Open Price</label>
<select name="open-prices" id="open-prices" multiple>
{% for price in open_prices -%}
<option value="{{ price }}" {% if price in sel_open_prices %}selected{% endif %}>{{ price }}</option>
{% endfor -%}
</select>
<button type="submit">Submit</button>
</form>
<hr />
{% if data.values | count -%}
<h4>Total Volume is {{total_volumes}} and selected
{{ 'are' if sel_open_prices | count > 1 else 'is'}}
{{ sel_open_prices | join(', ') }}.
</h4>
<table width="100%">
<thead>
<tr>
{% for column in data.columns %}
<th>{{ column }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in data.values %}
<tr style="text-align:center">
{% for cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% else -%}
<p>No items found.</p>
{% endif -%}
<script type="text/javascript">
(function(url) {
const priceSelect = document.querySelector('select[name="open-prices"]');
const tickerSelect = document.querySelector('select[name="symbols"]');
tickerSelect && tickerSelect.addEventListener('change', function(event) {
// Get selected items.
let optionsSelected = [];
let options = event.target.options;
for (let opt of options) {
if (opt.selected) {
optionsSelected.push(opt.value || opt.text);
}
}
// Send AJAX request to the server.
fetch(`${url}?symbols=${encodeURIComponent(optionsSelected.join(','))}`)
.then(resp => resp.ok && resp.json())
.then(data => {
// Add the entries to the input field.
priceSelect && (priceSelect.innerHTML = data.items.map(item => {
return `<option value="${item}" selected>${item}</option>`;
}).join(''))
});
});
})({{ url_for('open_prices') | tojson }});
</script>
</body>
</html>

Related

Flask: Keep last selected item in drop downs (Jinja)

i selected a country from a screener function, send this form again to screener function
screenshot
The problem, my form don't keep the last selected country in my form (the variable "country" is send ). I always have the value 'Any' in the form
my list for select
example i
id=1 => 'France'
id=2 => 'Japan'... so if a choose Japan, (id=2) [2]i want to see Japan and not "Any" in the form
[enter image description here]
your help would be appreciated
thank you
screener.html
<form method=POST action="{{ url_for('screener') }}" onchange=submit()>
<table class="table table-sm table-hover">
<thead>
<tr><td>control : {{ country }}</td></tr>
<tr>
<td>Country</td>
<td>
<select id="country" name="country" class="form-select form-select-sm" aria-label="">
<option value="">Any</option>
{% for db_country in db_countries %}
<option value="{{ db_country.id }}" {% if country == db_country.id %} selected {% endif %} >
{{ db_country.id }} - {{ db_country.name }}`</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
</table>
</form>
app.py
`#app.route('/screener/', methods=['POST', 'GET'])
def screener():
db_countries = Country.query.order_by(Country.name.asc()).all()
if request.method == 'POST':
country = request.form['country']
else:
country = 0
return render_template('screener.html', title='Screener',db_countries=db_countries, country=country)`
You are comparing a str to an int. For this reason, the comparison fails.
Change the variable country to an int and it works.
Either you change the type in the endpoint
country = request.form.get('country', 0, type=int)
or in the template.
{% if country | int == db_country.id %} selected {% endif %}

How to take the value from the field and send it to the Flask code for use in the updated page

Have a good day!
I don't know much about Flask
I try to depict insert several (two) objects on the page and embed add the ability (so that you can) update these objects.
There is a main element on the page - the choice of a date range.
When you select a start date and an end date and click the button in the same way (two objects should be updated).
The first object is a table. The table (object) is updated.
The second object is a graph. This object is not updated.
What can be done to update the second object (chart)?
I would be extremely grateful for any help.
It's HTML
<div class="row">
<div class="col-md-3">
<input type="text" name="From" id="From" class="form-control" placeholder="From Date"/>
</div>
<div class="col-md-3">
<input type="text" name="to" id="to" class="form-control" placeholder="To Date"/>
</div>
<div class="col-md-6">
<input type="button" name="range" id="range" value="Range" class="btn btn-success"/>
</div>
</div>
<div id="purchase_order"></div>
<hr>
<div class="row" style="align-content: center">
<div class="text" style="align-content: center">
</div>
<div class="outer-wrapper" style="align-content: center">
<div class="table-wrapper" id="table-wrapper" style="align-content: center">
<table>
<thead>
{% for col in column_names %}
<th>{{col}}</th>
{% endfor %}
</thead>
<tbody>
{% for row in row_data %}
<tr>
{% for cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-md-1">
</div>
<div class="col-md-10">
<div>
<canvas id="myChart" width="800px" style="align-content: center"></canvas>
</div>
</div>
<div class="col-md-1">
</div>
</div>
it's script
<script>
$(document).ready(function (){
$.datepicker.setDefaults({
dateFormat: 'yy-mm-dd'
});
$(function (){
$("#From").datepicker();
$("#to").datepicker();
});
$('#range').click(function (){
var From = $('#From').val();
var to = $('#to').val();
if (From != '' && to != '')
{
$.ajax({
url:"/range",
method:"POST",
data:{From:From, to:to},
success:function (data)
{
$('#table-wrapper').html(data);
$('#table-wrapper').append(data.htmlresponse);
}
});
$.ajax({
url:"/range2",
method:"POST",
data:{From:From, to:to},
success:function (data)
{
$('#myChart').html(data);
$('#myChart').append(data.htmlresponse2);
}
});
}
else
{
alert("Please Select the Date")
}
});
});
</script>
it's Flask code
#app.route('/', methods=['GET','POST'])
#app.route('/index')
def home_page(): # put application's code here
if request.method=="POST":
upload_excel=request.files['upload_excel']
if upload_excel.filename != '':
filepath=os.path.join(app.config["UPLOAD_FOLDER"],upload_excel.filename)
upload_excel.save(filepath)
data=pd.read_excel(upload_excel)
data.to_sql('kotel', con=db.engine, if_exists="append", index=False)
return print(data)
df = pd.read_sql('select * from kotel', con=db.engine)
df['date'] = df['date'].dt.round('2min')
y_data = df['tnv'].tolist()
x_data = df['date'].tolist()
print(y_data)
type(y_data)
print(x_data)
df_graph = df.copy()
df_graph.drop(df_graph.columns[[0, 1, 2]], axis=1, inplace=True)
print(df_graph)
# df['date'] = pd.to_datetime(df['date'], format="%Y.%d.%B %H")
return render_template('index new.html', column_names=df.columns.values, row_data=list(df.values.tolist()), column_names_graph=df_graph.columns.values, os_y = y_data, os_x = x_data)
#app.route("/range", methods=["POST","GET"])
def range():
if request.method == 'POST':
From = request.form['From']
to = request.form['to']
df = pd.read_sql('select * from kotel', con=db.engine)
df['date'] = pd.to_datetime(df['date'])
df = df.loc[(df['date'] >= From) & (df['date'] <= to)]
df['date'] = df['date'].dt.round('2min')
return jsonify({'htmlresponse': render_template('response.html', column_names=df.columns.values, row_data=list(df.values.tolist()))}), df
#app.route("/range2", methods=["POST","GET"])
def range2():
df_new = pd.read_sql('select * from table_1', con=db.engine)
y_data = df_new['temper'].tolist()
x_data = df_new['rashod'].tolist()
return jsonify({'htmlresponse2': render_template('response2.html', os_y = y_data, os_x = x_data)})
it's extended html 1 add
<table>
<thead>
{% for col in column_names %}
<th>{{col}}</th>
{% endfor %}
</thead>
<tbody>
{% for row in row_data %}
<tr>
{% for cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
it's extended html 2 add
<script>
const labels = [{% for item in os_x %}
"{{ item }}",
{% endfor %}];
const data = {
labels: labels,
datasets: [{
label: 'My First dataset',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: [{% for item in os_y %}
{{ item }},
{% endfor %}],
}]
};
const config = {
type: 'line',
data: data,
options: {}
};
</script>
<script>
const myChart = new Chart(
document.getElementById('myChart'),
config
);
</script>
I would really like to be able (so that I can) take a value variable from a form field (dropdown ) and send it to the Flask code so that I can use it in the updated page.
image screen
image screen

how can AJAX dynamically add rows to a table

I was wondering if someone could help me figure out how to add rows to the following html dynamically using AJAX whenever a database query finds more records. I am using python flask and pandas to create a dataframe with nodes information such as when a node is ACTIVE, or in SLEEP mode, LOCKED or UNLOCKED. I was thinking that I could somehow pass that dataframe and iterate through it to create a table with that information and a link to another page for that specific node. Now,I think I have the first part down where I am POSTing the page when it is first brought up with all of the recorded nodes. But I think I'm doing that part wrong too. I suppose I have two questions then:
How can I pass a dataframe to the html and iterate through it correctly?
How can I use AJAX to add another row to the table whenever any other node that registers? I was thinking maybe I pass a list of nodeID's to the HTML when I POST and then have setInterval pass that to the python flask side, query the database, create a new list and compare..removing those that I already added, pass the dataframe of those that are new to AJAX function along with a full list of NodeID's....does that sound right?
So far I was only able to figure this out for a POST but no idea what to do next:
HTML
<div class="container">
<h2>Incubator List</h2>
<p>List of Registered Encubators:</p>
<table class="table table-dark table-striped">
<thead>
<tr>
<th>NodeID</th>
<th>Last Update</th>
<th>Status</th>
<th> </th>
<th>Control Link</th>
</tr>
</thead>
{% for node in nodes %}
<tr>
<td>{{ node.NodeID }}</td>
<td>{{ node.last_update }}</td>
{% if {{ node.Sleep }} == "True" %}
<td><p class="bg-secondary text-white text-center">SLEEP</p>></td>
{% else %}
{% if {{ node.Active }} == "True" %}
<td><p class="bg-success text-white text-center">ACTIVE</p></td>
{% else %}
<td><p class="bg-danger text-white text-center">NOT ACTIVE</p>></td>
{% endif %}
{% endif %}
{% if {{ node.LOCK }} == "True" %}
<i class="fas fa-lock"></i>
{% else %}
<i class="fas fa-unlock"></i>
{% endif %}
<td>
<form action="/nodeslist" METHOD = "POST">
<input type="hidden" id="NodeID" name="NodeID" value={{ node.NodeID }}>
<input TYPE="SUBMIT" value="Access" class = "btn btn-success">
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script>
function UPDATEnodelist(nodeIDlist) {
$.get('/nodeDHT?nodeIDlist ='+nodeIDlist ,
function(data){
const parsed = JSON.parse(data)
nodeIDlist = parsed.nodeIDlist;
something-pandas-dataframe-something;
};
)
};
setInterval(function () {
UPDATEnodelist(nodeIDlist);
return false;
}, 2500);
</script>
{% endblock content %}
python flask
#app.route('/nodeslist', methods=['POST','GET'])
def nodelist():
df= DAO.Pull_Type_Nodes()
if request.method == "POST":
title='List of Registered Nodes')
nodeIDlist = nodes.NodeID.unique()
nodes = df.to_json(orient ='records')
return render_template('nodeslist.html',title=title, nodes=nodes, nodeIDlist=nodeIDlist)
else:
oldnodeIDlist = request.form['NodeID']
add_df = df[~df['NodeID'].isin(oldnodeIDlist)]
new_nodes = add_df.to_json(orient ='records')
return new_nodes,nodeIDlist
Please any help would be greatly appreciated!
EDIT:
The response should be a dataframe with fields 'nodeID','sleep' (bool), 'lock' (bool), 'active' (bool)
<table id="table" class="table table-dark table-striped">
<thead>
<tr>
<th>NodeID</th>
<th>Last Update</th>
<th>Status</th>
<th></th>
<th>Control Link</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<form action="/nodeslist" METHOD="POST">
<input type="hidden" id="NodeID" name="NodeID" value={{ node.NodeID }}>
<input TYPE="SUBMIT" value="Access" class="btn btn-success" id="btn">
</form>
</td>
</tr>
</tbody>
</table>
const date = new Date();
const data = [...Array(10).keys()].map(id => {
return {
id: id+1,
date: new Date((Math.random() * 10000000) + date.getTime()).toLocaleString(),
status: Math.floor(Math.random() * 2),
};
});
const tbody = document.querySelector('#table').querySelector('tbody');
function test(evt) {
evt.preventDefault();
let fragment = document.createDocumentFragment();
data.forEach(d => {
let tr = document.createElement('tr');
Object.keys(d).forEach(x => {
let td = document.createElement('td');
let textNode = document.createTextNode(d[x]);
td.appendChild(textNode);
tr.appendChild(td);
})
fragment.appendChild(tr);
});
tbody.appendChild(fragment);
}
document.querySelector('#btn').onclick = test;
Here working example

table row order are not updatin in the database after ajax call in django

I am using jquery UI .sortable to sort my table rows by drag and drop. I have declare a field map_order in the model as an order update. so the thing is when I am making ajax call to update the model order field. it didn't update it. but when I console log the sort variable it will show the assigning of index to the pk of model.
I have tried to update the filed but it did,nt work
HTML
<tbody id="#layerTable">
{% for layer in layers %} ​
<tr data-pk="{{ layer.id }}" class="ui-state-default"> <td><input type="checkbox" name="ids" value="{{ layer.id }}" /></td>
<td> {{ layer.name }} </td>
<td>{{ layer.heading }}</td>
<td>{{ layer.class_group }}</td>
<td> <span class="glyphicon glyphicon-resize-vertical"></span> {{ layer.map_order }}</td>
<td>{{ layer.map_server }} </td>
<td> {% if layer.sql_schema %}{{ layer.sql_schema }}.{{ layer.sql_table }}{% endif %} </td>
</tr>
​
JS
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$("tbody").sortable({
update: function(event, ui) {
​
sort = {};
window.CSRF_TOKEN = "{{ csrf_token }}";
$("tbody").children().each(function(){
sort[$(this).data('pk')] = $(this).index();
});
​
{#var csrftoken = $('input[name="csrfmiddlewaretoken"]').val();#}
$.ajax({
url: "{% url "mapport.maps.layers.all" map.id %}sort/",
type: "post",
data:{sort,
csrfmiddlewaretoken: window.CSRF_TOKEN,
},
​
});
console.log(sort)
},
}).disableSelection();
});
​
</script>
views
#csrf_exempt
def sort(self):
for index, pk in enumerate(self.request.POST.getlist('layer[]')):
layer = get_object_or_404(Layer, pk=pk)
layer.map_order = index
layer.save()
​
return HttpResponse('')
I I have expected to update the field map_order.. but it didn't update. the index is assigning in browser to the id when I drag and drop rows

Flask - Nested FieldList doesn't submit correctly

Here is a form with fieldlist nested in a other fieldlist.
The purpose is to create an Excel like form.
There is one row per city and one column per date (cities and dates are dynamic lists).
I append the dates to the city and then I append the cities to the main form.
The form is loading as expected :
But after submition, it looks like that :
How can I prevend the second appending after submition ?
Here is my py file :
from flask import Flask
from flask import Flask, flash, redirect, render_template, request, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, FieldList, FormField, SubmitField
app = Flask(__name__)
app.config['SECRET_KEY'] = 'key'
class DateForm(FlaskForm):
class Meta:
csrf = False
date = StringField('date')
class CityForm(FlaskForm):
class Meta:
csrf = False
city = StringField('city')
dates = FieldList(FormField(DateForm))
class MainForm(FlaskForm):
cities = FieldList(FormField(CityForm))
submit = SubmitField('Save')
#app.route('/', methods=['GET','POST'])
def home():
cities = ['NY', 'LA']
dates = ["2018", "2019"]
form = MainForm()
if form.validate_on_submit():
flash("validated on submit !", "alert alert-danger")
redirect(url_for('home'))
for c in cities:
city = CityForm()
city.city = c
for d in dates:
date = DateForm()
date.date = d
city.dates.append_entry(date)
form.cities.append_entry(city)
return render_template('template.html', form = form )
if __name__ == "__main__":
app.run(host="127.0.0.1", port="5000" ,debug=True)
And here is my html template :
<html>
<head>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-sm-6 offset-sm-3 p-0">
<form method="POST" action="{{ url_for('home')}}">
{{ form.hidden_tag() }}
<div class="form-group">
<table>
{% for c in form.cities %}
<tr>
<td>{{ c.city }}</td>
{% for d in c.dates %}
<td>{{ d.date.data }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
<div class="form-group">
{{ form.submit(class="btn btn-secondary") }}
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-6 offset-sm-3 p-0">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<p class="{{ category }}">{{ message }}</p>
{% endfor %}
{% endif %}
{% endwith %}
</div>
</div>
</div>
</body>
</html>
As per comments - missing a return:
return redirect(url_for('home'))

Categories