Generate multiple line charts in Django with ChartJS - javascript

i'm working in Django, to draw some graph in function of my modules. ie: if i have 2 modules, i want 2 charts , if 8 modules, 8 charts.
Modules are integreted in a plugin. so i can list all modules found in a plugin. i did this in Django:
def plugin_graph(request, hub_uid, plugin_uid):
request.session['is_admin'] = True
hub = Hub.objects.get(mac=hub_uid)
fields = []
if request.method == 'GET':
if 'field' in request.GET:
fields.append(request.GET['field'])
plugin = Plugin.objects.get(uid=plugin_uid, hub=Hub.objects.get(mac=hub_uid))
#on recupere lensemble desmodules de ce plug
modules = plugin.get_modules_list()
#pour chak module, tracer son graph
for m in modules:
modules_list = {'uid':m.uid,
'name':m.name,
'version':m.version,
'type':m.type,
'time':m.last_time(),
'rssi':m.rssi(),
'status_icon':m.status_icon()}
module = Module.objects.get(uid=m.uid, plugin=Plugin.objects.get(uid=plugin_uid, hub=Hub.objects.get(mac=hub_uid)))
historic = sorted(ModuleDocument.objects.filter(module=module), key=getKey)
values = get_graph_values(historic=historic, dates=None, fields=fields)
print values
field = None if len(fields) < 1 else fields[0]
return render(request, "admin/graph2.html",
{
'values': values,
'hub': hub_uid,
'plugin': plugin_uid,
'uid': m.uid,
'module': module,
'fields': module.get_number_fields(),
'field': field,
'level': 'module',
}
)
After recovering all my modules i draw the charts like that in javascript:
<script>
var ctx = document.querySelector("#chart");
var data = JSON.parse('{{ values }}'.replace(/"/g, '"'));
var labels = [];
var values = [];
var beginAtZero = true;
for (obj in data) {
labels.push(data[obj].x);
values.push(data[obj].y);
if (data[obj].y < 0) {
beginAtZero = false;
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: "{{field}}",
data: values,
borderColor: '#97168F'
}]
},
options: {
scales: {
xAxes: [{
time: {
unit: 'day'
}
}],
yAxes: [{
ticks: {
beginAtZero:beginAtZero
}
}]
}
}
});
</script>
My problem is just one chart is printed. i would like to put it in a for loop to recuperate all data & labels of each module to draw normally all charts i want.
Thank u for your help

You need to utilise the forloop.counter method in the django template to dynamically name a number of items, otherwise the charts will just get overwritten each time and you are just left with the last chart. Its worth noting that I include this code in the block content not a specific js block.
In the example below, the items that are dynamically named are;
The container div
<div id = 'emg-{{forloop.counter}}'>
The canvas
<canvas id="myChart-{{forloop.counter}}">
The chart JS function name
var func_name = "container-{{forloop.counter}}";
func_name = new Chart(
document.getElementById('myChart-{{forloop.counter}}'),{
type: 'scatter',
I've included the full code below. I appreciate my example is different from your posted code, but it demonstrates the concept of dynamically creating certain elements.
{% for time, name_list, value_list, latitude_list, longitude_list, polyline_list in data %}
{% with forloop.counter as track_object_counter %}
<div id = 'emg-{{forloop.counter}}'>
{% for item in emg_data %}
{% if item.loop_id == track_object_counter %}
<div id = "emg-plot" style="height:15rem; padding-top: 1rem;">
<canvas id="myChart-{{forloop.counter}}">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var y_data = {{item.data|strip_apostrophe}}
var x_data = '{{item.name}}'
var func_name = "container-{{forloop.counter}}";
func_name = new Chart(
document.getElementById('myChart-{{forloop.counter}}'),{
type: 'scatter',
data: {
datasets: [{
showLine: true,
label: x_data,
data: y_data,
backgroundColor: 'rgba(141,49,123,0.7)',
borderWidth: 2,
borderColor: 'rgba(141,49,123,0.7)'
}],
},
});
</script>
</canvas>
</div>
{% endif %}
{% endfor %}
</div>
{% endwith %}
{% endfor %}

Related

Django: Group and then Calculate the Sum of the column values through query

I have a model:
lifecycle_status_choice = (
('Lead', 'Lead'),
('Opportunity', 'Opportunity'),
('Customer', 'Customer'),
)
bill_status_choice = (
('Paid','Paid'),
('Pending','Pending'),
('Partially Paid','Partially Paid'),
)
class Bill(models.Model):
company_name = models.ForeignKey(Contact, on_delete=models.CASCADE)
grand_tot = models.DecimalField(max_digits=9, decimal_places=2)
status = models.CharField(max_length=100, choices=bill_status_choice)
lifecycle_status = models.CharField(max_length=100, choices=lifecycle_status_choice)
...
which has a fk to:
class Contact(models.Model):
associated_company = models.CharField(max_length=100)
...
Views.py
...
from bills.models import Bill
from django.db.models import Sum
def home(request):
data = Bill.objects.all().order_by('date')
context = {
'data ':data ,
}
return render(request, 'home.html',context)
Front end chartsjs:
const ctx = document.getElementById('myChart').getContext('2d');
const myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: [
{% for data in data %} '{{data.company_name}}', {% endfor %}
],
datasets: [{
label: '# of Votes',
data: [{% for data in data %} {{ data.grand_tot }}, {% endfor %}],
The problem:
I am using charts.js in my front end template and I am trying to display a chart in which as a user creates new company names the chart should display the total amount grouped by company names how would I achieve that?
eg:
I create two invoices for "xyz" company with amounts: $100 & $200 || & create one invoice for "abc" company with amount $100
I want the chart to group the "xyz" invoice amounts and display $300 (ie:100+200) || & "abc" = $100
FYI I'm aware I'm using something completely wrong in the view function but its just something that I tried.
Thanks
You can .annotate(…) [Django-doc] with:
from django.db.models import Sum
Contact.objects.annotate(bills_total=Sum('bill__grand_tot'))
The Contact objects that arise from this QuerySet will have an extra attribute .bills_total.
You can filter this with as satus paid with:
from django.db.models import Q, Sum
contacts = Contact.objects.annotate(bills_total=Sum('bill__grand_tot', filter=Q(bill__status='Paid')))
You can thus for example render this with:
{% for contact in contacts %}
{{ contact.associated_company }}: {{ contact.bills_total }}
{% endfor %}
or convert it to a list of Python dictionaries for example and then return it as a JSON blob.

Visual Studio Code: Jinja Curly Braces inline for Javascript variable

I am trying to get Jinja braces to stay in line for a javascript variable used in an apexchart.js call for a flask HTML page.
labels = JSON.parse({{ labels | tojson}})
data = JSON.parse({{ data | tojson}})
The problem I am having is that when I save the html the braces get formatted apart.
labels = JSON.parse({
{
labels | tojson
}
})
data = JSON.parse({
{
data | tojson
}
})
Is there away to stop Visual Studio Code from splitting the Jinja code apart?
Current Code Below:
<!-- language: lang-html -->
<!-- templates/index.html -->
{% extends 'base.html' %} {% block content %}
<title>Dashboard | {{title}}</title>
<div class="flex h-screen justify-center">
<div id="chart" class="h-1/4 w-1/2 content-center"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script>
labels = JSON.parse({
{
labels | tojson
}
})
data = JSON.parse({
{
data | tojson
}
})
var options = {
colors: ['#01ca95'],
chart: {
type: 'bar'
},
dataLabels: {
enabled: false
},
series: [{
name: 'sales',
data: data
}],
xaxis: {
categories: labels
},
yaxis: {
labels: {
formatter: function(data) {
//Setup new Formatting class variable
var Formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
return Formatter.format(data);
}
}
},
}
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
</script>
{% endblock content %}

how can I add a django array element into a js array using an if condition

I have a Django project with data being submitted by form , I am trying to use chart.js to display the data. I need to check whether an array exists() if so then add an item to 'labels' array in 'mycharts.js' . Heres what I have so far
django views.py
ing1 = request.POST.getlist('ingredients1')
ing2 = request.POST.getlist('ingredients2')
ing3 = request.POST.getlist('ingredients3')
( I've passed the above into my context )
mycharts.js
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {chart.js
labels: ['{{ing1.0}}', '{{ing2.0}}', {% if ing3|length > 1 %} '{{ing3.0}}', {% endif %}],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
(etc...)
if I take out the if statement inside of 'labels' array , then it shows the 'ing1' and 'ing2' headings , but I can't get it to work with this if statement , whats the correct way of doing it.
I put 'ing1, 'ing2' and 'ing3' into a list called 'title' inside my views , then put that list into the context , then did the following inside the chart.js script :
{% for item in title %} '{{ item }}', {% endfor %}
now it adds the correct data to the labels array.

Display bar chart values from database with symfony 4

I'm trying to display values from my database using symfony 4, twig and javascript in bar chart graphic this is how my bar chart should look like
but this is what 've got
this is my code
<script>
{% for stock in stocks %}
var barChartData = {
labels : ["{{ stock.idproduit.nom }}"],
datasets : [
{
fillColor : "#FC8213",
data : [{{ stock.quantite }}]
}
]
};
new Chart(document.getElementById("bar").getContext("2d")).Bar(barChartData);
{% endfor %}

Remove " " brackets from array Keys in Twig

Twig malforms my array keys and adds " " brackets to my array keys passed from a symfony2 controller.
The array is passed to a Javascript graphics library which requires:
[{x:"disabled test object",y:"17",label:"disabled test object"}]
Instead {{ array|json_encode|raw }} as suggested by the Twig docs and other SO questions I've read through returns the unreadable:
[{"x":"disabled test object","y":"17","label":"disabled test object"}]
I figure this should not be complicated to achieve but going through the json_encode options so far has not resulted in a clear answer. I am unsure whether there is something I can do from PHP so I've added the tag for now.
EDIT:
I am now attempting to step through the array manually using Twig. {{dump(points)}} confirms it is filled properly
{% set points = chart.dataPoints|json_encode|raw %} <=== this was the problem
dataPoints:
{% for point in points %}
{{ dump(point) }}
{ x: {{ point.x }}, y: {{ point.y }}, label: {{ point.label }} }
{% if loop.lastIndex != true %}
,
{% endif %}
{% endfor %}
But this does not work either as the dump is never reached. Is this the correct way of trying to access objects in a foreach via Twig though? This code is an amalgamation of several Twig docs tutorials.
EDIT 2, the solution:
The line {% set points = chart.dataPoints|json_encode|raw %} turned the whole array into a single string, making it impossible for javascript to interpret as an array. After removing this, all that was left was to make sure the query result had the proper array keys and to transform the X-axis data before passing it to Twig.
$i = 0;
$points = array();
/** #var array $query_result*/
foreach($query_result as &$row) {
foreach($row as $value) {
$point[] = [
'x' => ($i)*10,
'y' => $value['y'],
'label' => $value['label']
];
$points[$i] = $point;
$i++;
}
}
Quotes are not a problem for CanvasJS. As you can see in the example below, "x": works (I took this example):
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "Top Oil Reserves"
},
data: [{
dataPoints: [
{ x: 1, y: 297571, label: "Venezuela"},
{ "x": 2, "y": 267017, label: "Saudi" }
]
}]
});
chart.render();
}
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.min.js"></script>
</head>
<body>
<div id="chartContainer" style="height: 200px; width: 100%;"></div>
</body>
</html>
So we need to provide a JSON array for dataPoints:
Define the PHP array in your Controller's function and pass it to the template:
public function myAction()
{
// …
$json = array(
array(
'x' => 1,
'y' => 297571,
'label' => 'Venezuela',
),
array(
'x' => 2,
'y' => 267017,
'label' => 'Saudi',
),
);
return array(
// …
'json' => $json,
);
}
Display the array in the template and pass it to dataPoints :
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.min.js"></script>
<script>
var json = {{ json|json_encode|raw }};
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "Top Oil Reserves"
},
data: [{
dataPoints: json
}]
});
chart.render();
}
</script>
<div id="chartContainer" style="height: 300px; width: 100%;"></div>
The rendered output will be:
// …
<script>
var json = [{"x":1,"y":297571,"label":"Venezuela"},{"x":2,"y":267017,"label":"Saudi"}];
// …
CanvasJS will be able to read this JavaScript array and display the chart.
It looks like the first one is a JavaScript object, and the second one is JSON, try running JSON.parse on the string like this to convert it back in to an object:
var fixed = JSON.parse('[{"x":"disabled test object","y":"17","label":"disabled test object"}]');
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse

Categories

Resources