I am trying to build small web application fetchehing data from MySQL database with help of ajax date filter and applying dates and rendering table and chart in frontend I can able to get Table with my script not able to get chart how to do changes for getting chart and table as well this is main.py file
from flask import Flask, render_template, request, jsonify, flash, redirect
from flask_mysqldb import MySQL, MySQLdb # pip install flask-mysqldb https://github.com/alexferl/flask-mysqldb
app = Flask(__name__)
app.secret_key = "caircocoders-ednalan"
app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'xxxx'
app.config['MYSQL_DB'] = "battery_voltage"
app.config['MYSQL_CURSORCLASS'] = 'DictCursor'
mysql = MySQL(app)
@app.route('/')
def index():
cursor= mysql.connection.cursor()
cur = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cur.execute("SELECT * FROM voltage ORDER BY date desc")
orders = cur.fetchall()
return render_template('index.html', orders=orders)
@app.route("/range", methods=["POST", "GET"])
def range():
cur = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
if request.method == 'POST':
From = request.form['From']
to = request.form['to']
print(From)
print(to)
query = "SELECT * FROM voltage WHERE date BETWEEN '{}' AND '{}'".format(From, to)
cur.execute(query)
ordersrange = cur.fetchall()
return jsonify({'htmlresponse': render_template('response.html', ordersrange=ordersrange)})
if __name__ == "__main__":
app.run(debug=True,port="2021")
index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Python Flask Date Range Search with jQuery Ajax DatePicker MySQL Database</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css"/>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
</head>
<body>
<br/>
<div class="container">
<h2 align="left">OPSPOD Battery Voltage</h2>
<br/>
<br/>
<div class="col-md-2">
<input type="text" name="From" id="From" class="form-control" placeholder="From Date"/>
</div>
<div class="col-md-2">
<input type="text" name="to" id="to" class="form-control" placeholder="To Date"/>
</div>
<div class="col-md-8">
<input type="button" name="range" id="range" value="Range" class="btn btn-success"/>
</div>
<div class="clearfix"></div>
<br/>
<div id="purchase_order">
<table class="table table-bordered" style="width:40%" align="left" >
<tr>
<th width="5%">Date</th>
<th width="4%">Time</th>
<th width="4%">voltage</th>
<th width="4%">ignition</th>
</tr>
{% for row in orders %}
<tr>
<td>{{row.date}}</td>
<td>{{row.time}}</td>
<td>{{row.voltage}}</td>
<td>{{row.ignition}}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js"></script>
<!-- Script -->
<script>
$(document).ready(function(){
$.datepicker.setDefaults({
dateFormat: 'dd-mm-yy'
});
$(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)
{
$('#purchase_order').html(data);
$('#purchase_order').append(data.htmlresponse);
}
});
}
else
{
alert("Please Select the Date");
}
});
});
</script>
</body>
</html>
response.html
<table class="table table-bordered" style="width:40%" align="left" >
<tr>
<th width="5%">Date</th>
<th width="4%">Time</th>
<th width="4%">voltage</th>
<th width="4%">ignition</th>
</tr>
{% for row in ordersrange %}
<tr>
<td>{{row.date}}</td>
<td>{{row.time}}</td>
<td>{{row.voltage}}</td>
<td>{{row.ignition}}</td>
</tr>
{% endfor %}
</table>
This is the link for the data which I used in MYSQL DB.
With the code above I am able to get the output like this table below but I am trying for graph as well with same date filtering with X-axis time and Y-axis voltage
Let's start by addressing your database issues. Storing dates and times as strings is a bad idea as they use more space and cannot be handled as efficiently as native DATE
/ TIME
types. A date as string '01-12-2022' stored in a VARCHAR
uses 11 Bytes, whereas if you convert it to DATE
it is only 3 Bytes. Similarly for your time data - 8 Bytes as VARCHAR
or 3 Bytes as TIME
. Even better would be to combine the two together as DATETIME
requiring only 5 Bytes, but I shall leave that for you to ponder.
-- Update dates from dd-mm-yyyy (note 4 digit year) to yyyy-mm-dd
UPDATE `voltage` SET `date` = STR_TO_DATE(`date`, '%d-%m-%Y');
-- If your existing dates have 2 digit year then use
UPDATE `voltage` SET `date` = STR_TO_DATE(`date`, '%d-%m-%y');
-- update the column types
ALTER TABLE `voltage`
MODIFY COLUMN `date` DATE NOT NULL,
MODIFY COLUMN `time` TIME NOT NULL;
You should also make sure you have a composite index on (date
, time
).
To avoid this answer getting too long, I am not going to include the full content of the index.html
template file but I have made the following changes -
<div id="purchase_order">
to <div id="voltages">
<div id="chart"></div>
before <div id="voltages">
<thead>
around the header row and tbody
around the rest of the table rows<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
after the 2 jQuery scriptsFrom
as from
in various placesThen the inline script -
<script>
const chartLayout = {
hovermode: 'closest',
xaxis: {
type: 'date',
dtick: 10800000,
hoverformat: '%H:%M:%S',
tickformat: '%H:00\n%d %b',
rangebreaks: [{ pattern: 'hour' }]
}
};
Plotly.react('chart', [{ x: [/* leaving these for you to figure out */], y: [], line: { simplify: false } }], chartLayout);
$(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:"GET",
data:{from:from, to:to},
success:function(data)
{
let x = [], y = [], rows = '';
for (const row of data) {
x[x.length] = `${row.date} ${row.time}`;
y[y.length] = row.voltage;
rows += `<tr><td>${row.date}</td><td>${row.time}</td><td>${row.voltage}</td><td>${row.ignition}</td></tr>`;
}
// update table content
$('#voltages > table > tbody').html(rows);
// update chart
Plotly.react('chart', [{ x: x, y: y, line: { simplify: false } }], chartLayout);
}
});
}
else
{
alert("Please Select the Date");
}
});
});
</script>
And this is the modified /range
route -
@app.route('/range')
def range():
cur = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
fromDate = request.args.get('from')
toDate = request.args.get('to')
query = """
SELECT CAST(`date` AS CHAR) AS `date`, CAST(`time` AS CHAR) AS `time`, `voltage`, `ignition`
FROM voltage
WHERE date BETWEEN '{}' AND '{}'
ORDER BY date, time
""".format(fromDate, toDate)
cur.execute(query)
voltages = cur.fetchall()
return jsonify(voltages)
The date
and time
have been cast to CHAR
s in the SELECT
as json.dumps()
(used by jsonify) does not like handling them as their native types. You should switch to using parameterized prepared statements to mitigate the current SQLi vulnerabilities.