Improve chart update performance in GAS - performance

I have a function that is updating every chart of a sheet and my main issue is that the updateChart() function is really slow (about 50 seconds) :
I already made a script that parallelize the function but due to the 20 triggers per script limitation I can only run my thread twice. So I wanted to know if there was anything that can speed up the update of my charts.
function ModifyVAxisChart()
{
var ss=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DCN Dashboard Data");
var rangeMax=ss.getRange("O3:O231").getValues();//Range to modify if you add charts
var rangeMinId=ss.getRange("P3:P232").getValues();//Range to modify if you add charts
var i=0;
var nbChart=39;
//Logger.log("range Max ="+rangeMax + "autre="+rangeMinId);
var Vmin=0;
var Vmax=0;
var id=-1;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DCN Dashboard");
var chart = sheet.getCharts();
for(i=0;i<nbChart;i++)
{
Vmax=rangeMax[i*6];
Vmin=rangeMinId[i*6];
id=rangeMinId[(i*6)+1];
var delta=(Vmax-Vmin)*0.1;
Logger.log("Vmax="+Vmax+"Vmin="+Vmin+"id="+id+"i="+i);
var currChart = chart[id];
if(currChart.getType()!="COLUMN")
{
Vmin-=delta
Vmax=Number(Vmax)+(delta*1.5)//Number() function to avoid Vmax becoming a string for no reason
}
Logger.log("Vmax="+Vmax+"Vmin="+Vmin+"id="+id+"i="+i);
currChart = currChart.modify()
.setOption('vAxes', {0: {textStyle: {fontSize: 10}, titleTextStyle: {fontSize : 8}, viewWindow: {min: Vmin, max:Vmax}}})//adpative vaxis for AREA and COMBO
.build();
sheet.updateChart(currChart);
}
}

As the App script functions aren't asynchronous, they will wait until the request is complete to keep running the code (and thus to run the rest of the requests). What you could do is to make the requests using the Sheets API [1] in either JavaScript (you would need to serve and html) or using the UrlFetchApp class [2], this way you could initiate the requests without needing to wait for the response from the previous request.
I implemented the fetchAll() method [2] with one request to update a chart and worked successfully, here is the code (you need to put the sheetID):
function uploadChart() {
var data = SpreadsheetApp.getActiveSpreadsheet();
var sheet = data.getSheets()[1];
var chart = sheet.getCharts()[0];
var chartId = chart.getChartId();
var token = ScriptApp.getOAuthToken();
var url = "https://sheets.googleapis.com/v4/spreadsheets/SHEET_ID:batchUpdate";
var chartBody = {
"updateChartSpec": {
"chartId": chartId,
"spec": {
"title": "Model Q1 Sales",
"basicChart": {
"chartType": "BAR",
"legendPosition": "RIGHT_LEGEND",
"axis": [
{
"format": {
"bold": true,
"italic": true,
"fontSize": 24
},
"position": "BOTTOM_AXIS",
"title": "Sales"
},
{
"format": {
"bold": true,
"italic": true,
"fontSize": 24
},
"position": "LEFT_AXIS",
"title": "Model Numbers"
}
],
"domains": [
{
"domain": {
"sourceRange": {
"sources": [
{
"startRowIndex": 0,
"endRowIndex": 6,
"startColumnIndex": 0,
"endColumnIndex": 1
}
]
}
}
}
],
"series": [
{
"series": {
"sourceRange": {
"sources": [
{
"startRowIndex": 0,
"endRowIndex": 6,
"startColumnIndex": 1,
"endColumnIndex": 2
}
]
}
},
"targetAxis": "BOTTOM_AXIS"
},
{
"series": {
"sourceRange": {
"sources": [
{
"startRowIndex": 0,
"endRowIndex": 6,
"startColumnIndex": 2,
"endColumnIndex": 3
}
]
}
},
"targetAxis": "BOTTOM_AXIS"
},
{
"series": {
"sourceRange": {
"sources": [
{
"startRowIndex": 0,
"endRowIndex": 6,
"startColumnIndex": 3,
"endColumnIndex": 4
}
]
}
},
"targetAxis": "BOTTOM_AXIS"
}
],
"headerCount": 1
}
}
}
}
var requestBody = {
'requests': [chartBody]
}
var request1 = {
'url': url,
'headers': {
'Authorization': 'Bearer ' + token,
},
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify(requestBody),
'muteHttpExceptions': true
};
var requests = [request1]
var response = UrlFetchApp.fetchAll(requests);
Logger.log(response)
}
I used the update chart json from the "edit a chart" example [1].
To add more requests, you can either add more request jsons in the fetchAll() array parameter or add more update chart jsons in the 'requests' array on requestBody.
[1] https://developers.google.com/sheets/api/samples/charts
[2] https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchAll(Object)

Related

Implement AJAX into leaflet

I am trying to get data from json with Leaflet.
I am using Leaflet Data visualization framework.
I want to use ajax, because in future I want to generate random numbers in my data properties (something like Math.random() in time intervals)
What I have already tried
I downloaded Leaflet-ajax plugin, included it in header.
When i use something like that:
new L.geoJSON.ajax
I can't see any of my markers on map. I do not get any errors in this case.
Data, stored in data.js (this file is included in header):
var geojsonred = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"name": "1",
"value": 20 //I want to replace this with Math.random()
},
"geometry": {
"type": "Point",
"coordinates": [315, -360]
}
},
{
"type": "Feature",
"properties": {
"name": "2",
"value": 50 //I want to replace this with Math.random()
},
"geometry": {
"type": "Point",
"coordinates": [360, -360]
}
}
]
}
This is how my script looks like:
var minHue = 120;
var maxHue = 0;
//var marker = new L.RadialMeterMarker(new L.LatLng(-360, 320), meterMarkerOptions);
var marker = new L.geoJSON(geojsonred, {
pointToLayer: function(feature, latlng) {
return new L.RadialMeterMarker(latlng, {
data: {
'Speed': feature.properties.value
},
chartOptions: {
'Speed': {
displayName: 'Speed',
displayText: function (value) {
return value.toFixed(1);
},
color: 'hsl(240,100%,55%)',
fillColor: 'hsl(240,80%,55%)',
maxValue: 200,
minValue: 0
}
},
displayOptions: {
'Speed': {
color: new L.HSLHueFunction(new L.Point(0,minHue), new L.Point(200,maxHue), {outputSaturation: '100%', outputLuminosity: '25%'}),
fillColor: new L.HSLHueFunction(new L.Point(0,minHue), new L.Point(200,maxHue), {outputSaturation: '100%', outputLuminosity: '50%'})
}
},
fillOpacity: 0.8,
opacity: 1,
weight: 0.5,
radius: 20,
barThickness: 15,
maxDegrees: 360,
rotation: 0,
numSegments: 10
});
},
});
marker.addTo(map);
Do someone have any idea?

Convert amCharts stock chart period selector into a dropdown

I am using amCharts stock chart to show trades within a period of time. I am using php and ajax to get values.
Now I do have period selector as buttons but I need them as a select. When a user selects a value from dropdown, the chart zoom should change accordingly. Is it possible to do that?
Below is my code to get a stock chart. Please help.
<div id="chartdiv" style="width:100%; height:400px;"></div>
<select id="mySelect" onchange="test()">
<option value="5">5
<option value="15">15
<option value="30">30
<option value="60">1H
<option value="D">1D
</select>
<script type="text/javascript">
AmCharts.addInitHandler(function (chart) {
}, ["stock"]);
var chartData = generateChartData();
console.log(chartData);
var chart = AmCharts.makeChart("chartdiv", {
"type": "stock",
"theme": "light",
"autoMarginOffset": 30,
"valueAxesSettings": {
"position": "right",
"inside": false,
"autoMargins": true,
"axisColor": "#000000",
"tickLength": 1
},
"categoryAxesSettings": {
"parseDates": true,
"minPeriod": "ss",
"axisColor": "#000000",
"tickLength": 1
},
"mouseWheelZoomEnabled": true,
"dataSets": [{
"fieldMappings": [{
"fromField": "open",
"toField": "open"
}, {
"fromField": "close",
"toField": "close"
}, {
"fromField": "high",
"toField": "high"
}, {
"fromField": "low",
"toField": "low"
}, {
"fromField": "volume",
"toField": "volume"
}],
"color": "#7f8da9",
"dataProvider": chartData,
"title": '<?php echo $symbol; ?>',
"categoryField": "date"
}
],
"panels": [{
"urlTarget": "_blank",
"showCategoryAxis": true,
"percentHeight": 70,
"valueAxes": [{
"dashLength": 5
}],
"categoryAxis": {
"dashLength": 5
},
"stockGraphs": [{
"id": "g1",
"type": "candlestick",
"proCandlesticks": false,
"balloonText": "Open:<b>[[open]]</b><br>Low:<b>[[low]]</b><br>High:<b>[[high]]</b><br>Close:<b>[[close]]</b><br>",
"openField": "open",
"closeField": "close",
"highField": "high",
"lowField": "low",
"lineAlpha": 1,
"lineColor": "#53b987",
"fillColors": "#53b987",
"fillAlphas": 0.9,
"negativeFillColors": "#eb4d5c",
"negativeLineColor": "#eb4d5c",
"useDataSetColors": false,
"title": "Volume:",
"valueField": "volume"
}],
"stockLegend": {
"valueTextRegular": undefined,
"periodValueTextComparing": "[[percents.value.close]]%"
}
}
],
"chartScrollbarSettings": {
"updateOnReleaseOnly": true,
"autoGridCount": true,
"graph": "g1",
"graphType": "line",
"scrollbarHeight": 30
},
"periodSelector": {
"position": "top",
"inputFieldsEnabled": false,
"periodsText": "",
"dateFormat": "YYYY-MM-DD JJ:NN",
"periods": [{
"period": "hh",
"count": 1,
"label": "5",
"selected": true
}, {
"period": "hh",
"count": 6,
"label": "15"
}, {
"period": "hh",
"count": 4,
"label": "30"
}, {
"period": "hh",
"count": 12,
"label": "1H"
}, {
"period": "dd",
"count": 60,
"label": "1D"
}]
},
"listeners": [
{
"event": "rendered",
"method": function (e) {
if (e.chart.ignoreResize) {
e.chart.ignoreResize = false;
return;
}
// init
var margins = {
"left": 0,
"right": 0
};
// iterate thorugh all of the panels
for (var p = 0; p < chart.panels.length; p++) {
var panel = chart.panels[p];
// iterate through all of the axis
for (var i = 0; i < panel.valueAxes.length; i++) {
var axis = panel.valueAxes[i];
if (axis.inside !== false) {
continue;
}
var axisWidth = axis.getBBox().width + 10;
if (axisWidth > margins[axis.position]) {
margins[axis.position] = axisWidth;
}
}
}
// set margins
if (margins.left || margins.right) {
chart.panelsSettings.marginLeft = margins.left;
chart.panelsSettings.marginRight = margins.right;
e.chart.ignoreResize = true;
chart.invalidateSize();
}
}
},
{
"event": "zoomed",
"method": function (e) {
e.chart.lastZoomed = e;
//console.log(e);
console.log("ignoring zoomed");
}
},
]
});
setInterval(function () {
//Setting the new data to the graph
chart.dataProvider = generateChartData();
chart.validateData();
}, 10000);
function test() {
var resolution = $("#mySelect").val();
//console.log(resolution);
var pp, count;
if (resolution == "D") {
resolution = "1D";
pp = 'dd';
count = 60;
}
else if (resolution == "60") {
resolution = "1H";
pp = 'hh';
count = 12;
}
else if (resolution == "30") {
pp = 'hh';
count = 4;
}
else if (resolution == "15") {
pp = 'hh';
count = 6;
}
else if (resolution == "5") {
pp = 'hh';
count = 1;
}
else {
pp = 'hh';
}
//console.log(pp);
for (var x in chart.periodSelector.periods) {
var period = chart.periodSelector.periods[x];
if (pp == period.period && resolution == period.count) {
period.selected = true;
}
else {
period.selected = false;
}
}
// console.log(period.period);
chart.periodSelector.setDefaultPeriod();
}
</script>

Sorting dataTables for date columns

I have a datatable but sorting on its date column treats the data as a text instead of a date. I'm trying to make it sort as a date instead but stuck on it.
I tried adding datetime sorting plugin but it didnt work, or I couldnt make it work.
Here's the complete script of the page
$(document).ready(function () {
$('.datetimeclass').datepicker({
format: "dd/mm/yyyy",
language: "tr"
});
var responsiveHelper_dt_basic = undefined;
var breakpointDefinition = {
laptop: 1366,
tablet: 1024,
phone: 480
};
$('#dt_basic').dataTable({
// Tabletools options:
// https://datatables.net/extensions/tabletools/button_options
"sDom": "<'dt-toolbar'<'col-sm-4 col-xs-4 hidden-xs'T>r>" +
"t" +
"<'dt-toolbar-footer'<'col-sm-6 col-xs-12 hidden-xs'i>>",
"oTableTools": {
"aButtons": [
"copy",
//"csv",
"xls",
{
"sExtends": "print",
"sMessage": "Generated by Derimod <i>(press Esc to close)</i>"
}
],
"sSwfPath": "/Scripts/plugin/datatables/swf/copy_csv_xls_pdf.swf",
"columnDefs": [
{ "type": "datetime", targets: 7 }
]
},
"autoWidth": true,
"preDrawCallback": function () {
// Initialize the responsive datatables helper once.
if (!responsiveHelper_dt_basic) {
responsiveHelper_dt_basic = new ResponsiveDatatablesHelper($('#dt_basic'), breakpointDefinition);
}
},
"rowCallback": function (nRow) {
responsiveHelper_dt_basic.createExpandIcon(nRow);
},
"drawCallback": function (oSettings) {
responsiveHelper_dt_basic.respond();
}
, paging: false
// , "aLengthMenu": [[10, 50, 100, -1], [10, 50, 100, "All"]],
//"iDisplayLength": 10,
//,'sPaginationType': 'full_numbers'
});
});
How can I solve my problem?
EDIT: An example of my date format: 14.6.2017 11:49:47

InvalidPathException: No action config found for the specified url

I am getting below error as Ajax call response.
Below is the response body for Ajax call.
Apache Tomcat/7.0.61 - Error report
HTTP Status 500 - org.apache.struts.chain.commands.InvalidPathException: No action config found for the specified url.type Status reportmessage org.apache.struts.chain.commands.InvalidPathException: No action config found for the specified url.description The server encountered an internal error that prevented it from fulfilling this request.Apache Tomcat/7.0.61
Is it something to do with JRE 7?
Here is the JavaScript code from my JSP file.
I have defined a table in my JSP file which is referenced here.
function showLogHistory() {
var questionId = document.getElementById('txtQuestionId').innerHTML;
var url = "myAction.do?dispatchMethodName=showmyAudit&questionId="+questionId;
if ( $.fn.DataTable.isDataTable('#tblmyAudit') ) {
$('#tblmyAudit').DataTable().destroy();
}
$('#tblmyAudit').DataTable( {
"initComplete": function(settings, json) {
$("#tblmyAudit tbody tr.data-in-table").each(function () {
var i=0;
$(this).find('td').each(function (index) {
var currentCell = $(this);
var nextCell =
$(this).closest('tr').next('tr').find('td').eq(i).length > 0 ?
$(this).closest('tr').next('tr').find('td').eq(i) : null;
if ( currentCell.text() !== nextCell.text()) {
currentCell.css('backgroundColor', 'yellow');
}
i=i+1;
});
});
},
"ajax": {
"url": url,
"cache": true
},
"columns": [
{ "data": "questionId" },
{ "data": "Category" },
{ "data": "Area" },
{ "data": "question" },
{ "data": "answer" },
{ "data": "updated_by" },
{ "data": "updated_date" }
],
"scrollX": true,
"columnDefs": [ {
"targets": [ '_all' ],
"orderable": false
} ],
"createdRow": function( row, data, dataIndex ) {
$(row).addClass( 'data-in-table' );
}
} );
$('#detmyAudit').modal('show');
}
URL that gets generated for Ajax seems proper.

Using multiple values for a Crossfilter dimension

I am working with JSON like the following:
[
{
"source": "Google",
"date": "2014-02-01",
"spend": 21,
"clicks": 1000
},
{
"source": "Bing",
"date": "2014-02-01",
"spend": 5,
"clicks": 541
},
{
"source": "Google",
"date": "2014-02-02",
"spend": 24,
"clicks": 1029
},
{
"source": "Bing",
"date": "2014-02-02",
"spend": 12,
"clicks": 754
}
]
And want to feed it into Crossfilter to create a line chart with NVD3. I don't know where to start, but I want the line chart to use the following:
X Axis - Date
Y Axis - Clicks
1 line per source
This is what I've been able to build without Crossfilter:
(function () {
var ymdFormat = d3.time.format('%Y-%m-%d');
d3.json('./data.json', function (err, json) {
nv.addGraph(function () {
var chart = nv.models.lineChart()
chart.margin({ left: 100 })
.useInteractiveGuideline(true)
.transitionDuration(350)
.showLegend(true)
.showYAxis(true).showXAxis(true);
chart.xAxis
.axisLabel('Date')
.tickFormat(function (d) {
return d3.time.format('%b %Y')(new Date(d));
});
chart.yAxis
.axisLabel('Clicks')
.tickFormat(d3.format(','));
data = parseData(json);
d3.select('#graph').append('svg')
.datum(data).call(chart);
});
})
function parseData(json) {
var data, result, key;
data = {};
json.forEach(function (elmt) {
if (!(elmt.source in data)) {
data[elmt.source] = { values: [] }
}
data[elmt.source].values.push({ x: ymdFormat.parse(elmt.date), y: elmt.clicks })
});
result = [];
for (key in data) {
if (data.hasOwnProperty(key)) {
result.push({
key: key,
values: data[key].values
});
}
}
console.log(result);
return result;
}
})()
I have this problem. I have a solution for this, not very nice, however.
var filterByMonthSource = filter.dimension(function(d) { return d.date + ':' + d.source });
var clicksGroup = filterByMonth.group().reduceSum(function(d) { return d.clicks });
var clicksData = clicksGroup.all();
clicksData will have [{key: "2014-02-01:Google", value: ...}, ...]
I then have to break clicksData into array of 3 fields.

Resources