How to autorefresh SVG content in flask - javascript

I am trying to make auto filling graph with pygal on flask, I have tried the setInterval from jQuery in Javascript and text values are refreshing correctly, but still can't make autorefresh of the graph. Here are my sources:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type=text/javascript>
setInterval(
function()
{
$.getJSON(
'/_data',
{},
function(data)
{
$("#x-value").text('X: '+data.xval);
$("#y-value").text('Y: '+data.yval);
$("#z-value").text('Z: '+data.zval);
$("#chart").text(data.chart)
});
},
500);
</script>
...
<div id=x-value>X</div>
<div id=y-value>Y</div>
<div id=z-value>Z</div>
</div>
<div class="col-lg-6">
<object id="chart" type="image/svg+xml"></object>
</div>
I have tried many combinations of the "Object" and div tags, nothings's working..
Here is my python:
def data():
data = get_last()
charts = chart()
return jsonify(xval=data['X'],
yval=data['Y'],
zval=data['Z'],
chart=charts)
def chart():
data = get_last_hun()
chart = line_route(data)
return chart
def get_last():
vals = db.get_last()
return vals
def get_last_hun():
arr = {'X':[],'Y':[],'Z':[]}
latest = db.get_last()['id']
start = 1
if latest > 100:
start = latest-100
while start<=latest:
arr['X'].append(db.get_val(start)['X'])
arr['Y'].append(db.get_val(start)['Y'])
arr['Z'].append(db.get_val(start)['Z'])
start = start + 1
return arr
def line_route(data):
chart = pygal.Line()
chart.add('X', data['X'])
chart.add('Y', data['Y'])
chart.add('Z', data['Z'])
chart.render(is_unicode=True)
return render_template('home.html',chart = chart)
Do you know maybe some other way how to make it working, please?
Thank you very much!

Related

is it possible to implement a matplotlib Slider into flask

import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
from PIL import Image
import requests
from io import BytesIO
img_src = 'https://unsplash.it/500/300'
response = requests.get(img_src)
img = Image.open(BytesIO(response.content))
img = np.asarray(img.convert('L'))
fig,ax = plt.subplots()
zlims = (np.percentile(img[:, :], 0), np.percentile(img[:, :], 100 - 0))
im = ax.imshow(img, aspect='auto',vmin=zlims[0], vmax=zlims[1])
threshold = 0.
axthreshold = plt.axes([0.2, 0.01, 0.65, 0.03])
sthreshold = Slider(axthreshold, 'clip', -.1, 15.,
valinit=threshold, valstep=None)
def update(val):
ax.clear()
zlims = (np.percentile(img[:, :], .1+val), np.percentile(img[:, :], 100 - (.1+val)))
print(zlims[0],zlims[1])
ax.imshow(img, aspect='auto',vmin=zlims[0], vmax=zlims[1])
fig.canvas.draw_idle()
sthreshold.on_changed(update)
update(threshold)
this creates a image where you can limit what range the colormap covers with a slider. As "clip" increases the color map variety lessens.
Im am pretty familiar with flask. Is there any way to implement something like this into flask? I would prefer matplotlib but I'm ok with trying anything.
Thanks!
--Update--- I realized that I don't want to refresh the page every time the picture is adjusted. I tried plotly javascript approach. I was happy until I realized the res is too low.
html:
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.plot.ly/plotly-2.11.1.min.js"></script>
<style>
.container {
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<div class="container" id="myDiv"></div>
<div class="container">
<input style="width:50%" id="slide" type="range" min="0" max="5" step="0.025" value=".1" name="name_of_slider">
</div>
<div class="container" id="sliderAmount"></div>
<script>
const z1 = JSON.parse('{{ Lsort | tojson }}');
const IMG = JSON.parse('{{ IMG | tojson }}');
var size = z1.length
var qlower = z1[Math.ceil(size * .1 / 100) - 1]
var qupper = z1[Math.ceil(size * (100-.1) / 100) - 1]
var data = [
{
z: IMG,
type: 'heatmap',
colorscale: 'Viridis',
zauto: false,
zmax: qupper,
zmin: qlower,
}
];
var layout = {
autosize: false,
width: 1000,
height: 600,
};
Plotly.newPlot('myDiv', data, layout);
var slide = document.getElementById('slide'),
sliderDiv = document.getElementById("sliderAmount");
slide.onchange = function() {
sliderDiv.innerHTML = this.value;
var perc = this.value
var qlower = z1[Math.ceil(size * perc / 100) - 1]
var qupper = z1[Math.ceil(size * (100-perc) / 100) - 1]
var data = [
{
z: IMG,
type: 'heatmap',
colorscale: 'Viridis',
zauto: false,
zmax: qupper,
zmin: qlower,
}
];
var layout = {
autosize: false,
width: 1000,
height: 600,
};
Plotly.newPlot('myDiv', data, layout);
}
</script>
</body>
</html>
python:
from PIL import Image
import requests
from io import BytesIO
img_src = 'https://unsplash.it/500/300'
response = requests.get(img_src)
imgarray = Image.open(BytesIO(response.content))
imgarray = np.asarray(imgarray.convert('L'))
n = np.sort(imgarray.ravel()).tolist()
imgarray2D = np.flip(imgarray,axis=0)
imgarray2D = imgarray2D.tolist()
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def main():
return render_template('clip.html',Lsort = n,IMG=imgarray2D)
if __name__ == '__main__':
app.run()
I am no expert in Flask or Javascript, but I did something (maybe clumsy!) similar to a Matplotlib slider using an HTML range slider.
So, you put one of those sliders (with a class="watch" because you're watching it) and an HTML img in your web-page's HTML so you will have at least a slider in the index.html:
<input name="XXX" id="YYY" style="width:100%" class="watch" type="range" step="1" min="0" max="255" value="0">
and also an img:
<img src="{{url_for('static', filename='default.png')}}" id="image" class="img-fluid">
Then attach an onChange() function to the slider like this so it gets called whenever the slider is moved:
$(document).ready(function() {
$(".watch").on( "change input", fChanged);
fChanged(); // do one initial update before user changes anything
});
You'll also need the jQuery CDN stuff. I used:
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
And the function fChanged() gathers up the values of a number of sliders on the HTML page into a JSON to send to Flask endpoint called /_refresh like this:
{# https://flask.palletsprojects.com/en/2.0.x/patterns/jquery/ #}
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
function fChanged(eObj) {
$.getJSON($SCRIPT_ROOT + '/_refresh', {
Hmin: $('#Hmin').val(),
Hmax: $('#Hmax').val(),
Smin: $('#Smin').val(),
Smax: $('#Smax').val(),
Vmin: $('#Vmin').val(),
Vmax: $('#Vmax').val(),
mask: jQuery("input[type='radio']").filter(":checked").attr("value")
}, function(data) {
$("#code").text(data.code);
$("#image").attr("src", "data:image/png;base64,"+data.image);
});
return false;
}
You can see I had 6 sliders, a min and a max for each of Hue, Saturation and Value.
The Flask endpoint returned another JSON containing some code I wanted to display on the webpage and a base64-encoded PNG-format result image that I then used to update an HTML img by writing its src attribute.
The Python code for the _refresh endpoint looks like this - it does some other stuff with Hue, Saturation and Value that isn't interesting to you, but it shows how I processed the image and encoded it for returning as a JSON to the webpage:
#app.route('/_refresh')
def refresh():
# DEBUG print(request.args)
Hmin = request.args.get('Hmin')
Hmax = request.args.get('Hmax')
Smin = request.args.get('Smin')
Smax = request.args.get('Smax')
Vmin = request.args.get('Vmin')
Vmax = request.args.get('Vmax')
mask = request.args.get('mask')
# Do the image processing
if 'localfilename' in session:
im, HSV = loadImage(session['localfilename'])
else:
im, HSV = loadImage('default')
lo = np.array([Hmin,Smin,Vmin], dtype=np.uint8)
hi = np.array([Hmax,Smax,Vmax], dtype=np.uint8)
mask = cv2.inRange(HSV, lo, hi)
alpha = np.zeros((im.shape[0],im.shape[1]), dtype=np.uint8) + mask
res = np.dstack((im, alpha))
# DEBUG
# cv2.imwrite('result.png', res)
_, PNG = cv2.imencode(".png", res)
b64img = encodebytes(PNG.tobytes()).decode('ascii')
response = {
'Status' : 'Success',
'image': b64img,
: 'code': render_template('code.py', Hmin=Hmin, Hmax=Hmax, Smin=Smin, Smax=Smax, Vmin=Vmin, Vmax=Vmax)
}
return jsonify(response) # send the result to client
You are not obliged to use OpenCV for the processing or base64-encoding, you can equally do all that with PIL/Pillow if you prefer - just ask, or look at any of my previous PIL/Pillow answers.
It might all be clumsy and misguided but it worked for me. I'll be glad too if someone else shows a better way!
By the way, if anyone is using macOS and trying to run the code in your question, the GUI with the image and slider pops up briefly and disappears. You can then spend ages reading about Matplotlib backends, how to list them, how to see what's available, how to change them, that you need a "framework" Python installation, or maybe don't and after around an hour, you discover that all you need is to add an extra line as follows:
...
ax.imshow(img, aspect='auto',vmin=zlims[0], vmax=zlims[1])
fig.canvas.draw_idle()
plt.show() # add this line if plot flashes and disappears

Cannot run js script after fetch it (vanilla js)

Hello I've got a problem with fetch data in my javascript. I want to fetch api but everytime when I want to do this, somethink goes wrong. First I want to fetch html template, and this is working fine but when I want to use JS script from this file then it shows that 'function is not defined' I was trying many ways to do that. Below is code what doesn't work
var dataset;
fetch('../templates/mainPage.html')
.then(response => response.text())
.then(data => {
dataset = data;
document.getElementById('mainContent').innerHTML = dataset;
//var script = document.createElement('script');
//script.src = '../js/mainTmpl.js';
//document.body.appendChild(script);
//document.body.innerHTML += '<script defer src="../js/mainTmpl.js"></script>';
//script.addEventListener('load', logDataset);
logDataset();
});
and below is template what I want to use (HTML template and JS script is added to main HTML but that show me 'logDataset is not defined'
<div class="content">
<div class="main-container">
<span class="main-text" id="main-text">{{TEST}}</span>
<div class="blue-line"></div>
<span class="sub-text" id='sub-text'></span>
</div>
</div>
<script>
window.logDataset = function () {
var thathref = window.location.pathname;
document.getElementById('mainContent').innerHTML = dataset;
var mainText = document.getElementById('main-text');
var subtext = document.getElementById('sub-text');
console.log('test');
fetch('http://localhost:8080/'+thathref).then(
function(u){ return u.json(); }
).then(function(data){
var data1 = JSON.parse(JSON.stringify(data));
mainText.innerHTML = data1[0].text;
subtext.innerHTML = data1[1].text;
console.log(data1[0].text);
}
);
}
I've comment different methods that I tried to have working fine but it's still bad (I have to have JS script in other files)
Below screen of error Error
Error2
https://stackblitz.com/edit/js-1clyno?file=index.html
12345678901236789012345678901

Plot data that retrieved from Firebase-Database (Realtime)

I've a problem with plotting an number array in Plotly. So, we can skip to subject without further ado.
Here is HTML code:
<script src="plotly.min.js"></script>
<!-- <link rel="stylesheet" type="text/css" href="styles.css"> -->
</head>
<body>
<script src="https://www.gstatic.com/firebasejs/7.2.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.0/firebase-analytics.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-database.js"></script>
<div class="navbar"><span>Analog Plotter by remidosol</span></div>
<div class="wrapper">
<div id="chart"></div>
<script src="FireConfig.js"></script>
<script>
Plotly.plot('chart',[{
y:[analogval()],
type:'line'
}]);
var cnt = 0;
setInterval(function(){
Plotly.extendTraces('chart',{ y:[[analogval()]]}, [0]);
cnt++;
if(cnt > 300) {
Plotly.relayout('chart',{
xaxis: {
range: [cnt-300,cnt]
}
});
}
},15);
</script>
</div>
</body>
How can I plot number ARRAY that read from Firebase? I changed getData function's return code, once.(like I placed a num array parameter to getData, but it didn't make the plot.ly code work to plot data).
I could read data from Firebase but i couldn't plot it.
Here is view of my website and console:
It reads data but can't plot.
Would you help me please? What's wrong with this code? BTW, Firebase config block is okay, i changed it before create this subject.
I'm waiting for your help. Thank you from now.
Edit:
I can get data and convert it to number by replace and slice methods. It's correct now. But the plot.ly code still don't plot data line.
Following aforementioned function, here is the code below:
function analogval(){
databaseiot.orderByChild("analog").on('value', function(dataSnapshot) {
var arru = dataSnapshot.val().analog;
arru.toString();
arru = arru.replace(/\\r/g,'');
arru = arru.slice(1, 4);
arru = Number(arru);
console.log(arru);
return arru;
//arru = data.val().analog.split(",").map(Number);
})}
The issue is in your analogval() function. If I'm not mistaken, your current implementation of analogval () doesn't return anything. Your return statement is inside of the callback function that you passed to the .on() method. What you need is to have your analogval() function to return the value of the array.
One way to do this is to create a variable (e.g. array) visible to analogval() and set the value of array to be what you read from Firebase, then return array from analogval():
var array;
function analogval(){
databaseiot.orderByChild("analog").on('value', function(dataSnapshot) {
var arru = dataSnapshot.val().analog;
arru.toString();
arru = arru.replace(/\\r/g,'');
arru = arru.slice(1, 4);
arru = Number(arru);
console.log(arru);
array = arru;
});
return array;
}

Formatting API data for client side in a Google Script web app

I am pulling data via a CRM API and successfully rendering that data in the front end of my Google Script web app. But manipulating or formatting this data for the front end is a challenge for me.
In the code below, the Potential Name on the second line is rendering the correct data to the page. But the first line called Quote is showing undefined. This data is the data I am trying to format so that only the last six characters or the string are printed to the page.
Clearly, I must be trying to access the data from the API incorrectly. Could someone please provide me with the correct way to manipulate this data in Google Scripts?
Code.gs
function doGet() {
var templ = HtmlService.createTemplateFromFile('Allied-po');
templ.data = requestRecordFromCRM();
return templ.evaluate()
.setTitle('Purchase Order')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
/*Fetch record data from CRM*/
function requestRecordFromCRM() {
requestedId = '1234';
var authToken = 'XXXX';
var zohoRequestUrl = 'https://crm.zoho.com/crm/private/json/Potentials/getRecordById?&authtoken=' + authToken + '&scope=crmapi&id=' + requestedId;
var response = UrlFetchApp.fetch(zohoRequestUrl);
var sanitizedResponse = (response.getContentText());
/*Sanitize json*/
var output = JSON.parse(sanitizedResponse);
Logger.log(output);
/*Declare the variables you want to print*/
var parsedOutput = output.response.result.Potentials.row.FL;
var recordObj = {}
Logger.log(typeof output)
Logger.log(output.response.result.Potentials.row.FL.length)
for (var i = 0; i < output.response.result.Potentials.row.FL.length; i++) {
if (output.response.result.Potentials.row.FL[i].val == 'Potential Name') {
recordObj.potentialName = output.response.result.Potentials.row.FL[i].content
}
}
return (recordObj);
}
Index.html
<html>
<head>
<base target="_blank">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Purchase Order</title>
<?!= HtmlService.createHtmlOutputFromFile('Stylesheet').getContent(); ?>
</head>
<body>
<div>
Quote: <span id="job-number"><?= data.potentialName ?></span>
</div>
<div>
Potential Name: <?= data.potentialName ?>
</div>
<?!= HtmlService.createHtmlOutputFromFile('Javascript').getContent(); ?>
</body>
</html>
Javascript.html
<!-- Load jQuery, jQuery UI, and Bootstrap libraries -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script>
//Format Job Numbers - return only last six characters in potentialName string
(function() {
var parts = document.getElementById('job-number');
var selectedPart = parts.split(":");
var thePart = selectedPart[0];
return (thePart);
}());
</script>
This particular code retrieves the HTML element but not the innerHTML text
var parts = document.getElementById('job-number');
Instead do this to get the embedded HTML which can used to split the string like so:
var parts = document.getElementById('job-number').innerHTML;
The final code will look like this:
<script>
//Format Job Numbers - return only last six characters in potentialName string
(function() {
var parts = document.getElementById('job-number');
var selectedPart = parts.innerHTML.split(":");
console.log(parts)
console.log(selectedPart)
var thePart = selectedPart[1];
parts.innerHTML = thePart
return (thePart);
}());
</script>
There is limited information on how the object structure looks like? The assumption here is there is six character before ":" in the data.potentialName value. if you rather want exactly six characters you can do this:
var selectedPart = parts.subString(0,6)
Hope that helps!

Google Apps won't allow "Active Content" in HTML, and UiApp class has been depreciated

Have been trying to follow the guide here to creating a pie chart and from there develop some dashboard features in Google Apps Script. I found out that when you implement the calling of a script function (in the example it would be the drawChart() function), that is considered "Active Content" as seen here.
I have seen other examples that don't use HTML, but those all seem to require the use of the UiApp class, which has been depreciated. So, is the only way to get a dashboard/graphing feature in Google Apps Script to have an HTTPS security certificate? It seems rather limiting if this is the case. I see a post or two mentioning a similar frustration getting only a white screen, and I believe that is due to the HTTPS limitation.
Originally I didn't post code because I felt the issue here was pretty clear. Here is the code I have. I also tried a simple HTML "Hello World" file that didn't have any functions/scripts, and that worked. Here is the script as it relates to my Google Sheet:
function OpenChart(){
var html = HtmlService.createHtmlOutputFromFile('DummyChart');
SpreadsheetApp.getUi().showModalDialog(html, "Statistics");
}
Here is the HTML file it calls:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
//Load charts package and visualization API
google.charts.load('current', {'packages': ['corechart']});
google.charts.setOnLoadCallback(MakeChart);
//Here is the script I think could be causing a problem
function MakeChart()
{
var ss = SpreadsheetApp.getActive();
var s = ss.getSheets();
var s = sheets[1];
var UI = SpreadsheetApp.getUi();
var response = UI.prompt("Please enter the first cell in the category").getResponseText();
var ir = s.getRange(response);
var n= 0;
var stored = [];
stored.push(["Income Category", "Frequency"]);
while (ir.getValue()!= "") {
n = n +1;
ir = ir.offset(1, 0);
}
//Above we just set n, below we use n to fill our array
ir = ir.offset(-n,0)
for(i =0; i<n;i++) {
stored.push([ir.getValue(),ir.offset(n+2,0).getValue()]);
ir = ir.offset(1, 0);
}
var data = google.visualization.arrayToDataTable([
stored[]]);
/*I tried to also load data here directly as is seen on the pie chart example I used. This didn't have any affect*/
var options = { 'chartArea:backgroundColor': "Azure",
'is3D': true,
'title': 'Test Chart'};
//Now instantiate it
var chart = new google.visualization.PieChart(document.getElementById('chartDiv'));
chart.draw(data, options);
}
</script>
</head>
<body>
<div id="chartDiv" style="width: 450px; height: 300px;"> </div>
</body>
</html>
Any help appreciated.
Here is a quick example of how this can work. I stuck with the basic example and added the values from the pie chart example to the spreadsheet. The client side will need to call the server side to get the data. The is done with google.script.run. You would modify getData() to suite your needs.
code.gs:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu("charts").addItem("piechart", "openPieChart").addToUi()
}
function openPieChart(){
var html = HtmlService.createHtmlOutputFromFile("piechart");
var ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, "piechart")
}
function getData(){
return SpreadsheetApp.getActiveSheet().getDataRange().getValues()
}
piechart.html
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(function(){google.script.run.withSuccessHandler(drawChart).getData()});
function drawChart(values) {
var data = google.visualization.arrayToDataTable(values);
var options = {
title: 'My Daily Activities'
};
var chart = new google.visualization.PieChart(document.getElementById('piechart'));
chart.draw(data, options);
}
</script>
</head>
<body>
<div id="piechart" style="width: 900px; height: 500px;"></div>
</body>
</html>

Categories

Resources