How can I parse html table inside a script respecting merged cells - google-apps-script

This header is contained in a js file https://www.portaldefinancas.com/js-tx-ctb/th-cdib.js
document.write(""),document.write('</p></caption><thead><tr><th rowspan="4">Mês de<br>Referência</th><th colspan="7">Taxas - %</th></tr><tr> <th rowspan="3">Mensal</th><th colspan="4">Anualizada</th><th colspan="2">Acumulada</th></tr><tr> <th colspan="2">Ano de<br>252 dias<br> úteis</th><th colspan="2">Ano de<br>365/366 dias<br>corridos</th><th rowspan="2">No ano</th><th rowspan="2">Em <br>12 meses</th></tr><tr><th>Dias</th><th> Taxa</th><th>Dias</th><th> Taxa</th></tr></thead><tbody>');
How can I parse the headers respecting the merged rows and merged columns. The script I use today is
function getHeaders(url) {
var source = UrlFetchApp.fetch(url).getContentText()
source = source.split('document')[2]
var table = '<table><tr><th ' + source.match(/(?<=<th ).*(?=th>)/g) + 'th></tr></table>'
table=table.replace(/ê/g,'ê').replace(/ú/g,'ú').replace(/<br>/g,'\n')
var doc = XmlService.parse(table);
var rows = doc.getDescendants().filter(function(c) {
var element = c.asElement();
return element && element.getName() == "tr";
});
var data = rows.slice(0).map(function(row) {
return row.getChildren("th").map(function(cell) {
return cell.getValue();
});
});
return data;
}
but it doesn't respect merged areas. Thanks for any help !

Since intellectual exercises is my my drug of choice... I can't help it. Here is the possible solution. It works to a degree but it shows little the traits of lofty style of coding:
function main() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = getHeaders();
data = handle_rowspans(data);
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
function getHeaders(url) {
// var source = UrlFetchApp.fetch(url).getContentText()
// source = source.split('document')[2]
var source = `<thead><tr><th rowspan="4">Mês de<br>Referência</th><th colspan="7">Taxas - %</th></tr><tr> <th rowspan="3">Mensal</th><th colspan="4">Anualizada</th><th colspan="2">Acumulada</th></tr><tr> <th colspan="2">Ano de<br>252 dias<br> úteis</th><th colspan="2">Ano de<br>365/366 dias<br>corridos</th><th rowspan="2">No ano</th><th rowspan="2">Em <br>12 meses</th></tr><tr><th>Dias</th><th> Taxa</th><th>Dias</th><th> Taxa</th></tr></thead><tbody>`;
source = handle_colspans(source);
table = '<table><tr><th ' + source.match(/(?<=<th ).*(?=th>)/g) + 'th></tr></table>';
table = table.replace(/ê/g, 'ê').replace(/ú/g, 'ú').replace(/<br>/g, '\n');
var doc = XmlService.parse(table);
var rows = doc.getDescendants().filter(function (c) {
var element = c.asElement();
return element && element.getName() == "tr";
});
var data = rows.slice(0).map(function (row) {
return row.getChildren("th").map(function (cell) {
return cell.getValue();
});
});
return data;
}
function handle_colspans(table) {
return table.split('</tr>').map(r => add_cells_in_row(r)).join('</tr>');
function add_cells_in_row(row) {
var cells = row.split('</th>');
for (var i in cells) {
if (/colspan/.test(cells[i])) {
var colspan = cells[i].replace(/.*colspan="(\d+).*/, '$1');
cells[i] += '{col' + colspan + '}';
cells[i] = [cells[i], ...(new Array(+colspan - 1).fill('<th>'))];
}
if (/rowspan/.test(cells[i])) {
var rowspan = cells[i].replace(/.*rowspan="(\d+).*/, '$1');
cells[i] += '{row' + rowspan + '}';
}
}
return cells.flat().join('</th>')
}
}
function handle_rowspans(array) {
for (var row in array) {
for (var col in array[row]) {
if (/\{row/.test(array[row][col])) {
var rowspan = array[row][col].replace(/.*\{row(\d+).*/s, '$1');
for (var r = 1; r < rowspan; r++) array[+row + r].splice(col, 0, '')
}
}
}
return array;
}
It will get you the table like this:
Whrere {row#} and {col#} means how many cells or rows to the left or to the bottom you need to join to the current cell to recreate the original design. It could be the next dose of the intellectual exercises. :)

A solution, example with url = https://www.portaldefinancas.com/js-tx-ctb/th-cdib.js
function getHeaders(url) {
var source = UrlFetchApp.fetch(url).getContentText()
source = source.split('document')[2]
var table = '<table><tr><th ' + source.match(/(?<=<th ).*(?=th>)/g) + 'th></tr></table>'
table=table.replace(/ê/g,'ê').replace(/ú/g,'ú').replace(/<br>/g,'\n')
var doc = XmlService.parse(table);
var rows = doc.getDescendants().filter(function(c) {
var element = c.asElement();
return element && element.getName() == "tr";
});
var n=0
var data=[]
rows.slice(0).map(function(row) {data[n++]=[]})
n=0
rows.slice(0).map(function(row) {
row.getChildren("th").map(function(cell) {
try{nbcols = cell.getAttribute('colspan').getValue()}catch(e){nbcols = 1}
try{nbrows = cell.getAttribute('rowspan').getValue()}catch(e){nbrows = 1}
var value = cell.getValue()
r=0
var free=0
while(r<nbrows*1){
c=0
while(c<nbcols*1){
while(data[n+r][free]!=null){free++}
data[n+r][free]=(value)
value=''
c++
}
r++
}
});
n++
});
return (data);
}

Related

My website becomes unresponsive when dealing 1000 rows excel file

I am uploading data from an excel file into my website using input html button.
and then convert the data into json and then I map it with local external metadata.
Finally view it using the id.
My website becomes unresponsive & sometimes takes a lot of time processing. Please help
function ExportToTable() {
var regex = /^([a-zA-Z0-9\s_\\.\-:()])+(.xlsx|.xls)$/;
/*Checks whether the file is a valid excel file*/
if (regex.test($("#excelfile").val().toLowerCase())) {
var xlsxflag = false; /*Flag for checking whether excel is .xls format or .xlsx format*/
if ($("#excelfile").val().toLowerCase().indexOf(".xlsx") > 0) {
xlsxflag = true;
}
/*Checks whether the browser supports HTML5*/
if (typeof (FileReader) != "undefined") {
var reader = new FileReader();
reader.onload = function (e) {
var data = e.target.result;
/*Converts the excel data in to object*/
if (xlsxflag) {
var workbook = XLSX.read(data, { type: 'binary' });
}
else {
var workbook = XLS.read(data, { type: 'binary' });
}
/*Gets all the sheetnames of excel in to a variable*/
var sheet_name_list = workbook.SheetNames;
console.log(sheet_name_list);
var cnt = 0; /*This is used for restricting the script to consider only first
sheet of excel*/
sheet_name_list.forEach(function (y) { /*Iterate through all sheets*/
/*Convert the cell value to Json*/
if (xlsxflag) {
var exceljson = XLSX.utils.sheet_to_json(workbook.Sheets[y]);
}
else {
var exceljson = XLS.utils.sheet_to_row_object_array(workbook.Sheets[y]);
}
//Download & View Subscriptions
if (exceljson.length > 0 && cnt == 1) {
metadata = [];
fetch("metadata.json")
.then(response => response.json())
.then(json => {
metadata = json;
console.log(metadata);
user_metadata1 = [], obj_m_processed = [];
for (var i in exceljson) {
var obj = { email: exceljson[i].email, name: exceljson[i].team_alias, id: exceljson[i].autodesk_id };
for (var j in metadata) {
if (exceljson[i].email == metadata[j].email) {
obj.GEO = metadata[j].GEO;
obj.COUNTRY = metadata[j].COUNTRY;
obj.CITY = metadata[j].CITY;
obj.PROJECT = metadata[j].PROJECT;
obj.DEPARTMENT = metadata[j].DEPARTMENT;
obj.CC=metadata[j].CC;
obj_m_processed[metadata[j].email] = true;
}
}
obj.GEO = obj.GEO || '-';
obj.COUNTRY = obj.COUNTRY || '-';
obj.CITY = obj.CITY || '-';
obj.PROJECT = obj.PROJECT || '-';
obj.DEPARTMENT = obj.DEPARTMENT || '-';
obj.CC = obj.CC || '-';
user_metadata1.push(obj);
}
for (var j in metadata) {
if (typeof obj_m_processed[metadata[j].email] == 'undefined') {
user_metadata1.push({ email: metadata[j].email, name: metadata[j].name, id: metadata[j].autodesk_id,
GEO: metadata[j].GEO,
COUNTRY : metadata[j].COUNTRY,
CITY : metadata[j].CITY,
PROJECT : metadata[j].PROJECT,
DEPARTMENT : metadata[j].DEPARTMENT,
CC:metadata[j].CC
});
}
}
document.getElementById("headings4").innerHTML = "MetaData Mapping";
BindTable(user_metadata1, '#user_metadata1
cnt++;
});
$('#exceltable').show();
}
if (xlsxflag) {/*If excel file is .xlsx extension than creates a Array Buffer from excel*/
reader.readAsArrayBuffer($("#excelfile")[0].files[0]);
}
else {
reader.readAsBinaryString($("#excelfile")[0].files[0]);
}
}
else {
alert("Sorry! Your browser does not support HTML5!");
}
}
else {
alert("Please upload a valid Excel file!");
}
}
Here is how the json is bind after mapping metadata
function BindTable(jsondata, tableid) {/*Function used to convert the JSON array to Html Table*/
var columns = BindTableHeader(jsondata, tableid); /*Gets all the column headings of Excel*/
for (var i = 0; i < jsondata.length; i++) {
var row$ = $('<tr/>');
for (var colIndex = 0; colIndex < columns.length; colIndex++) {
var cellValue = jsondata[i][columns[colIndex]];
if (cellValue == null)
cellValue = "";
row$.append($('<td/>').html(cellValue));
}
$(tableid).append(row$);
}
}
function BindTableHeader(jsondata, tableid) {/*Function used to get all column names from JSON and bind the html table header*/
var columnSet = [];
var headerTr$ = $('<tr/>');
for (var i = 0; i < jsondata.length; i++) {
var rowHash = jsondata[i];
for (var key in rowHash) {
if (rowHash.hasOwnProperty(key)) {
if ($.inArray(key, columnSet) == -1) {/*Adding each unique column names to a variable array*/
columnSet.push(key);
headerTr$.append($('<th/>').html(key));
}
}
}
}
$(tableid).append(headerTr$);
return columnSet;
}

Get Google sheets to updatable JSON script to only need Header row

I have inherited a Google sheet script that generates a Live updatable JSON link from sheets chosen from an HTML form in a sidebar
At the start of a project, the only data that might be present is the Headers on both the Element and Connection sheets (and they are always present) and as the project matures rows of data are added to one or both sheets
My issue is that the script to generate the json requires that at least two rows of data exist (on both sheets) the Header row and a data row
When you run the script you get a pop up with a link then you click that link and a new browser tab opens with the JSON data. It is at this step I get the error
Line 161 in the gs modual is properties.setProperty("settings", newProperties); from
function setExportProperties(newProperties){
var properties = PropertiesService.getDocumentProperties();
var prop = properties.getProperty("settings");
properties.setProperty("settings", newProperties);
}
But this is set on line 292 from the HTML module
function setProperties(){
return google.script.run.setExportProperties(generateProperties());
}
I have tried for hours to figure out how to modify the script so only the header row is necessary but I have not achieved this
There is a lot of code here and it seems to me too much to post it but it is well documented in the code and I think someone who knows what they are doing can figure out pretty easily what needs to be done
From the added menu click Open Sidebar to get to the form. I have set it up in advance
Thank you in advance for your help with this
gs code
//===================================================================================================================
//ExportJson Code
//===================================================================================================================
//Displays an HTML-service dialog in Google Sheets that contains client-side JavaScript code for the Google Picker API.
function showForm() {
var html = HtmlService.createHtmlOutputFromFile('Sidebar.html')
.setTitle('Export Sheet As JSON Data')
.setWidth(300)
.setHeight(480)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showSidebar(html);
}
//function showData(elements,connections,headerrow,datarow,prefix,separator){
function showData(sheets,settings){
//Sheets Array
var elements=sheets[0];
var connections=sheets[1];
//Settings Array
var emptycells=settings[0];
var headerrow=settings[1];
var datarow=settings[2];
var prefix=settings[3];
var separator=settings[4];
var keepignore=settings[5];
var cellarrays=settings[6];
var separator2=settings[7];
var columnignore=settings[8];
var colprefix=settings[9];
var sheetid=SpreadsheetApp.getActiveSpreadsheet().getId();
if(elements=='' && connections==''){
Browser.msgBox('Select any one or both sheets and run again!');
return;
}
if(headerrow=='' || headerrow<=0){
Browser.msgBox('Please enter row number of your data header, it should be an integer value greater than 0');
return;
}
if(datarow=='' || datarow<=0){
Browser.msgBox('Please enter row number where data starts, it should be an integer value greater than 0');
return;
}
if(keepignore==true){
if(prefix=='' && separator!=''){
Browser.msgBox('Please Column Prefix And Array Separator');
return;
}
}
if(columnignore==true){
if(colprefix=='' || colprefix==false){
Browser.msgBox('Please Enter Column Prefix');
return;
}
}
if(cellarrays==true){
if(separator2=='' || separator2==false){
Browser.msgBox('Please Enter Array Separator');
return;
}
}
if(elements=='' && connections==''){
Browser.msgBox('Select sheet(s)!');
return;
}
var webappurl='https://script.google.com/macros/s/AKfycbwn.../exec';
webappurl=webappurl + '?id=' + sheetid;
if(elements!='')
webappurl=webappurl + '&sheet1=' + elements;
else
webappurl=webappurl + '&sheet1=' + '';
if(connections!='')
webappurl=webappurl + '&sheet2=' + connections;
else
webappurl=webappurl + '&sheet2=' + '';
webappurl=webappurl + '&header=' + headerrow;
webappurl=webappurl + '&startrow=' + datarow;
if(keepignore==true){
if(prefix=='' || separator==''){
webappurl=webappurl + '&prefix=' + 'false';
webappurl=webappurl + '&separator=' + 'false';
}
else{
webappurl=webappurl + '&prefix=' + prefix;
webappurl=webappurl + '&separator=' + separator;
}
}
else{
webappurl=webappurl + '&prefix=' + 'false';
webappurl=webappurl + '&separator=' + 'false';
}
if(emptycells==true)
webappurl=webappurl + '&emptycells=' + 'yes';
else
webappurl=webappurl + '&emptycells=' + 'no';
if(cellarrays==true){
webappurl=webappurl + '&cellarrays=' + 'yes';
webappurl=webappurl + '&separator2=' + separator2;
}
else{
webappurl=webappurl + '&cellarrays=' + 'no';
webappurl=webappurl + '&separator2=' + 'no';
}
if(columnignore==true){
webappurl=webappurl + '&columnignore=' + 'yes';
webappurl=webappurl + '&colprefix=' + colprefix;
}
else{
webappurl=webappurl + '&columnignore=' + 'no';
webappurl=webappurl + '&colprefix=' + 'no';
}
//Workflows.setCurrentMapSource("");
var kumuurl='Workflows.setCurrentMapSource("'+ webappurl +'")';
openUrl(webappurl,kumuurl);
}
//===================================================================================================================
function openUrl( url,kumuurl ){
var html = HtmlService.createHtmlOutput('<html>'
//+'<script>'
//+'window.close = function(){window.setTimeout(function(){google.script.host.close()},9)};'
//+'var a = document.createElement("a"); a.href="'+url+'"; a.target="_blank";'
//+'if(document.createEvent){'
//+' var event=document.createEvent("MouseEvents");'
//+' if(navigator.userAgent.toLowerCase().indexOf("firefox")>-1){window.document.body.append(a)}'
//+' event.initEvent("click",true,true); a.dispatchEvent(event);'
//+'}else{ a.click() }'
//+'close();'
//+'</script>'
// Offer URL as clickable link in case above code fails.
+'<body style="word-break:break-word;font-family:sans-serif;">' + kumuurl + '</body>'
+'<script>google.script.host.setHeight(150);google.script.host.setWidth(600)</script>'
+'</html>')
.setWidth(600).setHeight(1);
SpreadsheetApp.getUi().showModalDialog( html, "Click the Link below to get the LIVE Link" );
}
//===================================================================================================================
function setExportProperties(newProperties){
var properties = PropertiesService.getDocumentProperties();
var prop = properties.getProperty("settings");
properties.setProperty("settings", newProperties);
}
//===================================================================================================================
function getExportProperties(){
var props = PropertiesService.getDocumentProperties();
var prop = props.getProperty("settings");
//Logger.log(prop);
return prop;
}
//===================================================================================================================
function getSheetList(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheets=ss.getSheets();
var sheetnames=[];
sheetnames[0]="|"
for(var i=0;i<sheets.length;i++){
var sh=sheets[i];
sheetnames[i+1]=sh.getName()+"|"+sh.getName();
}
return sheetnames;
}
//===================================================================================================================
//LiveJson Code
//===================================================================================================================
function doGet(request) {
// Get request params.
var sheetKey = request.parameters.id;
var sheetName1 = request.parameters.sheet1;
var sheetName2 = request.parameters.sheet2;
var callback = request.parameters.callback;
var headerRow = request.parameters.header;
var startRow = request.parameters.startrow;
var prefix = request.parameters.prefix;
var separator = request.parameters.separator;
var emptycells = request.parameters.emptycells;
var cellarrays = request.parameters.cellarrays;
var separator2 = request.parameters.separator2;
var columnignore = request.parameters.columnignore;
var colprefix = request.parameters.colprefix;
// Parse the spreadsheet.
var spreadsheet = SpreadsheetApp.openById(sheetKey); //.getActiveSpreadsheet();
if(sheetName1!=''){
var keys = getHeaderRowKeys_(spreadsheet, sheetName1, headerRow, columnignore, colprefix);
var data1 = readData_(spreadsheet, sheetName1, headerRow, keys, startRow,prefix,separator,emptycells,cellarrays,separator2,columnignore, colprefix);
}
if(sheetName2!=''){
var keys = getHeaderRowKeys_(spreadsheet, sheetName2, headerRow, columnignore, colprefix);
var data2 = readData_(spreadsheet, sheetName2,headerRow, keys, startRow,prefix,separator,emptycells,cellarrays,separator2,columnignore, colprefix);
}
// Write and return the response.
if(sheetName1!='' && sheetName2!=''){
var response = JSON.stringify({ elements: data1,connections: data2 },null,'\t');
}
else if(sheetName1!='' && sheetName2==''){
var response = JSON.stringify({ elements: data1 },null,'\t');
}
else if(sheetName1=='' && sheetName2!=''){
var response = JSON.stringify({ connections: data2 },null,'\t');
}
var output = ContentService.createTextOutput();
if(callback == undefined){
// Serve as JSON
output.setContent(response).setMimeType(ContentService.MimeType.JSON);
}
else{
// Serve as JSONP
output.setContent(callback + "(" + response + ")").setMimeType(ContentService.MimeType.JAVASCRIPT);
}
//output.setContent(callback + "(" + response + ")").setMimeType(ContentService.MimeType.JAVASCRIPT);
return output;
}
//===================================================================================================================
function readData_(spreadsheet, sheetName, headerRow, properties, startRowNum, prefix,separator,emptycells,cellarrays,separator2,columnignore, colprefix) {
if (typeof properties == "undefined") {
properties = getHeaderRowKeys_(spreadsheet, sheetName, headerRow, columnignore, colprefix);
}
var rows = getDataRows_(spreadsheet, sheetName,headerRow, startRowNum,columnignore, colprefix);
var data = [];
if(emptycells=='yes'){//Ignore Empty Cells
for (var i = 0; i<rows.length; i++) {
var row = rows[i];
var record = {};
for (var p in properties) {
if(row[p]!=''){
var keys=properties
record=updateRecord(prefix,keys,p,row,record,prefix,separator,cellarrays,separator2)
//properties[p]=properties[p].replace(prefix,"")
}
}
data.push(record);
}
}
else{//Inclue Empty Cells
for (var i = 0; i<rows.length; i++) {
var row = rows[i];
var record = {};
for (var p in properties) {
var keys=properties
record=updateRecord(prefix,keys,p,row,record,prefix,separator,cellarrays,separator2)
}
data.push(record);
}
}
return data;
}
//===================================================================================================================
function updateRecord(prefix,properties,p,row,record,prefix,separator,cellarrays,separator2){
if(prefix=='false'){
if(cellarrays=='yes'){//Split Cell Values as arrays if contains comma
if(row[p].toString().indexOf(separator2)>=0){//Convert Cell As Array Only If Contains Comma
record[properties[p]]=row[p].toString().split(separator2);
}
else{
record[properties[p]]=row[p];
}
}
else{
record[properties[p]]=row[p];
}
}
else{//Split Entire Column as array if start with a prefix
var key=properties[p];
if(key.indexOf(prefix)==0){
key=key.replace(prefix,"")
if(row[p].toString().indexOf(separator)>=0)
record[key]=row[p].toString().split(separator);
else if(row[p].toString().indexOf(separator2)>=0)
if(cellarrays='yes')
record[key]=row[p].toString().split(separator2);
else
record[key]=row[p];
else
record[key]=row[p];
}
else{
if(cellarrays=='yes'){//Split Cell Values as arrays if contains separator2
if(row[p].toString().indexOf(separator2)>=0){//Convert Cell As Array Only If Contains separator2
record[key]=row[p].toString().split(separator2);
}
else{
record[key]=row[p];
}
}
else{
record[key]=row[p];
}
}
}
return record;
}
//===================================================================================================================
function getDataRows_(spreadsheet, sheetName, headerRow, startRowNum, columnignore, colprefix) {
var properties = getHeaderRow_2(spreadsheet, sheetName, headerRow, columnignore, colprefix);
if (typeof startRowNum == "undefined") startRowNum = 2;
var sheet = spreadsheet.getSheetByName(sheetName);
var values=sheet.getRange(startRowNum, 1, sheet.getLastRow()-1, sheet.getLastColumn()).getValues();
if(columnignore=='yes'){
var data=[];
for(var i=0;i<values.length;i++){
data[i]=[];
var k=0;
for(var j=0;j<properties.length;j++){
if(properties[j].toString().indexOf(colprefix)!=0){
data[i][k]=values[i][j];
k++;
}
}
}
return data;
}
else
return values;
}
//======================================================================================================================================================================================
function getHeaderRowKeys_(spreadsheet, sheetName, rowNum, columnignore, colprefix) {
if (typeof rowNum == "undefined") rowNum = 1;
var header=getHeaderRow_(spreadsheet, sheetName, rowNum, columnignore, colprefix);
return header;
}
//===================================================================================================================
function getHeaderRow_(spreadsheet, sheetName, rowNum, columnignore, colprefix) {
var sheet = spreadsheet.getSheetByName(sheetName);
var values=sheet.getRange(rowNum, 1, 1, sheet.getLastColumn()).getValues();
if(columnignore=='yes'){
var data=[];
for(var i=0;i<values.length;i++){
data[i]=[];
var k=0;
for(var j=0;j<values[0].length;j++){
if(values[i][j].toString().indexOf(colprefix)!=0){
data[i][k]=values[i][j];
k++;
}
}
}
return data[0];
}
else
return values[0];
}
//===================================================================================================================
function getHeaderRow_2(spreadsheet, sheetName, rowNum, columnignore, colprefix) {
var sheet = spreadsheet.getSheetByName(sheetName);
var values=sheet.getRange(rowNum, 1, 1, sheet.getLastColumn()).getValues();
return values[0];
}
HTML code (some CSS remove so as not to exceed char limit)
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<base target="_top">
</head>
<body>
<body onload="populate()"></body>
<div class="main">
<h2 align="center" style="font-family:Open+Sans;"> Select Sheets</h2>
<div class="section">
<div class="block form-group">
<br>
<center> <label for="elmn">Select Elements Sheet</label>
<select id="elmn" name="elmn" style="width: 180px;" ></select>
<br>
<label for="conn">Select Connections Sheet</label>
<select id="conn" name="conn" style="width: 180px;" ></select></center>
</div>
</div>
<h2 align="center" style="font-family:Open+Sans;">General Data</h2>
<div class="section">
<div id="new-div">
<center>Header Row:<br>
<input type="text" name="Header Row" value="1" id="headerrow">
<br><br>
Start Data Row:<br>
<input type="text" name="Data Row" value="2" id="datarow"> </center>
</div>
</div>
<h2 align="center" style="font-family:Open+Sans;">JSON Settings</h2>
<div class="buttonDiv" >
<!--<div class="newButtonDiv"></div>-->
<button class="action" value="Run" onclick="get_form_values()">View</button>
<input type="reset" value="Reset" onclick="enableTextBox()">
</div>
</div>
<script>
var elements = "";
var connections = "";
var emptycells = "";
var headerrow = "";
var datarow = "";
var prefix = "";
var separator = "";
var keepignore = "";
var cellarrays = "";
var separator2 = "";
var columnignore="";
var colprefix="";
var elementsIndex = "";
var elementsValue = "";
var connectionsIndex = "";
var connectionsValue = "";
function populate(){
var elementlist=document.getElementById('elmn');
var connectlist=document.getElementById('conn');
//document.getElementById("elmn").innerHTML = "";
//document.getElementById("conn").innerHTML = "";
function onSuccess(optionarray) {
for(var option in optionarray){
var pair=optionarray[option].split("|")
var newoption=document.createElement("Option");
newoption.value=pair[0];
newoption.innerHTML=pair[1];
elementlist.options.add(newoption);
var newoption=document.createElement("Option");
newoption.value=pair[0];
newoption.innerHTML=pair[1];
connectlist.options.add(newoption);
}
elementlist.selectedIndex=elementsIndex;
connectlist.selectedIndex=connectionsIndex;
}
//console.log(document.getElementById('elmn').options);
google.script.run.withSuccessHandler(onSuccess).getSheetList();
enableTextBox();
}
function get_form_values(){
//...
var e = document.getElementById('elmn');
elementsValue = e.options[e.selectedIndex].value;
elements = e.options[e.selectedIndex].text;
elementsIndex = e.options[e.selectedIndex].index;
//...
//...
var c = document.getElementById('conn');
connectionsValue = c.options[c.selectedIndex].value;
connections = c.options[c.selectedIndex].text;
connectionsIndex = c.options[c.selectedIndex].index;
//...
emptycells=document.getElementById('emptycells').checked;
headerrow=document.getElementById('headerrow').value;
datarow=document.getElementById('datarow').value;
prefix=document.getElementById('prefix').value;
separator=document.getElementById('separator').value;
keepignore=document.getElementById('keepignore').checked;
cellarrays=document.getElementById('cellarrays').checked;
separator2=document.getElementById('separator2').value;
columnignore=document.getElementById('columnignore').checked;
colprefix=document.getElementById('colprefix').value;
var sheets=[];
sheets[0]=elements;
sheets[1]=connections;
var settings=[];
settings[0]=emptycells;
settings[1]=headerrow;
settings[2]=datarow;
settings[3]=prefix;
settings[4]=separator;
settings[5]=keepignore;
settings[6]=cellarrays;
settings[7]=separator2;
settings[8]=columnignore;
settings[9]=colprefix;
setProperties();
google.script.run.showData(sheets,settings);
}
function setProperties(){
return google.script.run.setExportProperties(generateProperties());
}
function generateProperties(){
var properties = {
//General settings
"emptycells" : emptycells,
"headerrow" : headerrow,
"datarow" : datarow,
"prefix" : prefix,
"separator" : separator,
"keepignore" : keepignore,
"cellarrays" : cellarrays,
"columnignore" : columnignore,
"colprefix" : colprefix,
"separator2" : separator2,
"elements" : elements,
"connections" : connections,
"elementsIndex" : elementsIndex,
"elementsValue": elementsValue,
"connectionsIndex" : connectionsIndex,
"connectionsValue": connectionsValue,
};
return JSON.stringify(properties);
}
//Actually sets the local properties based on stored settings.
function updateProperties(properties){
var settings;
if(properties == null || properties === ""){
settings = {};
}
else{
settings = JSON.parse(properties);
}
//General
//connections = settings["connections"] != null ? settings["connections"] : connections;
elements = settings["elements"] != null ? (settings["elements"] === "" ? elements : settings["elements"]) : elements
elementsIndex = settings["elementsIndex"] != null ? settings["elementsIndex"] : elementsIndex;
elementsValue = settings["elementsValue"] != null ? settings["elementsValue"] : elementsValue;
connections = settings["connections"] != null ? (settings["connections"] === "" ? connections : settings["connections"]) : connections
connectionsIndex = settings["connectionsIndex"] != null ? settings["connectionsIndex"] : connectionsIndex;
connectionsValue = settings["connectionsValue"] != null ? settings["connectionsValue"] : connectionsValue;
emptycells = settings["emptycells"] != null ? settings["emptycells"] : emptycells;
headerrow = settings["headerrow"] != null ? settings["headerrow"] : headerrow;
datarow = settings["datarow"] != null ? settings["datarow"] : datarow;
prefix = settings["prefix"] != null ? settings["prefix"] : prefix;
separator = settings["separator"] != null ? settings["separator"] : separator;
keepignore = settings["keepignore"] != null ? settings["keepignore"] : keepignore;
cellarrays = settings["cellarrays"] != null ? settings["cellarrays"] : cellarrays;
separator2 = settings["separator2"] != null ? settings["separator2"] : separator2;
columnignore = settings["columnignore"] != null ? settings["columnignore"] : columnignore;
colprefix = settings["colprefix"] != null ? settings["colprefix"] : colprefix;
refreshSideBar();
//populate(elementsValue, connectionsValue);
}
//Function to rebuild the sidebar after loading export parameters
function refreshSideBar(){
document.getElementById('emptycells').checked=emptycells;
if(headerrow=="")
document.getElementById('headerrow').value="1";
else
document.getElementById('headerrow').value=headerrow;
if(datarow=="")
document.getElementById('datarow').value="2";
else
document.getElementById('datarow').value=datarow;
if(prefix=="")
document.getElementById('prefix').value="ja_";
else
document.getElementById('prefix').value=prefix;
if(separator=="")
document.getElementById('separator').value="|";
else
document.getElementById('separator').value=separator;
document.getElementById('keepignore').checked=keepignore;
document.getElementById('cellarrays').checked=cellarrays;
document.getElementById('columnignore').checked=columnignore;
if(colprefix=="")
document.getElementById('colprefix').value="ig_";
else
document.getElementById('colprefix').value=colprefix;
if(separator2=="")
document.getElementById('separator2').value="|";
else
document.getElementById('separator2').value=separator2;
EnableTextbox1('keepignore', 'prefix' ,'separator')
EnableTextbox('columnignore', 'colprefix')
EnableTextbox('cellarrays', 'separator2')
}
function getProperties(onSuccess){
return google.script.run.withSuccessHandler(onSuccess).getExportProperties();
}
//enable disable
function EnableTextbox(CheckboxId,TextboxId1){
if(document.getElementById(CheckboxId).checked){
document.getElementById(TextboxId1).style.backgroundColor = "#fff"
document.getElementById(TextboxId1).disabled=false;
}
else{
document.getElementById(TextboxId1).style.backgroundColor = "#5bb800"
document.getElementById(TextboxId1).disabled=true;
}
}
function EnableTextbox1(CheckboxId,TextboxId1, TextboxId2){
if(document.getElementById(CheckboxId).checked){
document.getElementById(TextboxId1).style.backgroundColor = "#fff";
document.getElementById(TextboxId2).style.backgroundColor = "#fff";
document.getElementById(TextboxId1).disabled=false;
document.getElementById(TextboxId2).disabled=false;
}
else{
document.getElementById(TextboxId1).style.backgroundColor = "#5bb800";
document.getElementById(TextboxId2).style.backgroundColor = "#5bb800";
document.getElementById(TextboxId1).disabled=true;
document.getElementById(TextboxId2).disabled=true;
}
}
function enableTextBox(){
document.getElementById("elmn").selectedIndex = 0;
document.getElementById("conn").selectedIndex = 0;
document.getElementById("headerrow").value = "1";
document.getElementById("datarow").value = "2";
document.getElementById("emptycells").checked = false;
//========================================================
document.getElementById("keepignore").checked = false;
document.getElementById("prefix").value = "ja_";
document.getElementById("separator").value = "|";
document.getElementById("columnignore").checked = false;
document.getElementById("colprefix").value = "ig_";
document.getElementById("cellarrays").checked = false;
document.getElementById("separator2").value = "|";
document.getElementById("prefix").style.backgroundColor = "#5bb800";
document.getElementById("separator").style.backgroundColor = "#5bb800";
document.getElementById("colprefix").style.backgroundColor = "#5bb800";
document.getElementById("separator2").style.backgroundColor = "#5bb800";
document.getElementById("prefix").disabled=true;
document.getElementById("separator").disabled=true;
document.getElementById("colprefix").disabled=true;
document.getElementById("separator2").disabled=true;
}
//end
getProperties(updateProperties);
</script>
</body>
</html>
I believe your goal is as follows.
You want to remove the following error. (The following image is from your question.)
When I saw your script and your sample Spreadsheet, I noticed that your sheet of "Connections" has only the header row. In this case, an error occurs at var values=sheet.getRange(startRowNum, 1, sheet.getLastRow()-1, sheet.getLastColumn()).getValues(); in your script. Because sheet.getLastRow()-1 is 0. So, in your situation, how about the following modification?
From:
var values=sheet.getRange(startRowNum, 1, sheet.getLastRow()-1, sheet.getLastColumn()).getValues();
To:
var rows = sheet.getLastRow() - 1;
var values = rows > 0 ? sheet.getRange(startRowNum, 1, rows, sheet.getLastColumn()).getValues() : [];
By this modification, when "Connections" sheet has only the header, [] is used as values.

How do I create a single PDF from multiple instances of a person's name in a Google Sheet?

Every Friday our students submit an electronic log of what they did in an extra-curricular activity using a Google Form. We want one PDF created each time a script is run. The problem is it creates a new PDF each time there is a new record in a sheet. We started to look at using a pivot table before creating the PDF, can't figure that part out. Any help would be much appreciated.
Kind regards,
var CLASSLIST = [];
var MAINLIST = [];
const MAINTAIN_GS = "1tFXqO0oKjm37P09Mi2jfYT-RML-G1dBr03qHPUzsT7w";
const MAINTAIN_SN = "Class & Teacher Email";
const STUDENT_FORM_SN="Form Responses 1";
const TEMPFOLDER_ID = "12Wzl_v_o-aJ_38P9YSshyQPTtGVXmsyH";
const TEMPLATE_DOC_ID = "1D0PBYPunBa63d0eC9b6IrzjAHmWzZVVJJM1zhhUVY3c";
function onOpen() {
loadMainDetails();
// Utilities.sleep(10000)
/*
var ui = SpreadsheetApp.getUi();
ui.createMenu('Send Email')
.addItem(CLASSLIST[0] ,'mainScript1')
.addItem(CLASSLIST[1] ,'mainScript2')
.addItem(CLASSLIST[2] ,'mainScript3')
.addItem(CLASSLIST[3] ,'mainScript4')
.addItem(CLASSLIST[4] ,'mainScript5')
.addItem(CLASSLIST[5] ,'mainScript6')
.addItem(CLASSLIST[6] ,'mainScript7')
.addItem(CLASSLIST[7] ,'mainScript8')
.addItem(CLASSLIST[8] ,'mainScript9')
.addItem(CLASSLIST[9] ,'mainScript10')
.addItem(CLASSLIST[10] ,'mainScript11')
.addItem(CLASSLIST[11] ,'mainScript12')
.addToUi(); */
var ui = SpreadsheetApp.getUi();
ui.createMenu('Send Email')
.addItem(CLASSLIST[0] ,'mainScript7I')
.addItem(CLASSLIST[1] ,'mainScript7P')
.addItem(CLASSLIST[2] ,'mainScript7S')
.addItem(CLASSLIST[3] ,'mainScript7W')
.addItem(CLASSLIST[4] ,'mainScript8I')
.addItem(CLASSLIST[5] ,'mainScript8P')
.addItem(CLASSLIST[6] ,'mainScript8S')
.addItem(CLASSLIST[7] ,'mainScript8W')
.addItem(CLASSLIST[8] ,'mainScript9I')
.addItem(CLASSLIST[9] ,'mainScript9P')
.addItem(CLASSLIST[10] ,'mainScript9S')
.addItem(CLASSLIST[11] ,'mainScript9W')
.addItem(CLASSLIST[12] ,'mainScript10A')
.addItem(CLASSLIST[13] ,'mainScript10B')
.addItem(CLASSLIST[14] ,'mainScript10C')
.addItem(CLASSLIST[15] ,'mainScript10D')
.addItem(CLASSLIST[16] ,'mainScript10E')
.addItem(CLASSLIST[17] ,'mainScript11A')
.addItem(CLASSLIST[18] ,'mainScript11B')
.addItem(CLASSLIST[19] ,'mainScript11C')
.addItem(CLASSLIST[20] ,'mainScript11D')
.addItem(CLASSLIST[21] ,'mainScript11E')
.addItem(CLASSLIST[22] ,'mainScript12A')
.addItem(CLASSLIST[23] ,'mainScript12B')
.addItem(CLASSLIST[24] ,'mainScript12C')
.addItem(CLASSLIST[25] ,'mainScript12D')
.addItem(CLASSLIST[26] ,'mainScript12E')
.addItem(CLASSLIST[27] ,'mainScript13A')
.addItem(CLASSLIST[28] ,'mainScript13B')
.addItem(CLASSLIST[29] ,'mainScript13C')
.addItem(CLASSLIST[30] ,'mainScript13D')
.addItem(CLASSLIST[31] ,'mainScript13E')
.addItem("All", 'mainScriptAll')
.addToUi();
// menuEntries.push({name: "Generate PDF", functionName: "mainScript0"});
// ss.addMenu("Email Teacher", menuEntries);
/* ss.addMenu("Send Email", menuEntries);
menuEntries.push({name: CLASSLIST[0], functionName: "mainScript7I"});
menuEntries.push({name: CLASSLIST[1], functionName: "mainScript7P"});
menuEntries.push({name: CLASSLIST[2], functionName: "mainScript7S"});
menuEntries.push({name: CLASSLIST[3], functionName: "mainScript7W"});
menuEntries.push({name: CLASSLIST[4], functionName: "mainScript8I"});
menuEntries.push({name: CLASSLIST[5], functionName: "mainScript8P"});
menuEntries.push({name: CLASSLIST[6], functionName: "mainScript8S"});
menuEntries.push({name: CLASSLIST[7], functionName: "mainScript8W"});
menuEntries.push({name: CLASSLIST[8], functionName: "mainScript9I"});
menuEntries.push({name: CLASSLIST[9], functionName: "mainScript9S"});
menuEntries.push({name: CLASSLIST[10], functionName: "mainScript9P"});
menuEntries.push({name: CLASSLIST[11], functionName: "mainScript9W"});
*/
}
function mainScript7I() {
mainScriptByClass("A7", "C7","B7");
}
function mainScript7P() {
mainScriptByClass("A8","C8","B8");
}
function mainScript7S() {
mainScriptByClass("A9","C9","B9");
}
function mainScript7W() {
mainScriptByClass("A10","C10","B10");
}
function mainScript8I() {
mainScriptByClass("A11", "C11","B11");
}
function mainScript8P() {
mainScriptByClass("A12", "C12","B12");
}
function mainScript8S() {
mainScriptByClass("A13", "C13","B13");
}
function mainScript8W() {
mainScriptByClass("A14", "C14","B14");
}
function mainScript9I() {
mainScriptByClass("A15", "C15","B15");
}
function mainScript9S() {
mainScriptByClass("A16", "C16","B16");
}
function mainScript9P() {
mainScriptByClass("A17", "C17","B17");
}
function mainScript9W() {
mainScriptByClass("A18", "C18","B18");
}
function mainScript10A() {
mainScriptByClass("A19", "C19","B19");
}
function mainScript10B() {
mainScriptByClass("A20", "C20","B20");
}
function mainScript10C() {
mainScriptByClass("A21", "C21","B21");
}
function mainScript10D() {
mainScriptByClass("A22", "C22","B22");
}
function mainScript10E() {
mainScriptByClass("A23", "C23","B23");
}
function mainScript11A() {
mainScriptByClass("A24", "C24","B24");
}
function mainScript11B() {
mainScriptByClass("A25", "C25","B25");
}
function mainScript11C() {
mainScriptByClass("A26", "C26","B26");
}
function mainScript11D() {
mainScriptByClass("A27", "C27","B27");
}
function mainScript11E() {
mainScriptByClass("A28", "C28","B28");
}
function mainScript12A() {
mainScriptByClass("A29", "C29","B29");
}
function mainScript12B() {
mainScriptByClass("A30", "C30","B30");
}
function mainScript12C() {
mainScriptByClass("A31", "C31","B31");
}
function mainScript12D() {
mainScriptByClass("A32", "C32","B32");
}
function mainScript12E() {
mainScriptByClass("A33", "C33","B33");
}
function mainScript13A() {
mainScriptByClass("A34", "C34","B34");
}
function mainScript13B() {
mainScriptByClass("A35", "C35","B35");
}
function mainScript13C() {
mainScriptByClass("A36", "C36","B36");
}
function mainScript13D() {
mainScriptByClass("A37", "C37","B37");
}
function mainScript13E() {
mainScriptByClass("A38", "C38","B38");
}
function mainScriptAll() {
mainScript7I();
mainScript7P();
mainScript7S();
mainScript7W();
mainScript8I();
mainScript8P();
mainScript8S();
mainScript8W();
mainScript9I();
mainScript9P();
mainScript9S();
mainScript9W();
mainScript10A();
mainScript10B();
mainScript10C();
mainScript10D();
mainScript10E();
mainScript11A();
mainScript11B();
mainScript11C();
mainScript11D();
mainScript11E();
mainScript12A();
mainScript12B();
mainScript12C();
mainScript12D();
mainScript12E();
mainScript13A();
mainScript13B();
mainScript13C();
mainScript13D();
mainScript13E();
}
function mainScriptByClass(vClassCol, vEmailCol, vFolderCol) {
if (vClassName != "ALL" ){
/* var TrueMaintain = SpreadsheetApp.openById(MAINTAIN_GS).getSheetByName(MAINTAIN_SN);
var vClassName = TrueMaintain.getRange(vClassCol).getValue();
var emailID = TrueMaintain.getRange(vEmailCol).getValue();
var urlID = TrueMaintain.getRange(vFolderCol).getValue(); */
var vClassName = SpreadsheetApp.openById(MAINTAIN_GS).getSheetByName(MAINTAIN_SN).getRange(vClassCol).getValue();
var emailID = SpreadsheetApp.openById(MAINTAIN_GS).getSheetByName(MAINTAIN_SN).getRange(vEmailCol).getValue();
Logger.log("emailID " + emailID)
var urlID = SpreadsheetApp.openById(MAINTAIN_GS).getSheetByName(MAINTAIN_SN).getRange(vFolderCol).getValue();
generatePDFByClass(vClassName);
Logger.log("URLID " + urlID);
//generatePDFByClass(vClassName);
var linkFromID = "https://drive.google.com/drive/folders/" + urlID ;
// GmailApp.sendEmail(emailID, "PDF Files for this week", "Hello these are the PDF results of the forms your student filled out:" + inkFromID);
GmailApp.sendEmail(emailID, "PDF Files for this week", "Hello these are the PDF results of the forms your students filled out:" + linkFromID);
} else {
mainScript();
}
}
function loadMainDetails () {
var vClassName, vURL, vFTEmail;
/* const ssMainFileID = "1SR2NFYOHZ2h2JANUoTsIZvFhLJ7RQc2ob--dU4IPNxk";
const sMainFileSheet = "Class & Teacher Email"; */
var file = SpreadsheetApp.openById(MAINTAIN_GS);
var ss = file.getSheetByName(MAINTAIN_SN);
/* Get Data from maintenance file */
/* getRange (Row, Column, RowRange, ColumnRange) */
vData = ss.getRange(7,1, ss.getLastRow() - 1, ss.getMaxColumns()).getDisplayValues();
vData.forEach ( function (vRow) {
// rowNum = rowNum + 1;
if(vRow[0].length > 0 ){
vClassName = vRow[0];
vURL = vRow[1];
vFTEmail = vRow[2];
CLASSLIST.push(vClassName);
//MAINLIST.push("{'className':'"+ vClassName + "','url':'" + vURL + "','ft':'" + vFTEmail +"'}")
MAINLIST.push(vClassName + "," + vURL + "," + vFTEmail)
}
});
}
function getURLByClass(sKeyClass) {
var vURL = "";
var mapList = [];
for(var i in MAINLIST){
mapList = MAINLIST[i].toString().split(",")
if (sKeyClass == mapList[0] ) {
vURL = mapList[1];
break;
}
}
return vURL;
}
function getFTMailByClass(sKeyClass) {
var vEmail = "";
var mapList = [];
for(var i in MAINLIST){
mapList = MAINLIST[i].toString().split(",")
if (sKeyClass == mapList[0] ) {
vEmail = mapList[2];
break;
}
}
return vEmail;
}
function generatePDFByClass(vClass){
//Temp
loadMainDetails();
Logger.log("loadMainDetails");
var ss = SpreadsheetApp.getActiveSpreadsheet()
var respSheet =ss.getSheetByName(STUDENT_FORM_SN);
// Sorting by Class No
var vRange = respSheet.getRange("A:G")
vRange.sort ({column: 3, ascending: true} );
var rowNum = 1;
var vData = [];
var vHeader = [];
var vSName, vFTClass;
var vPrevFTEmail = "", vPrevFTClass = "" ;
var vFTEmail = "";
var headerNum = 0;
var maxColumn = respSheet.getMaxColumns();
Logger.log("maxColumn" + maxColumn);
/* Get Headers to apply in template as Q */
vHeader = respSheet.getRange(1,1,1, maxColumn).getDisplayValues();
/* Get Data to apply in in template as A */
Logger.log("Header" + vHeader);
/* getRange (Row, Column, RowRange, ColumnRange) */
vData = respSheet.getRange(2,1, respSheet.getLastRow() - 1, maxColumn).getDisplayValues();
Logger.log("Data " + vData);
/* Response inputs */
vData.forEach ( function (vRow) {
rowNum = rowNum + 1;
//if(vRow[3] == vClass){
if(vRow[2] == vClass){
vSName = vRow[1];
vFTClass = vRow[2];
Logger.log("vFTClass " + vFTClass);
// vFTClass = vRow[3];
vPrevFTEmail = vFTEmail;
// Setup FT Email at Column AA for Reference.
//respSheet.getRange("AA" + rowNum).setValue(vFTEmail);
// Start Column
var vStartColumn = 1
// Student Line for generate PDF and Email
sData = respSheet.getRange(rowNum,vStartColumn, 1, maxColumn).getDisplayValues();
// var nPdfFile = createBulkPDF(vHeader, sData, vSName, vFTClass);
createBulkPDF(vHeader, sData, vSName, vFTClass);
vPrevFTClass = vFTClass;
}
});
}
function mainScript(){
//Temp
loadMainDetails();
var ss = SpreadsheetApp.getActiveSpreadsheet()
var respSheet =ss.getSheetByName(STUDENT_FORM_SN);
// Sorting by Class No
var vRange = respSheet.getRange("A:G")
vRange.sort ({column: 3, ascending: true});
var rowNum = 1;
var vData = [];
var vHeader = [];
var vSName, vFTClass;
var vPrevFTEmail = "", vPrevFTClass = "" ;
var vFTEmail = "";
var headerNum = 0;
var maxColumn = respSheet.getMaxColumns()
/* Get Headers to apply in template as Q */
vHeader = respSheet.getRange(1,1,1, maxColumn).getDisplayValues();
/* Get Data to apply in in template as A */
/* getRange (Row, Column, RowRange, ColumnRange) */
vData = respSheet.getRange(2,1, respSheet.getLastRow() - 1, maxColumn).getDisplayValues();
/* Response inputs */
vData.forEach ( function (vRow) {
rowNum = rowNum + 1;
if(vRow[0].length > 0 ){
vSName = vRow[1];
vFTClass = vRow[3];
vPrevFTEmail = vFTEmail;
vFTEmail = getFTMailByClass(vFTClass);
/* Send email by Grouping */
if (vPrevFTClass != "" && vFTClass != vPrevFTClass) {
sendRespMail(vPrevFTEmail, vPrevFTClass);
// vPrevFTClass = vFTClass;
// vAllPDF.splice(0, vAllPDF.length)
}
}
// Setup FT Email at Column AA for Reference.
//respSheet.getRange("AA" + rowNum).setValue(vFTEmail);
// Start Column
var vStartColumn = 1
// Student Line for generate PDF and Email
sData = respSheet.getRange(rowNum,vStartColumn, 1, maxColumn).getDisplayValues();
//var nPdfFile = createBulkPDF(vHeader, sData, vSName, vFTClass);
createBulkPDF(vHeader, sData, vSName, vFTClass);
vPrevFTClass = vFTClass;
//vAllPDF.push(nPdfFile);
});
/* Last Email Group */
//if (vFTClass != vPrevFTClass) {
// sendRespMail(vFTEmail, vFTClass);
sendRespMail(vFTEmail,vFTClass )
//}
}
function sendRespMail(email, className){
var url_link = getURLByClass(className);
var message = "Hello, this is a URL fpr group of PDFs containing the Data of your class (" + className + ")";
message = message + "stduents who filled out the Google Form.\n";
message = message + "https://drive.google.com/drive/folders/" + url_link;
var emailTo = email;
var subject = "Forms filled out this Friday";
var html = message;
if( emailTo != "") {
GmailApp.sendEmail(emailTo,subject,html,"" );
}
}
function sendRespMailforAttachment(email, pdfFiles){
var message = "Hello, this is a group of PDFs containing the Data of your class ";
message = message + "stduents who filled out the Google Form."
var emailTo = email;
var subject = "Forms filled out this Friday";
var html = message;
if( emailTo != "") {
// GmailApp.sendEmail(emailTo,subject,html,{ attachments: pdfFiles } );
}
}
function createBulkPDF(pHeader, pData, pSName, pClass){
Logger.log("createBulkPDF1");
var rowNum = 2;
var allPDF = [];
var vTemplateDocFile = DriveApp.getFileById(TEMPLATE_DOC_ID);
var vTempFolder = DriveApp.getFolderById(TEMPFOLDER_ID);
var tempFile = vTemplateDocFile.makeCopy(vTempFolder);
var tempDocFile = DocumentApp.openById(tempFile.getId());
var body = tempDocFile.getBody();
// Fill in Header for A { }
var hRowNum = 1;
pHeader.forEach ( function (hRow) {
hRowNum = hRowNum + 1;
for (var i = 1; i < hRow.length; i++){
body.replaceText("{Q" + i + "}", hRow[i] );
}
});
Logger.log("createBulkPDF2");
// Fill in Inputs for Q { }
var sRowNum = 1;
pData.forEach ( function (sRow) {
sRowNum = sRowNum + 1;
for (var i = 1; i < sRow.length; i++){
body.replaceText("{A" + i + "}", sRow[i] );
}
});
tempDocFile.saveAndClose();
var vPdfContentBlob = tempDocFile.getAs(MimeType.PDF);
var pdfName = pSName+Utilities.formatDate(new Date(),"GMT+8"," HH:mm:ss")+".pdf";
var classFolderID = getURLByClass(pClass);
var vPdfFolder = DriveApp.getFolderById(classFolderID);
var pdfRpt=vPdfFolder.createFile(vPdfContentBlob).setName(pdfName);
// var pdfRpt=vPdfFolder.createFile(vPdfContentBlob).setName(pdfName);
DriveApp.getFileById(tempFile.getId()).setTrashed(true);
Logger.log("createBulkPDF3");
return pdfRpt;
}
I understand that you already have a Sheet with the data and a separate function to convert it to a PDF, therefore you only need to refine the table so it only shows a particular student. Based on your comment, I guess that the table looks like this:
First of all I strongly recommend you to use a filter table instead of a pivot table, because it will fit better to your goal and you can code it pretty easily. You can include my example below into your project as is, you would only need to change the student name from Irène to whatever your need.
function studentFilter() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var data = sheet.getDataRange().getA1Notation();
sheet.getRange(data).createFilter();
var criteria = SpreadsheetApp.newFilterCriteria().whenTextEqualTo('Irène')
.build();
sheet.getFilter().setColumnFilterCriteria(1, criteria);
};
That code would use the SpreadsheetApp class of Apps Script to open the Sheet and get its range. After that it would create a filter table using the .createFilter() method and the FilterCriteriaBuilder class. After both the filter table and its criteria is created, it just applies the criteria to the table. This would be the end result on the previous example:
At this point the Sheet already looks like your goal, so you only need to follow your usual workflow and call the printing PDF function.

Cannot append option for select form

I want to load option data from a Sheet and append to the select element, but I cannot set the global variable
var select_values = [];
google.script.run.withSuccessHandler(loadOpt).loadData("Test");
function loadOpt(data_arrar) {
const arrayColumn = (arr, n) => arr.map(x => x[n]);
select_values = arrayColumn(data_arrar, 1);
}
var opt = document.getElementById("select_opts");
for (i = 0; i < select_values.length; i++) {
let option = document.createElement("option");
option.value = i;
option.text = select_values[i];
opt.appendChild(option);
}
The result is nothing appended to [select_opts].
I'm update the code.gs.
var TestSheets = SpreadsheetApp.openById("XXX");
function doGet(e) {
if (!e.parameter.page) {
// When no specific page requested, return "home page"
return HtmlService.createTemplateFromFile('Home').evaluate();
}
// else, use page parameter to pick an html file from the script
return HtmlService.createTemplateFromFile(e.parameter['page']).evaluate();
}
function loadData(Sheetname) {
var sheet = TestSheets.getSheetByName(Sheetname);
var rows = sheet.getRange("A1").getValue();
var Columns = sheet.getRange("B1").getValue();
var data_return = [];
for (var i = 0; i <= rows; i++) {
data_return[i] = [];
for (var j = 0; j <= Columns - 1; j++) {
data_return[i][j] = sheet.getRange(i + 2, j + 1).getValue();
}
}
return data_return;
}
Note that function loadData works well & has been tested.
The problem is that Javascript code outside of functions will be called on loading of the HTML document
It will not be reevaluated after running code in functions
If you want your loop to run after function loadData(Sheetname) - you need to put the loop into another function that is called after the execution of function loadData(Sheetname)
Sample:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<script>
var select_values = [];
google.script.run.withSuccessHandler(loadOpt).loadData("Test");
function loadOpt(data_arrar) {
const arrayColumn = (arr, n) => arr.map(x => x[n]);
select_values = arrayColumn(data_arrar, 1);
console.log(" select_values: " + select_values);
callLater();
}
console.log(" select_values in call outside: " + select_values);
function callLater(){
console.log(" select_values in callLater: " + select_values);
var opt = document.getElementById("select_opts");
for (i = 0; i < select_values.length; i++) {
let option = document.createElement("option");
option.value = i;
option.text = select_values[i];
opt.appendChild(option);
}
}
</script>
</body>
</html>

Google Charts - Hide series label too wide

With Google Apps Script I created a stacked bar chart. This is the result:
https://drive.google.com/file/d/1DZ2ZtSu2A81OAMc9ds9A4y-bS0l_oftL/view?usp=sharing
I would like to hide the labels on the bars when they are too wide compared to the available space. Following the instructions I found at this address https://developers.google.com/chart/interactive/docs/reference#DataView_setColumns I tried to use a custom function instead of "stringify" in the "annotationObj" object ( see the code) to create a label of zero length, but my function is not recognized when I try to create the chart (error message: Unknown function "getValueAt").
This is my code:
function CHARTS_002() { //
var ABCarray = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL','AM','AN','AO','AP','AQ','AR','AS','AT','AU','AV','AW','AX','AY','AZ','BA','BB','BC','BD','BE','BF','BG','BH','BI','BJ','BK','BL','BM','BN','BO','BP','BQ','BR','BS','BT','BU','BV','BW','BX','BY','BZ','CA','CB','CC','CD','CE','CF','CG','CH','CI','CJ','CK','CL','CM','CN','CO','CP','CQ','CR','CS','CT','CU','CV','CW','CX','CY','CZ'];
var ssId = '1KA2BnUsC-Lp64UhxjtN5Gtth2dOiHp3-pRwIQjAYOLI';
var shName = 'TopGrossingFilms';
var aScale = ["Poco d'accordo","Né d’accordo né in disaccordo","Abbastanza d'accordo","Totalmente d'accordo","Media"];
var aRange = [['B',2],['N',12]];
var sheet = SpreadsheetApp.openById(ssId).getSheetByName(shName);
var row = aRange[0][1];
var column = ABCarray.indexOf(aRange[0][0]) + 1;
var numRows = aRange[1][1] - aRange[0][1];
var numColumns = ABCarray.indexOf(aRange[1][0]) - ABCarray.indexOf(aRange[0][0]) + 1;
var sheetV = sheet.getRange(aRange[0][1], ABCarray.indexOf(aRange[0][0]) + 1, numRows, numColumns).getValues();
var sheetT1D = sheetV[0];
var aData = [];
for (var r in sheetV) {
aData.push(sheetV[r])
}
for (var r in aData) {
for (var c in aData[r]) {
if (!isNaN(aData[r][c])) {
aData[r][c] = round(aData[r][c],2);
if (aData[0][c] == 'Media') {
aData[r][c] = 13;
}
}
}
}
var data = Charts.newDataTable();
var annotationObj = { calc: "stringify",
//calc: "getValueAt",
//calc: "function(data, row) { var ret = data[row][§col§]; if (ret < 7) {return '';} else {return JSON.stringify(ret)} }",
sourceColumn: -1,
type: "string",
role: "annotation"
}
var aAnnotation = [];
for (var r in aData) {
if (r < 1) { continue; }
if (r == 1) {
for (var c in aData[r]) {
aAnnotation.push(c);
if (isNaN(aData[r][c])) {
data.addColumn(Charts.ColumnType.STRING, aData[0][c]);
} else {
data.addColumn(Charts.ColumnType.NUMBER, aData[0][c]);
if (aScale.indexOf(aData[0][c]) != -1) {
var myObj = JSON.parse(JSON.stringify(annotationObj));
var myCol = Number(c);
if (aData[0][c] == 'Media') {
myCol = Number(c) + 1;
}
myObj.sourceColumn = myCol;
myObj.calc = myObj.calc.replace("§col§",myCol)
aAnnotation.push(myObj);
}
}
}
}
data.addRow(aData[r]);
}
var dataViewDefinition = Charts.newDataViewDefinition().setColumns(aAnnotation);
var aTicks = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
var chartBuilder = Charts.newBarChart()
.setDimensions(1200, 700)
.setOption("hAxis", { ticks: aTicks})
.setOption('legend',{ position:'top', maxLines:3 })
.setOption('chartArea',{ left:650 })
.setOption('series',{
6: {type:'line', color:'00FF00', lineWidth:3, visibleInLegend: false}
})
.setDataTable(data)
.setDataViewDefinition(dataViewDefinition)
.setOption('bar', { groupWidth: "80%" })
.setStacked()
.setColors(['#C10000','#F1C12A','#BFBFBF','#0070C1','#244062','00FF00']);
var chart = chartBuilder.build();
var chartImage = chart.getAs('image/png').copyBlob();
DriveApp.createFile(chartImage).setName('NewBarChart.png');
}
function getValueAt(column, dataTable, row) {
var value = dataTable(row, column);
var ret = '';
if (value > 7) { ret = value.toString()}
return ret;
}
function round(value, exp) {
var funcName = 'round';
if (typeof exp === 'undefined' || +exp === 0)
return Math.round(value);
value = +value;
exp = +exp;
if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
return NaN;
// Shift
value = value.toString().split('e');
value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));
// Shift back
value = value.toString().split('e');
return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}
The chart that is produced can be seen in this public folder: https://drive.google.com/file/d/1DZ2ZtSu2A81OAMc9ds9A4y-bS0l_oftL/view?usp=sharing
Does anyone know how to get the result I would like to get?
Thanks in advance.