How to use highstock with django - javascript

I can't use Highstock correctly.
I'm trying to display a candlestick chart with using django and highstock.
My code transfers some ohlcv data from django backend to HTML frontend in JSON format.
And Highstock displays chart but not correctly.
I read highstock official documents but I can't understand many options because I'm a newbie(not only python and javascript even programming).
Please look at my code and a result.
I'd like to teach me.
models.py
from django.db import models
class OandaChartData(models.Model):
exchange_name = models.CharField(max_length=50)
pair = models.CharField(max_length=10)
open_time = models.DateTimeField()
open_value = models.FloatField()
high_value = models.FloatField()
low_value = models.FloatField()
close_value = models.FloatField()
volume = models.FloatField()
write_time = models.DateTimeField(null=True)
views.py
from django.shortcuts import render
from .models import OandaChartData
from django.core import serializers
import json
from datetime import timezone
def highstock_view(request):
record_list = []
records = OandaChartData.objects.all().order_by("id")[0:43200]
for record in records:
open_time = record.open_time.replace(tzinfo=timezone.utc).timestamp() * 1000
open_value = record.open_value
high_value = record.high_value
low_value = record.low_value
close_value = record.close_value
volume = record.volume
record_list_tmp = [open_time, open_value, high_value, low_value, close_value, volume]
record_list.append(record_list_tmp)
record_json = json.dumps(record_list)
return render(request, 'scraping/highstock_view.html', {'record_json':record_json})
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HighStockView</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
<div id="container" style="height: 400px; min-width: 310px"></div>
<script>
let data = {{record_json|safe}},
ohlc = [],
volume = [],
datalength = data.length,
groupingUnits = [[
'minutes',
[1, 5, 15, 30]
], [
'hours',
[1, 2, 4, 12]
], [
'month',
[1, 2, 3, 4, 6]
]],
i = 0;
for (i ; i < datalength ; i += 1) {
ohlc.push([
data[i][0],
data[i][1],
data[i][2],
data[i][3],
data[i][4],
]);
volume.push([
data[i][0],
data[i][5],
]);
}
// create the chart
Highcharts.stockChart('container', {
rangeSelector: {
selected: 1
},
title: {
text: 'OANDA USD/JPY'
},
yAxis: [{
labels: {
align: 'right',
x: -3
},
title: {
text: 'OHLC'
},
height: '60%',
lineWidth: 2,
resize: {
enabled: true
}
}, {
labels: {
align: 'right',
x: -3
},
title: {
text: 'Volume'
},
top: '65%',
height: '35%',
offset: 0,
lineWidth: 2
}],
tooltip: {
split: true
},
series: [{
type: 'candlestick',
name: 'USD/JPY',
data: ohlc,
dataGrouping: {
units: groupingUnits
}
}, {
type: 'column',
name: 'Volume',
data: volume,
yAxis: 1,
dataGrouping: {
units: groupingUnits
}
}]
});
</script>
{{ record_json }}
This is screenshot of above HTML page.
JSON data is a 1-minute data for 1 month.
I want to display a 1-minute chart but above HTML seems to display a 1-month chart(But 2 candlesticks(two months period ?) are displayed.)
And above HTML has some grey out button.
note: The raw text following the chart in the HTML is to confirm data transferring correctly.
Please help me.
db.sqlite3 file is in the following link.
https://drive.google.com/file/d/1bFun10rcbOjmqSTv45QfkL26pfMpvzZZ/view?usp=sharing
And sample.txt that received by frontend HTML is in the following link.
https://drive.google.com/file/d/1KXASJDunrKkVHmn5Y9u5R-f1x_ZH5lrn/view?usp=sharing

Related

Send graph values to Chart.js template with Python Flask

I'm trying to make a data visualizer with a Flask and Chart.js api. My application has the following structure:
API
api.py
templates
index.html
static
css
estil.css
js
chartjs.js
(...)
So my idea is to put all the charts in the same file, outside the html. My problem is that I don't know how to send the data that I read from the database with Flask to the chart.js template. Surely it is very easy, but it is the first time that I touch chart.js and javascript and it is resisting me a bit.
(PSEODUCODE)
Python flask api (api.py):
#app.route('/')
def index():
fruits = []
# SQLITE3 QUERY
return render_template("index.html", fruits = fruits)
HTML (index.html):
<canvas id="myChart"></canvas>
<script src="static/js/chartjs.js"
JS (chartjs.js):
const ctx2 = document.getElementById('myChart');
const background_color2 = ['#33a3ec', '#ff6384'];
new Chart(ctx2, {
type: 'doughnut',
data: {
labels: ['Orange', 'Pineaple'],
datasets: [{
data: [82, 38],
/*
* If I put it inside the HTML with <script>TODO</script>
* it works fine for me but if I do it here it doesn't show
* me the graphics:
* data: {% values %},
*/
backgroundColor: background_color2,
borderWidth: 10,
borderColor: "#fbfbfb"
}]
},
options: {
scales: {
display: false
},
animation: {
duration: 1000,
animateRotate: true,
render: false
},
plugins: {
title: {
display: true,
text: "Juice fruits"
},
legend: {
display: true,
position: 'bottom'
}
}
}
});
As always, after not coding for a few hours comes the answer. It occurred to me to create each graph inside a function (like this):
function chartFruitJuice(fruits) {
const ctx = document.getElementById('myChart');
const background_color = ['#33a3ec', '#ffce55', '#4ac1c1', '#ff00ff00'];
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Orange', 'Apple', 'Pineaple'],
datasets: [{
data: fruits,
backgroundColor: background_color,
borderWidth: 10,
borderColor: "#fbfbfb"
}]
}, options: {
scales: {
display: false
},
animation: {
duration: 1000,
animateRotate: true,
render: false
},
plugins: {
datalabels: {
display: true,
align: 'bottom',
backgroundColor: '#ccc',
borderRadius: 3,
font: {
size: 18,
},
},
title: {
display: true,
text: "Fruit juice"
},
legend: {
display: true,
position: 'bottom'
}
}
}
});
}
And in the HTML, pass the Flask values as parameters of said function:
<canvas id="myChart"></canvas>
<script>
charFruitJuice({{ fruits | tojson | safe }});
</script>

Unexpected labels when creating bar graph with plotly.js

Basic Problem
I'm making a bar graph using plotly.js where the x-axis represents dates. Dates are being fed to plotly as strings; when there are only 1 or 2 elements on the x-axis the labels don't match the strings being fed to plotly.
Context
This is part of a web-application using Flask and Python3, the data for the graph is coming from the Python backend; using console.log() statements I have confirmed that the arrays being given to plotly are as expected.
Example code that results in my error
x_labels = ["2019-01-14", "2019-01-15"]
y_axis = [11, 6]
var trace = {
type: 'bar',
x: x_labels,
y: y_axis,
}
var data = [trace]
var layout = {
xaxis: {
title: 'Date',
},
yaxis: {
title: 'Data'
}
}
Plotly.newPlot(graph, data, layout, {responsive: true})
JS Fiddle Demonstration
http://jsfiddle.net/yjk7r4x1/
Expected Output
The x-axis should just be showing dates such as Jan 13, 2019, Jan 14, 2019... etc. Instead, it is showing up as 12:00 Jan 13, 2019, 0:00 Jan 14, 2019... and so on. I assume those are meant to represent time, but I don't know where the time is coming from or why it is being displayed.
When defining your X-Axis you can modify it to include a "Type" and set the value to "category". By default, the library looks at the data attempts to determine the type for you if it is not specified. Here are what your changes should look like:
JFIDDLE:
http://jsfiddle.net/xa1uy5dz/
<head>
<!-- Plotly.js -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<!-- Plotly chart will be drawn inside this DIV -->
<div id="incorrect_output" style="width: 100%; height: 500px;"></div>
<script>
graph = document.getElementById("incorrect_output");
x_labels = ["2019-01-14", "2019-01-15"]
y_axis = [11, 6]
var trace = {
type: 'bar',
x: x_labels,
y: y_axis,
}
var data = [trace]
var layout = {
xaxis: {
title: 'Date',
type: 'category'
},
yaxis: {
title: 'Data'
}
}
Plotly.newPlot(graph, data, layout, {responsive: true})
</script>
<div id="accurate_output" style="width: 100%; height: 500px;"></div>
<script>
graph = document.getElementById("accurate_output");
x_labels = ["2019-01-14", "2019-01-15", "2019-01-16"]
y_axis = [11, 6, 8]
var trace = {
type: 'bar',
x: x_labels,
y: y_axis,
}
var data = [trace]
var layout = {
xaxis: {
title: 'Date',
type: 'category'
},
yaxis: {
title: 'Data'
}
}
Plotly.newPlot(graph, data, layout, {responsive: true})
</script>
</body>
Referenced Info:
https://plot.ly/javascript/reference/#layout-xaxis-type

Label not showing in Chart.js with Grails

I wish to pass data from a Grails controller to a chart.js chart in a Grails view. My code will not display the chart labels correctly.
The issue is that labels (an arrayList of dates) is not being read correctly as an array of strings in Javascript which is causing Chart.js not to display.
Can anyone offer any help?
Any help would be gratefully received. Thanks in advance!
My code is here:
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
<script>
var userResult = ${userResultMap as JSON};
var data = userResult.result;
var labels = userResult.dateCreated;
var config = {
type: 'line',
data: {
labels: testDate,
datasets: [{
label: 'Clinical FRE',
backgroundColor: '#7A564A',
borderColor: '#7A564A',
data: result,
fill: false
}]
},
options: {
legend: {
display: false
},
tooltips: {
enabled: false
},
responsive: true,
scales: {
yAxes: [{
gridLines: {
drawBorder: false,
color: ['#9b1f22', '#9b1f22', '#ed1c24', '#ed1c24', '#f7931f', '#f7931f', '#206b36', '#206b36', '#206b36', '#206b36', '#206b36']
},
ticks: {
min: 0,
max: 100,
stepSize: 10,
callback: function (value) {
return value + "%"
}
}
}]
}
}
};
window.onload = function createChart(data) {
var ctx = document.getElementById('myChart').getContext('2d');
window.myLine = new Chart(ctx, config)
};
</script>
Data is sent from controller using ModelandView command:
#Secured('ROLE_USER')
def home() {
try {
SecUser user = springSecurityService.currentUser
Participant p = Participant.findByUser(user)
Result userResults = Result.findByUser(user)
def userResultsList
def riskLevelMap
def iconClassMapList = []
def riskLevelMapList = []
def colourNameList = []
Map userResultMap = [:]
if (userResults!= null){
userResultsList = userResults.list()
if(userResultsList != null)
userResultsList.each {list->
iconClassMapList.add(previousTestsService?.getIconType(list))
riskLevelMap = riskAdviceService?.riskLevel(list.result)
riskLevelMapList.add(riskLevelMapList)
colourNameList.add(riskLevelMap?.colourName)
}
userResultMap.put("result",userResultsList?.result)
userResultMap.put("dateCreated",userResultsList?.dateCreated)
println userResultMap
println userResultsList.dateCreated
println(userResultsList.dateCreated.getClass())
}
return new ModelAndView('home', [user: user, participant: p, username: user.username,userResultsList: userResultsList,iconClassMapList:iconClassMapList,colourNameList:colourNameList,userResultMap:userResultMap])
} catch (Exception ex) {
log.error(ex.printStackTrace())
}
}
Sample data:
data - [13.7]
labels - [2018-09-17 16:39:00.0]
The main issue here is that grails automatically escapes values as HTML upon insertion in the GSP page. You can suppress this by adding the advice
<%# expressionCodec="none" %>
at the beginning of the GSP page.
Be aware that your application will be less secure after the change. Especially if the data can contain user-created input people might start messing with your application.
Here is a running example using Grails 3.3.8 based on the test data supplied by #Kumar Chapagain, thank you very much.
In the controller you don't need to package the data in a ModelAndView, as this is done automatically by Grails. Just return a map with the needed entries. I prefer to convert the map to JSON within the controller and not in the gsp page as it keeps the control where it belongs and the GSP more simple.
Controller:
package g338
import grails.converters.JSON
class ChartController {
def index() {
Map userResultMap = [:]
List dateCreated = ["2018-09-17 13:07:06.0","2018-09-17 13:27:06.0","2018-09-17 14:27:06.0","2018-09-17 17:27:06.0"]
List result = [50, 56, 23, 42]
userResultMap.put("dateCreated",dateCreated)
userResultMap.put("result",result)
[ userResultMap: userResultMap as JSON ]
}
}
gsp page: views/chart/index.gsp
<%# expressionCodec="none" %>
<!doctype html>
<html>
<head>
<meta name="layout" content="main"/>
<title>Welcome to Grails</title>
</head>
<body>
<canvas id="myChart"></canvas>
<g:javascript>
var result = ${userResultMap};
var data = result.result;
var labels = result.dateCreated;
var config = {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Clinical FRE',
backgroundColor: '#7A564A',
borderColor: '#7A564A',
data: result,
fill: false
}]
},
options: {
legend: {
display: false
},
tooltips: {
enabled: false
},
responsive: true,
scales: {
yAxes: [{
gridLines: {
drawBorder: false,
color: ['#9b1f22', '#9b1f22', '#ed1c24', '#ed1c24', '#f7931f', '#f7931f', '#206b36', '#206b36', '#206b36', '#206b36', '#206b36']
},
ticks: {
min: 0,
max: 100,
stepSize: 10,
callback: function (value) {
return value + "%"
}
}
}]
}
}
};
window.onload = function createChart(data) {
var ctx = document.getElementById('myChart').getContext('2d');
window.myLine = new Chart(ctx, config)
};
</g:javascript>
</body>
</html>
Make sure userResultsList(userListMap) data must be in the form below from the controller.
Map userResultMap = [:]
List dateCreated = ["2018-09-17 13:07:06.0","2018-09-17 13:27:06.0","2018-09-17 14:27:06.0","2018-09-17 17:27:06.0"]
List result = [50, 56, 23, 42]
userResultMap.put("dateCreated",dateCreated)
userResultMap.put("result",result)
Then you need to parse the userResultMap data as JSON if not parsed and do similar like this in gsp page:
<script>
var userResult = ${userResultMap as JSON};
var result = userResult.result;
var labels = userResult.dateCreated;
</script>
You would need to convert your array, object whatever you are using to JSON for JavaScript to understand it.
<g:javascript>
var testDate = ${userResultsList.dateCreated}
var result = ${userResultsList.result as JSON} // make sure grails.converters.JSON is imported
</g:javascript>
var labels = userResult.dateCreated;
var config = {
type: 'line',
data: {
labels: testDate,
you are using labels: testDate,
but assigning labels to labels
try
var testDate = userResult.dateCreated;
**labels: testDate**

Displaying X-Axis as DD/MM/YY HH:MM:SS.mm on Highchart

In high chart I am plotting two lines.
1st line has data points between , 2018-06-15T04:47:16 and 2018-06-15T04:52:16
2nd line has data points between , 2018-06-15T04:52:16 and 2018-06-15T04:57:16
Now I do not want them to display the line on that time period. But they are getting extended through out the period , because (probably) Y-Axis does not have Year, hour,minutes and seconds info.
How do I display and evaluate complete date time info in my X-Axis. Hopefully that will help me to show data on interval points and not through out the graph period.
Here is my code:
Highcharts.chart('container', {
chart: {
type: 'line'
},
title: {
text: 'How to display in DD/MM/YYYY hh:MM:ss'
},
subtitle: {
text: 'In X Axis'
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { // don't display the dummy year
day: '%e. %b',
hour:'%H:%M',
year: '%Y',
millisecond:'millisecond'
},
title: {
text: 'Date'
}
},
yAxis: {
title: {
text: 'Snow depth (m)'
},
min: 0
},
colors: ['#6CF', '#39F', '#06C', '#036', '#000'],
// Define the data points. All series have a dummy year
// of 1970/71 in order to be compared on the same x axis. Note
// that in JavaScript, months start at 0 for January, 1 for February etc.
series: [{
name: "Line1",
data: [
["2018-06-15T04:47:16", 8],
["2018-06-15T04:52:16", 8]
],
lineWidth:10
},
{
name: "Line2",
data: [
["2018-06-15T04:52:16", 14],
["2018-06-15T04:57:16", 8]
],
lineWidth:10
}
]
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/series-label.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
Highcharts needs time in milliseconds for datetime axes.
Based on your comment about not being able to wrap functions on return data, you can do this to get millisecond values for the xAxis:
chart: {
type: 'line',
events: {
load: function() {
var series = this.series;
for (let i = 0; i < series.length; i++) {
let newData = []
for (let j = 0; j < series[i].data.length; j++) {
newData.push({x: new Date(series[i].data[j].name).getTime(), y: series[i].data[j].y});
}
this.series[i].update({
data: newData
}, false);
}
this.redraw();
}
}
},
To always show dd-mm-yy hh:mm:ss.mm you need to set the xAxis label to show this, like this:
xAxis: {
labels: {
format: '{value:%e-%m-%y %H:%M:%S.%L}'
},
...
}
Highcharts.chart('container', {
chart: {
type: 'line',
events: {
load: function() {
var series = this.series;
for (let i = 0; i < series.length; i++) {
let newData = []
for (let j = 0; j < series[i].data.length; j++) {
newData.push({x: new Date(series[i].data[j].name).getTime(), y: series[i].data[j].y});
}
this.series[i].update({
data: newData
}, false);
}
this.redraw();
}
}
},
title: {
text: 'How to display in DD/MM/YYYY hh:MM:ss'
},
subtitle: {
text: 'In X Axis'
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { // don't display the dummy year
day: '%e. %b',
hour: '%H:%M',
year: '%Y',
millisecond: 'millisecond'
},
labels: {
format: '{value:%e-%m-%y %H:%M:%S.%L}'
},
title: {
text: 'Date'
}
},
yAxis: {
title: {
text: 'Snow depth (m)'
},
min: 0
},
colors: ['#6CF', '#39F', '#06C', '#036', '#000'],
// Define the data points. All series have a dummy year
// of 1970/71 in order to be compared on the same x axis. Note
// that in JavaScript, months start at 0 for January, 1 for February etc.
series: [{
name: "Line1",
data: [
["2018-06-15T04:47:16", 8],
["2018-06-15T04:52:16", 8]
],
lineWidth: 10
},
{
name: "Line2",
data: [
["2018-06-15T04:52:16", 14],
["2018-06-15T04:57:16", 8]
],
lineWidth: 10
}
]
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/series-label.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
JSfiddle working example: https://jsfiddle.net/ewolden/j038vrLz/

Show a "Bar" for zero results with morris.js

I'm using Morris.js to display data in charts, Id like to show a bar for every option, even if its zero, currently I get this:
As you can see the values for 'HIV Instant' for age group 21-25 and 26-30 are 0, how would I show a line (just a thin one) for each result? is this possible with Morris? I've searched the docs for bar charts & searched google & SO but cant find anything? Any help is appreciated. Cheers
Here is the code for generating said chart:
// break up the object
var age_barParts = [];
$.each( results_by_age_chart, function(key , val){
age_barParts.push({
'test': key,
'u16': val['u16'],
'16-20': val['16-20'],
'21-25': val['21-25'],
'26-30': val['26-30'],
'31-49': val['31-49'],
'50+': val['50+']
});
});
//build the graph
var age_bar = Morris.Bar({
element: 'age_bar',
data: age_barParts,
xkey: ['test'],
ykeys: ['u16', '16-20', '21-25', '26-30', '31-49', '50+'],
labels: ["Under 16s", "16 to 20", "21 to 25", "26 to 30", "31 to 49", "Over 50s"],
barColors: color_array,
hideHover: 'auto',
resize: 'true',
gridTextSize: 16,
gridTextColor: '#5cb85c',
xLabelAngle: '70',
resize: true,
padding: 40
});
In Morris.js there is no option to force drawing of a line for zero values, but you can set them to a very low non-zero value, for example 0.05.
Below an example with Morris:
var age_barParts = [];
age_barParts.push({
'test': 'HIV',
'u16': 3,
'16-20': 5,
'21-25': 1,
'26-30': 2,
'31-49': 8,
'50+': 2
});
age_barParts.push({
'test': 'HIV Instant',
'u16': 1,
'16-20': 4,
'21-25': 0.05,
'26-30': 0.05,
'31-49': 7,
'50+': 3
});
var age_bar = Morris.Bar({
element: 'age_bar',
data: age_barParts,
xkey: ['test'],
ykeys: ['u16', '16-20', '21-25', '26-30', '31-49', '50+'],
labels: ["Under 16s", "16 to 20", "21 to 25", "26 to 30", "31 to 49", "Over 50s"],
hideHover: 'auto',
resize: 'true',
gridTextSize: 16,
gridTextColor: '#5cb85c',
xLabelAngle: '70',
resize: true,
padding: 40
});
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
<div id="age_bar" style="height: 450px;"></div>
As alternative you can adopt Highcharts which has a specific plotOption.
Below an example with Highcharts:
$(function () {
$('#age_bar').highcharts({
chart: {
type: 'column'
},
title: {
text: 'HIV stats'
},
xAxis: {
categories: ['HIV', 'HIV Instant']
},
yAxis: {
title: {
text: '#'
}
},
plotOptions: {
column: {
minPointLength: 3
}
},
colors: ["#0b62a4","#7a92a3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],
series: [{
name: 'u16',
data: [1, 0]
}, {
name: '16-20',
data: [5, 4]
}, {
name: '21-25',
data: [1, 0]
}, {
name: '26-30',
data: [2, 0]
}, {
name: '31-49',
data: [8, 7]
}, {
name: '50+',
data: [2, 3]
}],
});
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//code.highcharts.com/highcharts.js"></script>
<div id="age_bar" style="height: 450px;"></div>
Update:
Here is a jsfiddle (for Morris) which modifies the hover legend replacing 0.05 with 0. You can improve this approach editing the hoverCallback function:
https://jsfiddle.net/beaver71/zm8wt4pj/
If you compile from coffee script files, just add after lastTop += size in morris.bar.coffee
if size == 0 then size = 1
Otherwise, if you just want to modify the javascript file, just add after lastTop += size; in morris.js
if (size === 0) { size = 1; }

Categories

Resources