I am getting really confused on what to do here. I have a python script that does some cool things and then should display those cool things via some charts.
The way I've seen it's done is via Flask and ChartJS, but every website that I've seen uses it to display static content, which, of course, is not what I need.
I've tried doing this with this template:
<!doctype html>
<html>
<head>
<title>Line Chart</title>
<script src="/static/chartjs/Chart.min.js"></script>
<script src="/static/chartjs/utils.js"></script>
<style>
canvas{
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
</style>
</head>
<body>
<div style="width:75%;">
<canvas id="canvas"></canvas>
</div>
<br>
<br>
<button id="randomizeData">Randomize Data</button>
<button id="addDataset">Add Dataset</button>
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<script>
var MONTHS = [];
var config = {
type: 'line',
data: {
labels: ['1/05/19\n00:57', '1/05/19\n00:58', '1/05/19\n00:59', '1/05/19\n01:00', '1/05/19\n01:01', '1/05/19\n01:02', '1/05/19\n01:03'],
{{ dataset }}
},
options: {
responsive: true,
title: {
display: true,
text: 'Chart.js Line Chart'
},
tooltips: {
mode: 'index',
intersect: false,
},
hover: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Month'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Value'
}
}]
}
}
};
window.onload = function() {
var ctx = document.getElementById('canvas').getContext('2d');
window.myLine = new Chart(ctx, config);
};
document.getElementById('randomizeData').addEventListener('click', function() {
config.data.datasets.forEach(function(dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});
window.myLine.update();
});
var colorNames = Object.keys(window.chartColors);
document.getElementById('addDataset').addEventListener('click', function() {
var colorName = colorNames[config.data.datasets.length % colorNames.length];
var newColor = window.chartColors[colorName];
var newDataset = {
label: 'Dataset ' + config.data.datasets.length,
backgroundColor: newColor,
borderColor: newColor,
data: [],
fill: false
};
for (var index = 0; index < config.data.labels.length; ++index) {
newDataset.data.push(randomScalingFactor());
}
config.data.datasets.push(newDataset);
window.myLine.update();
});
document.getElementById('addData').addEventListener('click', function() {
if (config.data.datasets.length > 0) {
var month = MONTHS[1];
config.data.labels.push(month);
config.data.datasets.forEach(function(dataset) {
dataset.data.push(randomScalingFactor());
});
window.myLine.update();
}
});
document.getElementById('removeDataset').addEventListener('click', function() {
config.data.datasets.splice(0, 1);
window.myLine.update();
});
document.getElementById('removeData').addEventListener('click', function() {
config.data.labels.splice(-1, 1); // remove the label first
config.data.datasets.forEach(function(dataset) {
dataset.data.pop();
});
window.myLine.update();
});
</script>
</body>
</html>
This contains a lot of unused stuff (it was downloaded directly from the chart.js samples) and slightly altered by me (see {{ dataset }})
And this is the python code I use to generate the dataset part:
def generate_dataset(self):
data = 'datasets: ['
for post_list_index, post in enumerate(self.post_list):
data = data + "{label: '" + post + "', backgroundColor: window.chartColors.red, borderColor: window.chartColors.red, data: ["
for index, history in enumerate(self.submissions_score_history):
score = history[post_list_index]
if index < len(self.submissions_score_history) - 1:
data = data + str(score) + ','
else:
data = data + str(score)
data = data + '],fill: false,}'
if post_list_index < len(self.post_list) - 1:
data = data + ','
else:
pass
data = data + ']'
return data
Now, unfortunately, this doesnt render anything. I'm pretty sure that this is due to the label: 'label'; being escaped to 'label'.
I've searched about this issue and apparently this javascript injection thing I'm attempting to do has been removed because it represents a security vulnerability.
This project is quite literally just a dashboard for another small python project that will be used by me only, so I was trying to build something quickly and easily, but this is turning out to be quite troublesome :/
Related
I am trying to get row information from my datatable in flask. The array shows data on the JQuery side, but is empty when accessed in flask. How can I pass my array so I can access it with python?
Flask
#api.route('/apply_billing')
def apply_billing():
bill_month = request.args.get('bill_month', 0)
markup = request.args.get('markup', 0, type=float)
billed = request.args.get('billed', 0, type=float)
comment = request.args.get('comment', 0, type=str)
tickets = request.args.getlist('tickets')
print(tickets)
return jsonify(result=str(bill_month) + str(markup))
JQuery
<script type=text/javascript>
$(function() {
$('#submit').bind('click', function() {
var ticket_array = [];
$('#billinglist').DataTable().rows('.selected').every(function(rowIdx){
ticket_array.push($('#billinglist').DataTable().row(rowIdx).data())
});
var data = {
"bill_month": $('input[name="month"]').val(),
"markup": $('input[name="markup"]').val(),
"billed": $('input[name="billed"]').val(),
"comment": $('input[name="comment"]').val(),
"tickets": ticket_array
}
console.log(data)
$.ajax({
url: "{{ url_for('api.apply_billing') }}",
data: data,
success: function(response) {
alert(response.result);
$('#billinglist').DataTable().ajax.reload();
}
});
return false;
});
});
</script>
I was able to solve this issue using martijn-pieters's answer at https://stackoverflow.com/users/100297/martijn-pieters
Flask:
#api.route('/apply_billing', methods=["POST"])
def apply_billing():
data = request.get_json()
print(data["tickets"])
return jsonify("test")
JQuery
<script type=text/javascript>
$(function() {
$('#submit').bind('click', function() {
var ticket_array = [];
$('#billinglist').DataTable().rows('.selected').every(function(rowIdx){
ticket_array.push($('#billinglist').DataTable().row(rowIdx).data())
});
var data = {
bill_month: $('input[name="month"]').val(),
markup: $('input[name="markup"]').val(),
billed: $('input[name="billed"]').val(),
comment: $('input[name="comment"]').val(),
tickets: ticket_array
}
console.log(data)
$.ajax({
url: "{{ url_for('api.apply_billing') }}",
type: "POST",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
success: function(response) {
alert(response.result);
$('#billinglist').DataTable().ajax.reload();
},
error: function(){
alert("No Bueno!!")
}
});
return false;
});
});
</script>
I've implemented a Website with a geolocation function and Button.
This Webpage gets displayed fine in a QwebEngineView (OSM Map too).
The Webpage is loaded
def __init__(self, parent=None):
super(MainGUI, self).__init__(parent)
self.ui = uic.loadUi("GPS.ui", self)
self.ui.w3View.load(QtCore.QUrl('file:///Map.html'))
All native OpenLayers Buttons (Zoom IN and OUT) are working fine.
I created a new custom button to display my geolocation on the OpenLayers Map.
This is the function im using to geolocate me:
var handleGeolocation = function() {
var coordinates;
var geolocation = new ol.Geolocation({
projection: view.getProjection(),
tracking: true
});
// handle geolocation error.
geolocation.on('error', function (error) {
var info = document.getElementById('info');
info.innerHTML = error.message;
info.style.display = '';
});
var accuracyFeature = new ol.Feature();
geolocation.on('change:accuracyGeometry', function () {
accuracyFeature.setGeometry(geolocation.getAccuracyGeometry());
});
var positionFeature = new ol.Feature();
positionFeature.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#3399CC'
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
})
})
}));
geolocation.once('change:position', function () {
coordinates = geolocation.getPosition();
positionFeature.setGeometry(coordinates ?
new ol.geom.Point(coordinates) : null);
map.getView().setCenter(coordinates);
map.getView().setZoom(17);
});
new ol.layer.Vector({
map: map,
source: new ol.source.Vector({
features: [accuracyFeature, positionFeature]
})
});
}
I created the Button with the help from here.
the whole Webpage looks like this:
<!DOCTYPE html>
<html>
<head>
<title>Accessible Map</title>
<link rel="stylesheet" href="https://openlayers.org/en/v4.2.0/css/ol.css" type="text/css">
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
<script src="https://openlayers.org/en/v4.2.0/build/ol-debug.js"></script>
<style>
a.skiplink {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
padding: 0;
border: 0;
height: 1px;
width: 1px;
overflow: hidden;
}
a.skiplink:focus {
clip: auto;
height: auto;
width: auto;
background-color: #fff;
padding: 0.3em;
}
#map:focus {
outline: #4A74A8 solid 0.15em;
}
map{
max-width: 760 px;
max-height: 50 px;
}
.geoButton {
top: 80px;
left: .5em;
}
</style>
</head>
<body>
<div id="map" class="map" tabindex="0"></div>
<script>
window.app = {};
var app = window.app;
view = new ol.View({
center: [0, 0],
zoom: 2
});
app.getLocation = function(opt_options) {
var options = opt_options || {};
var geoButton = document.createElement('button');
geoButton.innerHTML = 'L';
var handleGeolocation = function() {
/* var isMobile = {
Android: function () {
return navigator.userAgent.match(/Android/i);
},
BlackBerry: function () {
return navigator.userAgent.match(/BlackBerry/i);
},
iOS: function () {
return navigator.userAgent.match(/iPhone|iPod|iPad/i);
},
Opera: function () {
return navigator.userAgent.match(/Opera Mini/i);
},
Windows: function () {
return navigator.userAgent.match(/IEMobile/i);
},
any: function () {
return ((isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()));
}
};
if (isMobile.any()){
geolocation.setTrackingOptions(enableHighAccuracy );
} */
var coordinates;
var geolocation = new ol.Geolocation({
projection: view.getProjection(),
tracking: true
});
// handle geolocation error.
geolocation.on('error', function (error) {
var info = document.getElementById('info');
info.innerHTML = error.message;
info.style.display = '';
});
var accuracyFeature = new ol.Feature();
geolocation.on('change:accuracyGeometry', function () {
accuracyFeature.setGeometry(geolocation.getAccuracyGeometry());
});
var positionFeature = new ol.Feature();
positionFeature.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#3399CC'
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
})
})
}));
geolocation.once('change:position', function () {
coordinates = geolocation.getPosition();
positionFeature.setGeometry(coordinates ?
new ol.geom.Point(coordinates) : null);
map.getView().setCenter(coordinates);
map.getView().setZoom(17);
});
new ol.layer.Vector({
map: map,
source: new ol.source.Vector({
features: [accuracyFeature, positionFeature]
})
});
}
geoButton.addEventListener('click', handleGeolocation, false);
geoButton.addEventListener('touchstart', handleGeolocation, false);
var element = document.createElement('div');
element.className = 'ol-unselectable ol-control geoButton';
element.appendChild(geoButton);
ol.control.Control.call(this, {
element: element,
target: options.target
});
};
ol.inherits(app.getLocation, ol.control.Control);
//Standard Initialisierung
// Geolocation function
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}).extend([new app.getLocation()]),
view: view
});
//Display Input Datastream
</script>
</body>
</html>
Inside a normal browser it's working fine but not inside my PyQt app.
After clicking the custom Button nothing happens.
What am i doing wrong or is not possible?
The main problem is to enable the permissions, if you run in a browser like Firefox, Chrome, etc. will show you a popup asking you to accept or not the GeoLocation permission.
In this case it must be enabled by code, for this the QWebEnginePage emits a signal every time authorization is required, it does this through the signal featurePermissionRequested, we connect it to some slot and we enable the permission with the function setFeaturePermission().
self.ui.w3View..page().featurePermissionRequested.connect(self.onFeaturePermissionRequested)
def onFeaturePermissionRequested(self, securityOrigin, feature):
self.sender().setFeaturePermission(securityOrigin,
QWebEnginePage.Geolocation,
QWebEnginePage.PermissionGrantedByUser)
Note: On Linux I still experience problems with the Geoclue provider, the error message reads as follows:
Failed to set Geoclue positioning requirements. Geoclue error:
org.qtproject.QtDBus.Error.InvalidObjectPath
Adding the following lines solved my Problems:
self.ui.w3View.page().featurePermissionRequested.connect(self.permissionRequested)
def permissionRequested(self, frame, feature):
self.ui.w3View.page().setFeaturePermission(frame, feature, QtWebEngineWidgets.QWebEnginePage.PermissionGrantedByUser)
Found it in this post and editing it to PyQt 5.9.
I've developed a script for work that measures the distance traveled in a route by a vehicle, what's the longest distance traveled, and whether the route was from point A to B, a loop, or the vehicle was simply in idle.
I'm using GoogleMaps API to determine all of this, and search through the XML data to determine the current location of the vehicle. There's a 'Vicinity' tag that tells what the current city is at that long/lat coordinate.
I don't see anything describing the current highway exit or mile marker, does anyone know how to get this information through Google?
I apologize if this was asked before. I found a similar post but it was from 7 years ago and stated that GoogleMaps currently did not offer that sort of service.
I think it works pretty well.
It makes double markers sometimes (like two merge markers a few yards apart).
There is some extra code that's probably not used, and some things commented out.
I copy/pasted most of this from another project I was working on.
<style>
body {
width: 100%;
}
#right-panel, #map {
height: 400px;
width: 50%;
float: left;
overflow: auto;
}
hr {
clear: both;
}
</style>
<input id="from" placeholder="from" value="Tulsa"/>
<input id="to" placeholder="to" value="Chicago"/>
<select onchange="routeType(this)">
<option value="DRIVING">DRIVING</option>
<option value="WALKING">WALKING</option>
<option value="BICYCLING">BICYCLING</option>
<option value="TRANSIT">TRANSIT</option>
</select>
<input value="plot" onclick="calculateRoute()" type="button"/>
<hr/>
<div id="map"></div>
<div id="right-panel"></div>
<hr/>
<input onclick="calculateRoute()" value="GO" type="button"/>
<div id="maplog"></div>
<script type="text/javascript" src="https://maps.google.com/maps/api/js?libraries=geometry"></script>
<script>
var map;
var here = {};
var dest = {};
var directionsService;
var directionsDisplay;
var travelMode = 'DRIVING'; //'WALKING';
var sourcePosition;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 35, lng: -100},
zoom: 5,
mapTypeId: 'terrain'
});
var marker = new google.maps.Marker({
position: {lat: 50.8735506, lng: 4.3238525},
map: map,
title: 'Fermenthings winkel'
});
dest.marker = marker;
directionsService = new google.maps.DirectionsService;
directionsDisplay = new google.maps.DirectionsRenderer({
draggable: true,
suppressMarkers: true,
panel: document.getElementById('right-panel'),
map: map
});
}
function trackLocation() {
getLocation();
}
google.maps.event.addDomListener(window, 'load', initMap);
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
//x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
// x.innerHTML = "Latitude: " + position.coords.latitude +
// "<br>Longitude: " + position.coords.longitude;
if(map) {
sourcePosition = {lat: position.coords.latitude, lng: position.coords.longitude};
//map.setCenter(sourcePosition);
if(here.marker) {
here.marker.setMap(null);
}
here.marker = new google.maps.Marker({
position: sourcePosition,
draggable: true,
icon: {url: 'thumbnails/here.png'},
map: map
});
var bounds = new google.maps.LatLngBounds();
bounds.extend(sourcePosition);
bounds.extend(dest.marker.getPosition());
map.fitBounds(bounds);
google.maps.event.addListener(here.marker, 'position_changed', function() {
sourcePosition = {lat: this.getPosition().lat(), lng: this.getPosition().lng()};
});
google.maps.event.addListener(here.marker, 'dragend', function() {
sourcePosition = {lat: this.getPosition().lat(), lng: this.getPosition().lng()};
calculateRoute();
});
//return;
}
// route
calculateRoute();
}
function createMarker(lat, lng, title) {
return new google.maps.Marker({
position: {lat: lat, lng: lng},
map: map,
title: title
});
}
function displayRoute(origin, destination, service, display, waypoints, callback) {
if(waypoints) {
var options = {
origin: origin,
destination: destination,
waypoints: waypoints,
travelMode: travelMode //'DRIVING',
};
}
else {
var options = {
origin: origin,
destination: destination,
travelMode: travelMode //'DRIVING',
};
}
service.route(options, function(response, status) {
if (status === 'OK') {
//
//createMarkers(response, callback);
display.setDirections(response);
if(callback) {
callback(response);
}
} else {
// alert('Could not display directions due to: ' + status);
}
});
}
function routeType(elm) {
travelMode = elm.value;
if(sourcePosition) {
calculateRoute();
}
}
function calculateRoute() {
var from = document.getElementById('from').value;
var to = document.getElementById('to').value;
displayRoute(
from,
to,
directionsService,
directionsDisplay,
null, //waypoints,
function(response) {
// loop steps
var steps = response.routes[0].legs[0].steps;
for(var i = 0; i<steps.length; i++) {
var step = response.routes[0].legs[0].steps[i];
// search for keywords in the instructions
if( (step.instructions.indexOf('Take') > -1 || step.instructions.indexOf('take') > -1) && step.instructions.indexOf('exit') > -1) {
//here we enter a highway
document.getElementById('maplog').innerHTML += 'exit: ' + step.start_location.lat() +','+ step.start_location.lng() +'<br/>';
createMarker(step.start_location.lat(), step.start_location.lng(), 'exit');
}
if( (step.instructions.indexOf('Merge') > -1 || step.instructions.indexOf('merge') > -1) && step.instructions.indexOf('onto') > -1) {
//here we exit a highway
document.getElementById('maplog').innerHTML += 'merge: ' + step.start_location.lat() +','+ step.start_location.lng() +'<br/>';
createMarker(step.start_location.lat(), step.start_location.lng(), 'merge');
}
}
//document.getElementById('maplog').innerHTML = JSON.stringify(response);
}
);
}
</script>
my file temp.py read data from my sensor connected to my rasbperry . my views sends it to html file . I think my call in index.html is not correct . Because when I run my Django project it returns a blank page
views.py
from django.shortcuts import render
from temp import get_temp
import json
def index(request):
return render(request, 'aps/index.html', {'t' : get_temp()})
temp.py
def get_temp() :
tfile = open("/sys/bus/w1/devices/28-0000075292ed/w1_slave")
text = tfile.read()
tfile.close()
secondline = text.split("\n")[1]
temperaturedata = secondline.split(" ")[9]
temperature = float(temperaturedata[2:])
temperature = temperature / 1000
return temperature
index.html
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title> Temperature Sensor</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<style type="text/css">
${demo.css}
</style>
<script type="text/javascript">
$(function () {
$(document).ready(function () {
Highcharts.setOptions({
global: {
useUTC: false
}
});
$('#container').highcharts({
chart: {
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = {{ t }} ;
series.addPoint([x, y], true, true);
}, 3000);
}
}
},
title: {
text: 'Live sensor temperature data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'sensor data',
data: (function () {
var data = [],
time = (new Date()).getTime(),
i;
for (i = -19; i <= 0; i += 1) {
data.push({
x: time + i * 1000,
y: {{ t }}
});
}
return data;
}())
}]
});
});
});
</script>
</head>
<body>
<script src="https://code.highcharts.com/highcharts.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>
</body>
</html>
Add your urls.py
What url are you using at the browser?
If possible add what give you back the browser when you press cntrl+u in your blank page.
I am new to D3js and python. I am trying to load data from postgresql and pass the JSON to D3js and draw a line chart. I am receiving error
<anonymous> hello:61
d3.json/<() d3.v2.js:2950
ready() d3.v2.js:2940
d3.xhr/req.onreadystatechange().
Below is my D3 js call.
d3.json ("http://104.131.191.213/books/",function (error,data) {
if (error) return console.error(error);
console.log(data);
data.forEach(function(d) {
d.year = parseDate(d.year.toString);
d.buys = +d.buys;
});
My parsedate function below:
var parseDate = d3.time.format("%y").parse;
JSON:
{
"data": [
{
"buys": 5841,
"year": "1986"
},
{
"buys": 54,
"year": "1954"
},
{
"buys": 176,
"year": "1967"
},
{
"buys": 9389,
"year": "1991"
},
My full code below:
<!DOCTYPE html>
meta charset="utf-8">
<title> flask+D3 hello </title>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<body>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.year); })
.y(function(d) { return y(d.buys); });
var svg=d3.select ("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json ("http://104.131.191.213/books/",function (error,data) {
if (error) return console.error(error);
console.log(data);
data.forEach(function(d) {
d.year = parseDate(d.year.toString);
d.buys = +d.buys;
});
x.domain(d3.extent(data, function(d) { return d.year; }));
y.domain(d3.extent(data, function(d) { return d.buys; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y",6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
</script>
May i know where i am going wrong?
from d3.parseDate Error cannot read property 'forEach' of undefined:
d3.json(url[, callback])
Creates a request for the JSON file at the specified url with the mime
type "application/json". If a callback is specified, the request is
immediately issued with the GET method, and the callback will be
invoked asynchronously when the file is loaded or the request fails;
the callback is invoked with two arguments: the error, if any, and the
parsed JSON. The parsed JSON is undefined if an error occurs. If no
callback is specified, the returned request can be issued using
xhr.get or similar, and handled using xhr.on.
so print the error and double check the url.