Problem:
The import function importXLSXtoGsheet() times out before it can process all 52 XLSX files, I received the error:
Exception: Time-out: https://www.googleapis.com/batch/drive/v3 at [unknown function](Code:63) at Do(Code:8) at importXLSXtoGsheet(Code:71)
If I run the function with 1 file in the importXLS folder, it works correctly.
Script explained:
I've got 52 folders, each containing one spreadsheet file.
Each folder is shared with different colleagues.
During the day, people make changes to the files.
At the end of the day, all files are collected in one folder (gsheetFolder) and converted to XLSX files, using the function collectAndExportXLS.
These files are copied to a local server in the evening (using batch script and drive sync) which updates other information in the file and are copied back to the importXLSXfolder.
In the morning the importXLSXtoGsheet function runs and converts all XLSX files in the importXLSXfolder folder to Gsheet files in the gsheetFolder.
After that sortGsheetFiles runs, sorting and moving every Gsheet file in one of the 52 folders (using an array list from the current spreadsheet).
Other actions include cleaning the folders with the deleteFolder function.
Script:
var gsheetFolder = '###';
var XLSXfolder = '###';
var importXLSXfolder = '###';
// Modified
function deleteFolder(folderId) {
var url = "https://www.googleapis.com/drive/v3/files?q='" + folderId + "'+in+parents+and+trashed%3Dfalse&fields=files%2Fid&access_token=" + ScriptApp.getOAuthToken();
var res = UrlFetchApp.fetch(url);
var obj = JSON.parse(res.getContentText());
var reqs = obj.files.map(function(e) {return {method: "DELETE", endpoint: "https://www.googleapis.com/drive/v3/files/" + e.id}});
var requests = {batchPath: "batch/drive/v3", requests: reqs};
if (requests.requests.length > 0) BatchRequest.Do(requests);
}
// Added
function deleteFiles(files) {
var reqs = files.map(function(e) {return {method: "DELETE", endpoint: "https://www.googleapis.com/drive/v3/files/" + e.id}});
var requests = {batchPath: "batch/drive/v3", requests: reqs};
if (requests.requests.length > 0) BatchRequest.Do(requests);
}
// Added
function getValuesFromSpreadsheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
return sheet.getRange("A2:B53").getValues();
}
// Modified
function sortGsheetFiles() {
var url = "https://www.googleapis.com/drive/v3/files?q='" + gsheetFolder + "'+in+parents+and+mimeType%3D'" + MimeType.GOOGLE_SHEETS + "'+and+trashed%3Dfalse&fields=files(id%2Cname)&access_token=" + ScriptApp.getOAuthToken();
var res = UrlFetchApp.fetch(url);
var obj = JSON.parse(res.getContentText());
var values = getValuesFromSpreadsheet();
var reqs = values.reduce(function(ar, e) {
for (var i = 0; i < obj.files.length; i++) {
if (obj.files[i].name == e[0]) {
ar.push({
method: "PATCH",
endpoint: "https://www.googleapis.com/drive/v3/files/" + obj.files[i].id + "?addParents=" + e[1] + "&removeParents=" + gsheetFolder,
});
break;
}
}
return ar;
}, []);
var requests = {batchPath: "batch/drive/v3", requests: reqs};
if (requests.requests.length > 0) BatchRequest.Do(requests);
deleteFolder(importXLSXfolder);
}
// Modified
function importXLSXtoGsheet(){
deleteFolder(XLSXfolder);
var url = "https://www.googleapis.com/drive/v3/files?q='" + importXLSXfolder + "'+in+parents+and+mimeType%3D'" + MimeType.MICROSOFT_EXCEL + "'+and+trashed%3Dfalse&fields=files(id%2Cname)&access_token=" + ScriptApp.getOAuthToken();
var res = UrlFetchApp.fetch(url);
var obj = JSON.parse(res.getContentText());
var reqs = obj.files.map(function(e) {return {
method: "POST",
endpoint: "https://www.googleapis.com/drive/v3/files/" + e.id + "/copy",
requestBody: {mimeType: MimeType.GOOGLE_SHEETS, name: e.name + ".xlsx", parents: [gsheetFolder]},
}
});
var requests = {batchPath: "batch/drive/v3", requests: reqs};
if (requests.requests.length > 0) BatchRequest.Do(requests);
deleteFolder(importXLSXfolder);
}
// Modified
function ConvertBackToXLS(fileList) {
var token = ScriptApp.getOAuthToken();
var reqs1 = fileList.map(function(e) {return {
method: "GET",
url: "https://docs.google.com/spreadsheets/export?id=" + e.id + "&exportFormat=xlsx&access_token=" + token,
}
});
var res = UrlFetchApp.fetchAll(reqs1);
var reqs2 = res.map(function(e, i) {
var metadata = {name: fileList[i].name, parents: [XLSXfolder]};
var form = FetchApp.createFormData(); // Create form data
form.append("metadata", Utilities.newBlob(JSON.stringify(metadata), "application/json"));
form.append("file", e.getBlob());
var url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart";
return {url: url, method: "POST", headers: {Authorization: "Bearer " + token}, body: form};
});
FetchApp.fetchAll(reqs2);
}
// Modified
function collectAndExportXLS() {
deleteFolder(gsheetFolder);
var values = getValuesFromSpreadsheet();
var reqs1 = values.reduce(function(ar, e) {
if (e[0] && e[1]) {
ar.push({
method: "GET",
endpoint: "https://www.googleapis.com/drive/v3/files?q='" + e[1] + "'+in+parents+and+trashed%3Dfalse&fields=files(id%2Cname)",
});
}
return ar;
}, []);
var resForReq1 = BatchRequest.Do({batchPath: "batch/drive/v3", requests: reqs1});
var temp = resForReq1.getContentText().split("--batch");
var files = temp.slice(1, temp.length - 1).map(function(e) {return JSON.parse(e.match(/{[\S\s]+}/g)[0])});
var fileList = files.reduce(function(ar, e) {return ar.concat(e.files.map(function(f) {return f}))}, []);
ConvertBackToXLS(fileList);
deleteFiles(fileList);
}
About your question, I could understand like below.
When importXLSXtoGsheet() is run with 52 files, the error occurs.
When importXLSXtoGsheet() is run with less than 13 files, no error occurs.
Other functions except for importXLSXtoGsheet() works fine.
If my understanding is correct, as one workaround, it decides the maximum number for processing the files once. When this is reflect to importXLSXtoGsheet() of your script, the modified script is as follows.
Modified script:
function importXLSXtoGsheet(){
deleteFolder(XLSXfolder);
var url = "https://www.googleapis.com/drive/v3/files?q='" + importXLSXfolder + "'+in+parents+and+mimeType%3D'" + MimeType.MICROSOFT_EXCEL + "'+and+trashed%3Dfalse&fields=files(id%2Cname)&access_token=" + ScriptApp.getOAuthToken();
var res = UrlFetchApp.fetch(url);
var obj = JSON.parse(res.getContentText());
// I modified below script.
var n = 10; // Maximum number.
var files = [];
var len = obj.files.length;
for (var i = 0; i < len; i++) {
files.push(obj.files.splice(0, n));
len -= n - 1;
}
files.forEach(function(f) {
var reqs = f.map(function(e) {return {
method: "POST",
endpoint: "https://www.googleapis.com/drive/v3/files/" + e.id + "/copy",
requestBody: {mimeType: MimeType.GOOGLE_SHEETS, name: e.name + ".xlsx", parents: [gsheetFolder]},
}
});
var requests = {batchPath: "batch/drive/v3", requests: reqs};
if (requests.requests.length > 0) BatchRequest.Do(requests);
});
deleteFolder(importXLSXfolder);
}
Note:
In this sample script, 10 files are processed every batch request. If you want to change this, please modify var n = 10;.
Related
I used a script to backup all my google docs, when running the function it works correctly and makes a copy. But when setting up a time-driven trigger it doesn't run but says that it does.
Project Trigger
I checked to make sure the project's time zone was correct and tried to run it at different times, but it still doesn't run.
The script below:
var BACKUP_FOLDER_ID = '16SGkgNCPeNn9DgB9RmhLAv-z5C_kCbCM';
var NATIVE_MIME_TYPES = {};
NATIVE_MIME_TYPES[MimeType.GOOGLE_DOCS] = MimeType.MICROSOFT_WORD;
NATIVE_MIME_TYPES[MimeType.GOOGLE_SHEETS] = MimeType.MICROSOFT_EXCEL;
NATIVE_MIME_TYPES[MimeType.GOOGLE_SLIDES] = MimeType.MICROSOFT_POWERPOINT;
var NATIVE_EXTENSIONS = {};
NATIVE_EXTENSIONS[MimeType.GOOGLE_DOCS] = '.docx';
NATIVE_EXTENSIONS[MimeType.GOOGLE_SHEETS] = '.xlsx';
NATIVE_EXTENSIONS[MimeType.GOOGLE_SLIDES] = '.pptx';
var BACKUP_MIME_TYPES = Object.keys(NATIVE_MIME_TYPES);
function backupAll() {
const backupFolder = DriveApp.getFolderById(BACKUP_FOLDER_ID);
BACKUP_MIME_TYPES.forEach(function(mimeType) {
var files = DriveApp.getFilesByType(mimeType);
while (files.hasNext()) {
var file = files.next();
if (file.getOwner() && file.getOwner().getEmail() == Session.getActiveUser().getEmail()) {
backup(file, backupFolder);
}
}
});
}
function backup(file, folder) {
var targetName = file.getName() + ' ' + file.getId();
var lastUpdated = file.getLastUpdated();
var pdf = getPdfBlob(file);
var native = getNativeBlob(file);
var zip = Utilities.zip([pdf, native], targetName + '.zip');
createOrUpdateFileForBlob(zip, folder, lastUpdated);
}
function createOrUpdateFileForBlob(blob, folder, ifOlderThan) {
var existingFiles = folder.getFilesByName(blob.getName());
if (existingFiles.hasNext()) {
var file = existingFiles.next();
if (file.getLastUpdated() < ifOlderThan) {
updateFile(file, blob);
}
} else {
folder.createFile(blob);
}
}
function updateFile(file, blob) {
const url = 'https://www.googleapis.com/upload/drive/v2/files/' + file.getId() + '?uploadType=media';
const params = {
method: 'put',
headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() },
payload: blob
};
var response = UrlFetchApp.fetch(url, params);
if (response.getResponseCode() < 200 || response.getResponseCode() > 299) {
throw 'Failed to update file named ' + file.getName();
}
}
function getPdfBlob(file) {
var blob = file.getAs('application/pdf');
return blob;
}
function getNativeBlob(file) {
const nativeMimeType = NATIVE_MIME_TYPES[file.getMimeType()];
const extension = NATIVE_EXTENSIONS[file.getMimeType()];
const url = 'https://www.googleapis.com/drive/v2/files/' + file.getId() + '/export?mimeType=' + nativeMimeType;
const params = {
method: 'get',
headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() }
};
const blob = UrlFetchApp.fetch(url, params).getBlob();
blob.setName(file.getName() + extension);
return blob;
}
I create a PDF and then send its information to my website so that this PDF is converted to an image file and published:
SpreadsheetApp.flush();
var theurl = 'https://docs.google.com/a/mydomain.org/spreadsheets/d/' +
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' +
'/export?format=pdf' +
'&size=0' +
'&portrait=true' +
'&fitw=true' +
'&top_margin=0' +
'&bottom_margin=0' +
'&left_margin=0' +
'&right_margin=0' +
'&sheetnames=false&printtitle=false' +
'&pagenum=false' +
'&gridlines=false' +
'&fzr=FALSE' +
'&gid=' +
'aaaaaaaaaaa';
var token = ScriptApp.getOAuthToken();
var docurl = UrlFetchApp.fetch(theurl, { headers: { 'Authorization': 'Bearer ' + token } });
var pdfBlob = docurl.getBlob();
//...get token and Blob (do not create the file);
var fileName = ss.getSheetByName("General").getRange("H2").getValue();
//Access or create the 'Archives' folder;
var folder;
var folders = DriveApp.getFoldersByName("Archives");
if(folders.hasNext()) {
folder = folders.next();
}else {
folder = DriveApp.createFolder("Archives");
}
//Remove duplicate file with the same name;
var existing = folder.getFilesByName(fileName);
if(existing.hasNext()) {
var duplicate = existing.next();
if (duplicate.getOwner().getEmail() == Session.getActiveUser().getEmail()) {
var durl = 'https://www.googleapis.com/drive/v3/files/'+duplicate.getId();
var dres = UrlFetchApp.fetch(durl,{
method: 'delete',
muteHttpExceptions: true,
headers: {'Authorization': 'Bearer '+token}
});
var status = dres.getResponseCode();
if (status >=400) {
} else if (status == 204) {
folder.createFile(pdfBlob.setName(fileName));
}
}
} else {
folder.createFile(pdfBlob.setName(fileName));
}
Utilities.sleep(5000);
createPostByFileName(folder, fileName);
function createPostByFileName(folder, fileName) {
var fileIterator = folder.getFilesByName(fileName);
if(fileIterator.hasNext()) {
var file = fileIterator.next()
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW)
name = file.getName();
league = name.split(' ')[0];
title = name.split(league)[1].split('.pdf')[0];
link = file.getUrl();
shareable = link.split('/view')[0];
id = file.getId();
var data = {
'api_league_name': league,
'title': title,
'google_drive_id': id,
'google_drive_url': shareable,
'pass': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
};
var options = {
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify(data)
};
UrlFetchApp.fetch('https://www.xxx.com.br/api/posts', options);
}
}
For some reason I am forced to put Utilities.sleep(5000); before calling the function that sends the PDF data to my website, because if I don't, when the website tries to convert the PDF to an image, an problem happens as if the PDF is not yet available in the folder or it generates a big slowdown until can access the file.
Obviously putting this sleep is not a professional way to solve the case, because then I can't even know why this happens.
Anyone who has had this experience, can tell me how I should proceed in a professional way?
From the discussions in the comment, when Drive API is used for your script, it becomes as follows. Before you use this, please enable Drive API at Advanced Google services.
Modified script:
function sample() {
const createFile = (filename, blob, folderId) => Drive.Files.insert({ title: filename, parents: [{ id: folderId }] }, blob);
SpreadsheetApp.flush();
var theurl = 'https://docs.google.com/a/mydomain.org/spreadsheets/d/' +
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' +
'/export?format=pdf' +
'&size=0' +
'&portrait=true' +
'&fitw=true' +
'&top_margin=0' +
'&bottom_margin=0' +
'&left_margin=0' +
'&right_margin=0' +
'&sheetnames=false&printtitle=false' +
'&pagenum=false' +
'&gridlines=false' +
'&fzr=FALSE' +
'&gid=' +
'aaaaaaaaaaa';
var token = ScriptApp.getOAuthToken();
var docurl = UrlFetchApp.fetch(theurl, { headers: { 'Authorization': 'Bearer ' + token } });
var pdfBlob = docurl.getBlob();
var fileName = ss.getSheetByName("General").getRange("H2").getValue();
var folder;
var folders = DriveApp.getFoldersByName("Archives");
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder("Archives");
}
var existing = folder.getFilesByName(fileName);
var pdfFileId = "";
if (existing.hasNext()) {
var duplicate = existing.next();
if (duplicate.getOwner().getEmail() == Session.getActiveUser().getEmail()) {
var durl = 'https://www.googleapis.com/drive/v3/files/' + duplicate.getId();
var dres = UrlFetchApp.fetch(durl, {
method: 'delete',
muteHttpExceptions: true,
headers: { 'Authorization': 'Bearer ' + token }
});
var status = dres.getResponseCode();
if (status >= 400) {
} else if (status == 204) {
var obj = createFile(fileName, pdfBlob, folder.getId());
pdfFileId = obj.id;
}
}
} else {
var obj = createFile(fileName, pdfBlob, folder.getId());
pdfFileId = obj.id;
}
// Utilities.sleep(5000);
Drive.Permissions.insert({ role: "reader", type: "anyone" }, pdfFileId);
league = fileName.split(' ')[0];
title = fileName.split(league)[1].split('.pdf')[0];
shareable = "https://drive.google.com/file/d/" + pdfFileId;
id = pdfFileId;
var data = {
'api_league_name': league,
'title': title,
'google_drive_id': id,
'google_drive_url': shareable,
'pass': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
};
var options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(data)
};
UrlFetchApp.fetch('https://www.xxx.com.br/api/posts', options);
}
Note:
This is a sample script for testing whether Utilities.sleep(5000) can be removed.
I have a sheet, with some tabs, in one tab i make statement for my clients, there i select a customer in B2 and using a query formula i get the desired data, after that i have a script to first hide blank rows and then send that email with the pdf as attachment, and i do thins only when need send that email.
this is my script:
function HideEdoCta() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName('Edo Cta');
var lastRow = sheet.getLastRow();
var range = sheet.getRange(1, 4, lastRow, 1);
var data = range.getValues();
//Rows
for (var i = 0; i < data.length; i++) {
if (data[i][0] == 1) {
sheet.hideRows(i + 1)
}
}
sheet.hideColumns(4, 1)
}
function UnHideEdoCta() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Edo Cta');
sheet.unhideRow(sheet.getDataRange());
}
function MailEdoCta(email, subject, body, sheetName) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName('Edo Cta');
var valueToCheck = sheet.getRange('C1').getValue();
if (valueToCheck == true && casa != '') {
HideEdoCta()
SpreadsheetApp.flush()
var email = sheet.getRange('D1').getValue(); // Enter the required email address here
var casa = sheet.getRange('B2').getValue();
var subject = 'Estado de Cuenta Sky';
var body =
"Hola, <strong>" + casa + "</strong><br><br>" +
"xxx.<br>" +
"-- <br>" +
"<strong>xxx</strong><br>"
;
// Base URL
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ss.getId());
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&scale=1' // 1= Normal 100% / 2= Fit to width / 3= Fit to height / 4= Fit to Page
+ '&fitw=true&source=labnol' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=true&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
}).getBlob().setName(sheet.getName() + ".pdf");
// Uncomment the line below to save the PDF to the root of your drive.
// var newFile = DriveApp.createFile(response).setName(sheet.getName() + ".pdf")
GmailApp.sendEmail(email, subject, "",
{ name: 'xxx', htmlBody: body, attachments: [response] }
);
SpreadsheetApp.flush()
UnHideEdoCta()
sheet.getRange('C1').setValue(false)
sheet.getRange('B2').clearContent()
}
else {
sheet.getRange('C1').setValue(false)
}
}
in this script i have a validation that if there is a customer selected in B2 and C1 is true, send the email, because the trigger is set to run every minute.
now what i need is send the same email, every 5th of the month to all my customers automatically (i only have 24 customers)
how can i make this possible without selecting one by one ?
any help please ?
here is a sample sheet with sample data
Try this
function mailing() {
var doc = SpreadsheetApp.getActive();
var sh = SpreadsheetApp.getActiveSheet();
var data = doc.getRange('listOfCustomers').getValues();
for (var i = 0; i < data.length; i++) {
if (data[i][1]) {
sh.getRange('B2').setValue(data[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(1000);
}
}
};
a copy of your spreadsheet would have been appreciated for testing.
In response to your comment from above:
This is the basic script that reads the information spreadsheet and calls the email function along with an object that identifies the template and other important information.
function sendEmails() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Emails');
const [hA, ...dt] = sh.getDataRange().getValues();
const vs = dt.filter(r => r[0] && r[1] && r[2] && r[3] && r[4] == 'Send' && r[5]);
const idx = {};
hA.forEach((h, i) => { idx[h] = i; });
vs.forEach(function (r) {
let obj = { index: idx, row: r }
this[r[5]](obj);//This executes the function named in r[5] and passes it obj
});
}
This is one of the functions that sends emails or creates a draft:
function sendEmails103(obj) {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('libImages');
const [hA, ...dt] = sh.getDataRange().getValues();
let idx = {};
hA.forEach((h, i) => idx[h] = i);
let imgObj = {};
vs = dt.filter(r => r[idx['filename']] == obj.row[obj.index['htmlFile']])
vs.forEach((r, i) => {
let params = { muteHttpExceptions: true, headers: { "Authorization": "Bearer " + ScriptApp.getOAuthToken() } };
let aurl = "https://photoslibrary.googleapis.com/v1/mediaItems/" + r[idx['mediaItemId']]
let resp = JSON.parse(UrlFetchApp.fetch(aurl, params).getContentText());
let burl = `${resp.baseUrl}=w${r[idx['maxwidth']]}-h${r[idx['maxheight']]}`
imgObj[r[idx['Key']]] = UrlFetchApp.fetch(burl).getBlob();
});
let htmlTemplate = HtmlService.createTemplateFromFile(obj.row[obj.index['htmlFile']]);
let html = htmlTemplate.evaluate().getContent();
if (html) {
if (obj.row[obj.index['operation']] == 'Create Draft') {
GmailApp.createDraft(obj.row[obj.index['Recipients']], obj.row[obj.index['Subject']], '', { htmlBody: html, inlineImages: imgObj, replyTo: obj.row[obj.index['replyTo']] });
} else {
GmailApp.sendEmail(obj.row[obj.index['Recipients']],obj.row[obj.index['Subject']],'',{htmlBody:html,inlineImages:imgObj,replyTo: obj.row[obj.index['replyTo']]});
}
}
}
This probably not for everyone, but I like having more room to customize my letters and having been a web developer for a long time I'm quite comfortable composing letters in html which is probably not true of everyone.
I'm experiencing an error called code 500. The script works fine if I'm using it in the owner account, but if I'm going to open the file as a user/editor, the code 500 error shows. Here is the link to the sample spreadsheet that I'm working on. I tried asking here but seems like it is a little complicated so I created a new single spreadsheet so that it can be easily identified the error.
Here's the code
function doGet(e) {
this[e.parameter.run](e.parameter.sheetName || null);
return ContentService.createTextOutput('It worked!');
}
function HideRows() {
const activeSheet = SpreadsheetApp.getActiveSheet();
const url = ScriptApp.getService().getUrl();
UrlFetchApp.fetch(url + "?run=script_HideRows&sheetName=" + activeSheet.getSheetName(), {headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()}});
// DriveApp.getFiles() // This is used for automatically detecting the scope of "https://www.googleapis.com/auth/drive.readonly". This scope is used for the access token.
}
function showRows() {
const url = ScriptApp.getService().getUrl();
UrlFetchApp.fetch(url + "?run=script_showRows", {headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()}});
Browser.msgBox(url + "?run=script_showRows");
}
var startRow = 6;
var colToCheck = 2;
// This script is the same with your "HideRows".
function script_HideRows() {
var sheetNames = ["MS_Q1", "MS_Q2", "MS_Q3", "MS_Q4", "SUMMARY"]; // Please set the sheet names here. In this case, 4 sheets are used.
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getSheets().forEach(sheet => {
var sheetName = sheet.getSheetName();
if (sheetNames.includes(sheetName)) {
if (sheetName == "SUMMARY") { // When the sheet is "SUMMARY", the start row is changed.
startRow = 7;
}
var numRows = sheet.getLastRow();
var elements = sheet.getRange(startRow, colToCheck, numRows).getValues();
for (var i=0; i < elements.length; i++) {
if (shouldHideRow(sheet, i, elements[i][0])) {
sheet.hideRows(startRow + i);
}
}
// Hide the rest of the rows
var totalNumRows = sheet.getMaxRows();
if (totalNumRows > numRows)
sheet.hideRows(numRows+1, totalNumRows - numRows);
}
});
}
// This script is the same with your "showRows".
function script_showRows() {
// set up spreadsheet and sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// var ss = SpreadsheetApp.getActiveSpreadsheet(),
var sheets = ss.getSheets();
for(var i = 0, iLen = sheets.length; i < iLen; i++) {
// get sheet
var sh = sheets[i];
// unhide columns
var rCols = sh.getRange("1:1");
sh.unhideColumn(rCols);
// unhide rows
var rRows = sh.getRange("A:A");
sh.unhideRow(rRows);
}
};
function shouldHideRow(ss, rowIndex, rowValue) {
if (rowValue != '') return false;
if (ss.getRange(startRow + rowIndex, colToCheck, 1, 1).isPartOfMerge()) return false;
if (ss.getRange(startRow + rowIndex + 1, colToCheck, 1, 1).isPartOfMerge()) return false;
return true;
}
First, comment out this
// this [e.parameter.run] (e.parameter.sheetName || null);
Second, avoid this
const url = ScriptApp.getService().getUrl();
replace to
const url = 'https://script.google.com/macros/s/ABCD1234/exec';
Third, publish the web app for all user accessing every time you change code
The next code works for me fine
function doGet(e) {
return ContentService.createTextOutput('It worked!');
}
function HideRows() {
const activeSheet = SpreadsheetApp.getActiveSheet();
const url =
'https://script.google.com/macros/s/ABCD1234/exec';
var response = UrlFetchApp.fetch(
url + '?run=script_HideRows&sheetName=' + activeSheet.getSheetName(),
{
headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() },
muteHttpExceptions: true,
}
);
Browser.msgBox(response);
}
Actual web page image that should come I am getting error:
"The script completed but did not return anything."
I have published new version of code but still getting the error after saving the code and error which is coming is "The script completed but did not return anything."
Code.gs
function doGet(e) {
var op = e.parameter.action;
var ss = SpreadsheetApp.openByUrl("some url");
var sheet = ss.getSheetByName("Sheet1");
if (op == "insert")
return insert_value(e, sheet);
//Make sure you are sending proper parameters
if (op == "read")
return read_value(e, ss);
if (op == "update")
return update_value(e, sheet);
if (op == "delete")
return delete_value(e, sheet);
}
//Recieve parameter and pass it to function to handle
function insert_value(request, sheet) {
var id = request.parameter.id;
var country = request.parameter.name;
var flag = 1;
var lr = sheet.getLastRow();
for (var i = 1; i <= lr; i++) {
var id1 = sheet.getRange(i, 2).getValue();
if (id1 == id) {
flag = 0;
var result = "Id already exist..";
}
}
//add new row with recieved parameter from client
if (flag == 1) {
var d = new Date();
var currentTime = d.toLocaleString();
var rowData = sheet.appendRow([currentTime, id, country]);
var result="Insertion successful";
}
result = JSON.stringify({
"result": result
});
return ContentService
.createTextOutput(request.parameter.callback + "(" + result + ")")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
function read_value(request, ss) {
var output = ContentService.createTextOutput(),
data = {};
//Note : here sheet is sheet name , don't get confuse with other operation
var sheet = "sheet1";
data.records = readData_(ss, sheet);
var callback = request.parameters.callback;
if (callback === undefined) {
output.setContent(JSON.stringify(data));
} else {
output.setContent(callback + "(" + JSON.stringify(data) + ")");
}
output.setMimeType(ContentService.MimeType.JAVASCRIPT);
return output;
}
function readData_(ss, sheetname, properties) {
if (typeof properties == "undefined") {
properties = getHeaderRow_(ss, sheetname);
properties = properties.map(function(p) { return p.replace(/\s+/g, '_'); });
}
var rows = getDataRows_(ss, sheetname),
data = [];
for (var r = 0, l = rows.length; r < l; r++) {
var row = rows[r],
record = {};
for (var p in properties) {
record[properties[p]] = row[p];
}
data.push(record);
}
return data;
}
function getDataRows_(ss, sheetname) {
var sh = ss.getSheetByName(sheetname);
return sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn()).getValues();
}
function getHeaderRow_(ss, sheetname) {
var sh = ss.getSheetByName(sheetname);
return sh.getRange(1, 1, 1, sh.getLastColumn()).getValues()[0];
}
//update function
function update_value(request, sheet) {
var output = ContentService.createTextOutput();
var id = request.parameter.id;
var flag = 0;
var country = request.parameter.name;
var lr = sheet.getLastRow();
for (var i = 1; i <= lr; i++) {
var rid = sheet.getRange(i, 2).getValue();
if (rid == id) {
sheet.getRange(i, 3).setValue(country);
var result = "value updated successfully";
flag = 1;
}
}
if (flag == 0)
var result="id not found";
result = JSON.stringify({
"result": result
});
return ContentService
.createTextOutput(request.parameter.callback + "(" + result + ")")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
function delete_value(request,sheet) {
var output = ContentService.createTextOutput();
var id = request.parameter.id;
var country = request.parameter.name;
var flag = 0;
var lr = sheet.getLastRow();
for (var i = 1; i <= lr; i++) {
var rid = sheet.getRange(i, 2).getValue();
if (rid == id) {
sheet.deleteRow(i);
var result = "value deleted successfully";
flag = 1;
}
}
if(flag==0)
var result="id not found";
result = JSON.stringify({
"result": result
});
return ContentService
.createTextOutput(request.parameter.callback + "(" + result + ")")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
HTML script section
<script>
var script_url = "apps script webapp url";
// Make an AJAX call to Google Script
function insert_value() {
$("#re").css("visibility","hidden");
document.getElementById("loader").style.visibility = "visible";
$('#mySpinner').addClass('spinner');
var id1 = $("#id").val();
var name = $("#name").val();
var url = script_url + "?callback=ctrlq&name=" + name + "&id=" + id1 + "&action=insert";
var request = jQuery.ajax({
crossDomain: true,
url: url ,
method: "GET",
dataType: "jsonp"
});
}
function update_value(){
$("#re").css("visibility","hidden");
document.getElementById("loader").style.visibility = "visible";
var id1 = $("#id").val();
var name = $("#name").val();
var url = script_url + "?callback=ctrlq&name=" + name + "&id=" + id1 + "&action=update";
var request = jQuery.ajax({
crossDomain: true,
url: url ,
method: "GET",
dataType: "jsonp"
});
}
function delete_value(){
$("#re").css("visibility","hidden");
document.getElementById("loader").style.visibility = "visible";
$('#mySpinner').addClass('spinner');
var id1 = $("#id").val();
var name = $("#name").val();
var url = script_url + "?callback=ctrlq&name=" + name + "&id=" + id1 + "&action=delete";
var request = jQuery.ajax({
crossDomain: true,
url: url ,
method: "GET",
dataType: "jsonp"
});
}
// print the returned data
function ctrlq(e) {
$("#re").html(e.result);
$("#re").css("visibility","visible");
read_value();
}
function read_value() {
// Other code here to process the result
}
</script>
HTML function calls:
<body>
<div align="center">
<h1>Operations .</h1>
<p>This is simple application<p>
<form >
ID
<input type = "text" name ="id" id="id">
Name
<input type = "text" name ="name" id="name">
</form>
<div id="loader"></div>
<p id="re"></p>
<input type = "button" id = "b1" onClick="insert_value()" value = "Insert"></input>
<input type="button" onclick="read_value()" value="Read" />
<input type="button" onclick="update_value()" value="Update" />
<input type="button" onclick="delete_value()" value="Delete" />
<div id="showData"></div>
</div>
</body>
<div align="center">
</div>
<html>
It should show web page for performing operation shown in screenshot
web page:
The error is correct - consider what happens when no parameters are given to your webapp function doGet. This scenario occurs when the webapp is visited via browser. To resolve this error, you must return something:
function doGet(e) {
console.log({event: e});
...
if (op)
return ContentService.createTextOutput("Unsupported operation");
else
return HtmlService.createHtmlOutputFromFile(...);
}
}
The default response will vary based on how your webapp is used - if you intend to use the webapp only as a crud backend vs. if you plan to have it host the web interface as well.
If the webapp is to host the interface as well, your HTML should use the built in client-server communication (google.script.run...) rather than jQuery.ajax. Review the Apps Script documentation in detail. This pattern will allow you to report and even handle errors in your server code execution.