Create pie chart with HighCharts in Django error - Blank page - django

I'm trying to create a pie chart with highcharts with Django (with Mac Maverick), but I just get a blank page. Is there some error in my code below?
My data consists of a dictionary with 2 keys (body and id__count), the loop inside it works perfectly in body tag. But it's not working in script tag.
<script>
function numberWithCommas(x) {
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
$(function () {
var chart;
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'categoryPieChart'
},title: {
text: 'Category'
},tooltip: {
formatter: function() {
return '<b>'+ this.point.name +'</b>: '+ numberWithCommas(this.y.toFixed(2));
}
},exporting: {
enabled: false
},plotOptions: {
pie: {
dataLabels: {
enabled: true,
formatter: function() {
return '<b>'+ this.point.name +'</b>: '+ Math.round(this.percentage) +' %';
}
}
}
},series: [{
type: 'pie',
data: [
{% for cat in responses_pie %}
[
'{{ cat.body }}',
{% if cat.id__count > 0 %}
{{ cat.id__count }}
{% else %}
0
{% endif %}
],
{% endfor %}
]
}]
});
});
});
});
</script>
This is the view source on Chrome in the script tag:
function numberWithCommas(x) {
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
$(function () {
var chart;
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'categoryPieChart'
},title: {
text: 'Category'
},tooltip: {
formatter: function() {
return '<b>'+ this.point.name +'</b>: '+ numberWithCommas(this.y.toFixed(2));
}
},exporting: {
enabled: false
},plotOptions: {
pie: {
dataLabels: {
enabled: true,
formatter: function() {
return '<b>'+ this.point.name +'</b>: '+ Math.round(this.percentage) +' %';
}
}
}
},series: [{
type: 'pie',
data: [
[
'apple',
2
],
]
}]
});
});
});
});
And my view (views.py)
def piechart(request):
responses_pie = AnswerRadio.objects.values("body").annotate(Count("id"))
return render(request, 'chart_code_v2.html', {'responses_pie': responses_pie})

You have an extra }); at the end (one from Highcharts.Chart({, one from $(document).ready(function() {, one from $(function () { and one too much). This should be visible as a syntax error in your javascript console.
Also, calling $(document).ready(fn) is superfluous, because $(fn), which you use on the function around it, is a shorthand for $(document).ready(fn).
If you plan to support old Internet Explorer versions, you will also need to remove the last comma from your data, for example like this:
{% for cat in responses_pie %}
[
'{{ cat.body }}',
{% if cat.id__count > 0 %}
{{ cat.id__count }}
{% else %}
0
{% endif %}
]{% if not forloop.last %},{% endif %}
{% endfor %}
Generally, it is better to serialize data in the view via json.dumps and pass the string to the template, as it is less error prone. For example:
import json
def piechart(request):
responses_pie = AnswerRadio.objects.values("body").annotate(Count("id"))
res = []
for cat in responses_pie:
res.append([
cat.body,
cat.id__count,
# ... more stuff
])
return render(request, 'chart_code_v2.html', {'responses_pie_json': json.dumps(res)})
then just use data: {{ responses_pie_json|safe }} in your template. Be aware that some Python objects (datetime, Decimal, ...) need some extra work for JSON serialization.

Related

Get items list count dynamically according to the filtering result

My app displays the total posts count, initially according to the server's response.
However, these posts can be filtered and I need to get the post count dynamically according to the filtering result.
inputFilter:function() {
var vm = this;
return vm.posts.filter((post) => {
var vmPosts = post.title.match(searchValue);
var countFilteredPostsDinammicaly = vmPosts.count(); //??????
return vmPosts;
});
}
Thanks.
You can make a computed property which will return you a filtered array on basis of the query/filters applied.
Another computed property will return you the length of the filtered array i.e filtered post count to show on the DOM.
new Vue({
el: '#app',
data: {
posts: [
{ title: 'apple is best fruit' },
{ title: 'orange is 2020 best fruit' },
{ title: 'apples are sweeter than oranges in 2020' }
],
query: ''
},
computed: {
getFilteredPosts () {
return this.posts.filter(post => post.title.includes(this.query))
},
getFilteredPostsCount () {
return this.getFilteredPosts.length
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input placeholder="search for `apple`" v-model="query"/>
<br/><br/>
Filtered posts count: {{ getFilteredPostsCount }}
<br/><br/>
Filtered posts:
<li v-for="(post, i) in getFilteredPosts" :key="i">
{{ post.title }}
</li>
</div>

Comparing value from html to context

I need to compare a value (variable) extracted from a page to context.
For example:
Color is a dropdown selection.
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
{% if context_value == selected_color %}
.. do something
{% endif %}
})};
Is this possible ? If not, is there some solution for such case ?
I recommend you use Ajax to communicate asynchronously between JavaScript and python (without refreshing the page).
your JS:
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
$.ajax({
method: "POST",
url: 'color_check',
data: selected_color,
success: handleFormSuccess,
error: handleFormError,
})
})
function handleFormSuccess(data, textStatus, jqXHR){
.. do something
}
function handleFormError(jqXHR, textStatus, errorThrown){}
};
Your python view:
def color_check(request):
if request.is_ajax():
selected_color = request.POST
context_value = 'Red'
if selected_color == context_value:
return JsonResponse(True)
EDIT: Arun Singh's solution is simpler and works too. I would only make the paragraph hidden from the user:
<p style="display:none" id="my-data" data-name="{{context_value}}"></p>
<p id='data'>{{ context_value }}</p>
or
<p id="my-data" data-name="{{context_value}}"></p>
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
var djangoData = $('#data').val();
if (djangoData === selected_color){
console.log('do something)
}else{
console.log('do something else')
}
})};

jqgrid use in Django template: Reverse for 'item-jqgrid-entity1'

Trying to revive a Django app that was working 2014-2015 then was turned off for almost 3 years and now is not willing to cooperate.
Mac OS X el Capitan 10.11.6
Python 3.6 (also tried 3.4)
Django 1.8.3
In short, how does notation (in template) {% url 'item-jqgrid-entity1' pk=original.id %} connects to django models Item and Entity1 WITHOUT having item-jqgrid-entity1 listed in url.py ? I am getting
Reverse for 'item-jqgrid-entity1' with arguments '()' and
keyword arguments '{'pk': 123}' not found. 0 pattern(s) tried: []
and it used to work somehow. If I introduce matching URL in urls.py, it works, but I want to know how it was working without it.
For a given model (Item) the default template change_form.html was overriden. The custom one goes like this:
{% extends "admin/change_form.html" %}
{% load admin_urls %}
.
.
var searchable_columns = {
alteration_type: {
searchoptions: {
dataUrl: '{% url 'item-jqgrid-entity1' pk=original.id %}'
}
},
disease: {
searchoptions: {
dataUrl: '{% url 'item-jqgrid-entity2' pk=original.id %}'
}
},
}
.
.
$(document).ready( function () {
$("#table_{{ original.id }}").jqGrid({
autowidth: true,
height: 300,
hidegrid: false,
// double click handler
ondblClickRow: function(rowid, iRow, iCol, e) {
// rowid is object ID i want to edit
// mimics showRelatedObjectPopup()
// in django's RelatedObjectLookups.js
href = '{% url 'admin:some real url here' %}' + rowid + '/?_to_field=id&_popup=1';
$('div.alert').remove();
var win = window.open(href,
'id_drug',
'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus();
return false;
},
// #25: workflow: marking edited rows
gridComplete: function () {
for (var i = 0; i < rowsToColor.length; i++) {
$("#" + rowsToColor[i]).find("td").css("background-color", "#dff0d8");
$("#" + rowsToColor[i]).find("td").css("color", "#468847");
}
// this will overwrite green ones, too.
for (var i = 0; i < incompleteRowsToColor.length; i++) {
$("#" + incompleteRowsToColor[i]).find("td").css("background-color", "#fcf8e3");
$("#" + incompleteRowsToColor[i]).find("td").css("color", "#b94a48");
}
// this will overwrite green and yellow ones, too.
for (var i = 0; i < unworthyRowsToColor.length; i++) {
$("#" + unworthyRowsToColor[i]).find("td").css("background-color", "#f2dede");
$("#" + unworthyRowsToColor[i]).find("td").css("color", "#b94a48");
}
// commented lines are bold.
for (var i = 0; i < commentedRowsToBold.length; i++) {
$("#" + commentedRowsToBold[i]).find("td").css("font-weight", "bold");
}
},
datatype: "json",
colModel: [
{
label: 'Entity1',
name: 'entity1',
width: 45,
// search options
stype: 'select',
searchoptions: searchable_columns.entity1.searchoptions,
editable: true,
edittype: 'select',
editoptions: {
dataUrl: '{% url 'editdata-AllEntity1' %}',
},
},
{
label: 'Entity2',
name: 'entity2',
width: 75,
// search options
stype: 'select',
searchoptions: searchable_columns.entity2.searchoptions,
editable: true,
edittype: 'select',
editoptions: {
dataUrl: '{% url 'editdata-allEntity2' %}',
},
],
viewrecords: true, // show the current page, data rang and total records on the toolbar
caption: "{{ original.pk }}: {{ original }}",
pager: "#tablePager_{{ original.id }}",
}
});
$('#table_{{ original.id }}').jqGrid('filterToolbar');
$('#table_{{ original.id }}').navGrid("#tablePager_{{ original.id }}", {
search: false, // show search button on the toolbar
add: false,
edit: false,
del: false,
refresh: true, position: "left", cloneToTop: true
},

django leaflet_map how to add geojson layer on demand

I have a geodjango project using django-leaflet. A base map is displayed in the body of the html file using {% leaflet_map "mapid" %} . No other initialization is needed for the base map to display. Also, there is a choice box of polygon features that user can select. Upon selection the selected feature should display on top of the base map. As shown, the selected feature is the success response of a post ajax call. I get the correct data, however, I don't know how to display it on the map.
<html>
<head>
{% load leaflet_tags %}
{% leaflet_css %}
{% leaflet_js %}
<style>
#mapid {
width: 870px;
height: 530px;
}
</style>
</head>
<body>
<div> {% leaflet_map "mapid" %} </div>
<form id="aoi-form" method="post" action="" >
{% csrf_token %}
<select id="aoi-choice" name="modelarea_sel_name" >
{% for aoi in modelarea.aois.field.choices %}
<option>{{aoi|title}}</option>
{% endfor %}
</select>
</form>
<script type="text/javascript">
$(document).ready(function() {
// sets the csrf token for all the ajax calls
setCSRFToken();
function displayModelArea(data) {
window.addEventListener("map:init", function(event) {
var map = event.detail.map;
L.geoJson(data, {
onEachFeature: function onEachFeature(feature, layer) {
layer.bindPopup(feature.properties.name);
}
}).addTo(map);
});
}
$('#aoi-choice').on('change', function(e) {
var selecteditem = $(this).find("option:selected").val();
$.ajax({
url: $("#aoi-form").attr("action"),
type: $("#aoi-form").attr("method"),
data: { "aoi-choice": selecteditem },
success: function(response) {
alert(response);
<How to add this geojson to the map in success?>
<something like:>
displayModelArea(response) // this is not working
},
error : function(xhr,errmsg,err) {}
}); // end of ajax
}); // end of on change
}); // end of document
</script>
</body>
</html>
views.py
def rsmgui_home(request):
template_name = 'rsmgui_nav.html'
if request.POST.get("aoi-choice"):
polydata = serialize('geojson', ModelArea.objects.filter(name=request.POST.get("aoi-choice")))
return JsonResponse(polydata, safe=False)
For those interested in the solution, I must say, my original code contained two mistakes:
1) Had to return HttpResponse instead of JsonResponse from the views.py.
2) Had to initialize and get handle of the map (i.e. mymap) outside of the $(document).ready. Although, I am sure I should be able to do the same with jquery, But I did not know how!
FYI - the data format returned from views was somethings like this:
{"type": "FeatureCollection", "features": [{"type": "Feature",
"properties": {
"objectid": 3235,
"name": "WCU-L38E-2",
"shape_area": 5178189.95436,
"shape_len": 20962.4168727,
"model": "rsmgui.modelarea"
},
"id": 3,
"geometry": {
"type": "MultiPolygon",
"coordinates":
[[[[-80.4431746089703, 26.1496150525031], [-80.4438712320284, 26.1494779568429], [-80.4438714835869, 26.1494791485814],
[-80.4438767327393, 26.1495038898062], [-80.443876985872, 26.1495050844166], [-80.4439596517546, 26.1498945523],
[-80.4439599048889, 26.1498957469102], [-80.4445181371852, 26.1525257445301], [-80.444518396688, 26.1525269377307],
[-80.4454063419315, 26.1566079785707], [-80.4454066030393, 26.1566091717754], [-80.4454367956768, 26.1567473825391],
[-80.4454269367566, 26.1568310621773], [-80.4454019935245, 26.1569010164227], [-80.4451487080981, 26.1572161211586],
[-80.4451198504533, 26.1572690055153], [-80.4451124234237, 26.1572826141087], [-80.4450987418606, 26.1573731140696],
[-80.4451097451673, 26.1574483323541], [-80.4451099518947, 26.1574492731537], [-80.4454045318579, 26.1587738519124],
[-80.4454291560893, 26.1588281989547], [-80.4454726925214, 26.158873231686], [-80.4454745412333, 26.1588751446035],
[-80.4457224526486, 26.1590556968988], [-80.4459825369665, 26.1592924192246], [-80.4460113732483, 26.1593318182711],
[-80.446029694147, 26.1594029793378], [-80.4460299489157, 26.1594041725167], [-80.4461426842445, 26.1599298599992],
[-80.4461429406032, 26.159931053184], [-80.4461632993508, 26.1600259843658], [-80.4461635557098, 26.1600271775505],
[-80.4461662854139, 26.1600399110449], [-80.446166541773, 26.1600411042296], [-80.4461669349025, 26.1600429241313],
[-80.4461698308018, 26.1600564420782], [-80.4461703745794, 26.1600589761589], [-80.4461706309386, 26.1600601693436],
[-80.4461724673901, 26.1600687195423], [-80.446172720567, 26.1600699141476], [-80.4461890554485, 26.1601460828104],
[-80.4461892651751, 26.1601470637419], [-80.4461894764895, 26.1601480446796], [-80.4464055948207, 26.1611557705659],
[-80.4464058511845, 26.1611569637501], [-80.4465182479751, 26.1616803740569], [-80.446518504341, 26.1616815672408],
[-80.4467438353217, 26.1627308699823], [-80.4467440932799, 26.1627320631719], [-80.4471482872166, 26.1646036789173],
[-80.4471485483585, 26.1646048721181], [-80.4483626433148, 26.1701518069936], [-80.4483628854181, 26.1701530015516],
[-80.4483814524309, 26.1702450510074], [-80.4483896526105, 26.1703196621089], [-80.44837083869, 26.1703813120244],
[-80.4483349119681, 26.1704454670367], [-80.448238820436, 26.1705762562447], [-80.4480865737154, 26.1708302726741],
[-80.4480562687647, 26.1709111657153], [-80.4480474043405, 26.1709779965095], [-80.4480570552683, 26.1710631695728],
[-80.4480726187489, 26.1711178954947], [-80.4480728735615, 26.1711190886688], [-80.448600444514, 26.1735684214603],
[-80.4486318283615, 26.1736785332924], [-80.4486793668728, 26.1737445068659], [-80.4492087688318, 26.1741548565954],
[-80.4492476879068, 26.1742313275962], [-80.4492734967374, 26.1743198523785], [-80.4492737277215, 26.1743210511907],
[-80.4493637707618, 26.174788553371], [-80.4494647807674, 26.1752251203668], [-80.4494649710993, 26.1752261843278],
[-80.4495030325796, 26.1754400354424], [-80.4495075156305, 26.1755698630249], [-80.4494919585678, 26.1757079020217],
[-80.4494654601658, 26.1758137385183], [-80.4494247803613, 26.1759047014177], [-80.4493654321733, 26.1760018949102],
[-80.449291361239, 26.1760990334811], [-80.449224827617, 26.1761585592545], [-80.4490944858737, 26.176220056103],
[-80.448817683956, 26.176319577968], [-80.4486133770828, 26.176391343208], [-80.448294685515, 26.1764133756087],
[-80.4482742214126, 26.1764147902816], [-80.4482629422319, 26.1764173105563], [-80.4482608358316, 26.1764177810802],
[-80.4481701381423, 26.1764380517997], [-80.4480857500865, 26.1764548904322], [-80.4480278075929, 26.1764455275992],
[-80.4479938505702, 26.1764255169321], [-80.4447866204566, 26.1616803278906], [-80.4427502472453, 26.1523162458952],
[-80.4425042672057, 26.1511130652601], [-80.4424324383669, 26.1508789484218], [-80.4422838184404, 26.1506638659222],
[-80.442263580685, 26.150586872045], [-80.4422584377825, 26.1505622200219], [-80.4422537121117, 26.1505393091941],
[-80.4422484417484, 26.1505144173773], [-80.4422447794031, 26.1504939380542], [-80.4422409109672, 26.1504737273243],
[-80.4422344502975, 26.1504440606653], [-80.4422140566669, 26.1500502089701], [-80.4421980585152, 26.1498452491223],
[-80.4421989899204, 26.149844702483], [-80.4422962649397, 26.149811382303], [-80.4423241174424, 26.1498018149787],
[-80.442339856439, 26.1497964493973], [-80.4423577440382, 26.1497903226474], [-80.4423887913014, 26.1497796872474],
[-80.4424405282263, 26.1497619686973], [-80.4424639436836, 26.1497539446344], [-80.4424890564009, 26.1497459185392],
[-80.4426580984819, 26.1496918891336], [-80.4431746089703, 26.1496150525031]]]]}}],
"crs": {
"type": "link",
"properties": {
"href": "http://spatialreference.org/ref/epsg/4326/",
"type": "proj4"
}
}
};
Corrected code:
<html>
<head>
{% load leaflet_tags %}
{% leaflet_css %}
{% leaflet_js %}
<style>
#mapid {
width: 870px;
height: 530px;
}
</style>
</head>
<body>
<div>
{% leaflet_map "mapid" %}
</div>
<form id="aoi-form" method="post" action="" >
{% csrf_token %}
<select id="aoi-choice" name="modelarea_sel_name" >
{% for aoi in modelarea.aois.field.choices %}
<option>{{aoi|title}}</option>
{% endfor %}
</select>
</form>
<script type="text/javascript">
var mymap;
window.addEventListener("map:init", function (event) {
mymap = event.detail.map;
}, false);
$(document).ready(function() {
function displayModelArea(map, data) {
L.marker([26.92, -81.5]).addTo(mymap);
L.geoJSON(data).addTo(mymap);
}
$('#aoi-choice').on('change', function(e) {
var selecteditem = $(this).find("option:selected").val();
$.ajax({
url: $("#aoi-form").attr("action"),
type: $("#aoi-form").attr("method"),
data: { "aoi-choice": selecteditem },
success: function(response) {
displayModelArea(mymap, response)
},
error : function(xhr,errmsg,err) {}
}); // end of ajax
}); // end of on change
}); // end of document
</script>
</body>
</html>
views.py
def rsmgui_home(request):
template_name = 'rsmgui_nav.html'
if request.POST.get("aoi-choice"):
polydata = serialize('geojson', ModelArea.objects.filter(name=request.POST.get("aoi-choice")))
return HttpResponse(polydata, content_type='json')

How to update the new order of the sortable data?

I use this light sortable plugin to sort my data by drag and drop
http://farhadi.ir/projects/html5sortable/
Now, how to update my sort order
$('.sortable').sortable().bind('sortupdate', function() {
//Triggered when the user stopped sorting and the DOM position has changed.
});
views.py
def filter_order(request):
if request.method == 'POST':
order = request.POST.getlist('filter[]')
count = 0
for id in order:
count += 1
filter = FilterModel().objects.get(pk=id)
filter.sort_order = count
filter.save()
return HttpResponse('Successfully updating rules order.')
else:
return HttpResponse("Error updating rules order.")
urls.py
urlpatterns = patterns('transactions.views',
............
url(r'^filter-order/$', 'filter_order',
name='filter_order'),
)
rules.html
<ul class="sortable" id="filter-items">
{% for filter in filters %}
<li id="{{ filter.id }}">{{filter.rules}}</li>
{% endfor %}
</ul>
<script>
$('.sortable').sortable().bind('sortupdate', function() {
datas = new Array();
var i = 0;
$('#filter-items li').each(function() {
datas[i] = $(this).attr('id');
i++;
});
$.ajax({
type: "POST",
data: {
'filter[]': datas,
'csrfmiddlewaretoken': '{{csrf_token}}'
},
url:"/transactions/filter-order/",
contentType: "application/json;charset=utf-8",
dataType: "json",
success: function(data) {
notify('', 'Successfully updating rules order.', { autoClose: true, delay: 1000 });
},
error: function(ts) {
notify('', ts.responseText, { autoClose: true, delay: 1000 });
}
});
});
</script>