Google script - Service error: Spreadsheets - javascript

As of a couple of weeks now, I'm getting a "Service error: Spreadsheets" error when running a script in Google Sheets. Before that, it was working fine.
It's supposed to export a PDF while deleting some of the empty rows.
I tried adding a 1000ms delay, but that didn't work.
Any help would be greatly appreciated. You can find the code below.
var sheetName = "PDF - B";
var sheetName2 = "Offertes";
var folderID = "XXX"; // Folder id to save in a folder.
var sourceSpreadsheet = SpreadsheetApp.getActive();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
var sourceSheet2 = sourceSpreadsheet.getSheetByName(sheetName2);
var folder = DriveApp.getFolderById(folderID);
var invoiceName = sourceSheet.getRange("B57").getValue();
var invoiceDate = sourceSheet.getRange("H57").getValue();
var invoiceBusnr = sourceSheet.getRange("J1").getValue();
var invoiceDate_2 = Utilities.formatDate(invoiceDate, "CET", "yyyy-MM-dd")
var pdfName = "["+invoiceDate_2+"] "+invoiceName+" - Offerte "+invoiceBusnr+" Ventje";
var fact = sourceSheet.getRange("H60").getValue();
var value_f1 = sourceSheet.getRange("I53").getValue();
var value_f2 = sourceSheet.getRange("I54").getValue();
var buyerRow = sourceSheet.getRange("I61").getValue();
var buyerColumn = sourceSheet.getRange("J61").getValue();
var vColumn = sourceSheet.getRange("G61").getValue();
var f_cell = sourceSheet2.getRange(buyerRow,buyerColumn);
var f_version = sourceSheet2.getRange(buyerRow,vColumn);
if (fact=="F1") {
f_cell.setValue(value_f1);
f_version.setValue(2);
}
if (fact=="F2") {
f_cell.setValue(value_f2);
}
//Copy whole spreadsheet
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder));
//repace cell values with text (to avoid broken references)
var destSheet = destSpreadsheet.getSheets()[0]
var destSheet = destSpreadsheet.getSheetByName(sheetName)
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
var imageSheet = destSpreadsheet.getSheetByName("Bus foto's");
var imageRow = destSheet.getRange("B1").getValue();
var sourceImage_1 = imageSheet.getRange(imageRow, 2, 1, 1);
var destImage_1 = destSheet.getRange(4, 2, 1, 1);
var sourceImage_2 = imageSheet.getRange(imageRow, 3, 1, 1);
var destImage_2 = destSheet.getRange(4, 7, 1, 1);
var sourceImage_3 = imageSheet.getRange(imageRow, 4, 1, 1);
var destImage_3 = destSheet.getRange(6, 2, 1, 1);
var sourceImage_4 = imageSheet.getRange(imageRow, 5, 1, 1);
var destImage_4 = destSheet.getRange(6, 7, 1, 1);
sourceImage_1.copyTo(destImage_1,SpreadsheetApp.CopyPasteType.PASTE_VALUES,false);
sourceImage_2.copyTo(destImage_2,SpreadsheetApp.CopyPasteType.PASTE_VALUES,false);
sourceImage_3.copyTo(destImage_3,SpreadsheetApp.CopyPasteType.PASTE_VALUES,false);
sourceImage_4.copyTo(destImage_4,SpreadsheetApp.CopyPasteType.PASTE_VALUES,false);
//delete redundant sheets
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (i % 10 == 0) { Utilities.sleep(3000); } // Pause the function for 3000 milliseconds after 10 iterations
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
//delete redundant rows
var soverig14 = destSheet.getRange("B50").getValue();
var soverig13 = destSheet.getRange("B49").getValue();
var soverig12 = destSheet.getRange("B48").getValue();
var soverig11 = destSheet.getRange("B47").getValue();
var soverig10 = destSheet.getRange("B46").getValue();
var soverig9 = destSheet.getRange("B45").getValue();
var soverig8 = destSheet.getRange("B44").getValue();
var soverig7 = destSheet.getRange("B43").getValue();
var soverig6 = destSheet.getRange("B42").getValue();
var soverig5 = destSheet.getRange("B41").getValue();
var soverig4 = destSheet.getRange("B40").getValue();
var soverig3 = destSheet.getRange("B39").getValue();
var soverig2 = destSheet.getRange("B38").getValue();
var soverig1 = destSheet.getRange("B37").getValue();
var soverig_t = destSheet.getRange("B36").getValue();
var soverig_m1 = destSheet.getRange("B35").getValue();
var sschuif = destSheet.getRange("B34").getValue();
var boverig3 = destSheet.getRange("B26").getValue();
var boverig2 = destSheet.getRange("B25").getValue();
var boverig1 = destSheet.getRange("B24").getValue();
var boverig0 = destSheet.getRange("B23").getValue();
var boverig_t = destSheet.getRange("B22").getValue();
var boverig_m1 = destSheet.getRange("B21").getValue();
var f1 = destSheet.getRange("B53").getValue();
var f2 = destSheet.getRange("B54").getValue();
var f3 = destSheet.getRange("B60").getValue();
var f4 = destSheet.getRange("B61").getValue();
if (f4=="") {
destSheet.deleteRow(61);
}
if (f3=="") {
destSheet.deleteRow(60);
}
if (f2=="") {
destSheet.deleteRow(54);
}
if (f1=="") {
destSheet.deleteRow(53);
}
if (soverig14=="") {
destSheet.deleteRow(50);
}
if (soverig13=="") {
destSheet.deleteRow(49);
}
if (soverig12=="") {
destSheet.deleteRow(48);
}
if (soverig11=="") {
destSheet.deleteRow(47);
}
if (soverig10=="") {
destSheet.deleteRow(46);
}
if (soverig9=="") {
destSheet.deleteRow(45);
}
if (soverig8=="") {
destSheet.deleteRow(44);
}
if (soverig7=="") {
destSheet.deleteRow(43);
}
if (soverig6=="") {
destSheet.deleteRow(42);
}
if (soverig5=="") {
destSheet.deleteRow(41);
}
if (soverig4=="") {
destSheet.deleteRow(40);
}
if (soverig3=="") {
destSheet.deleteRow(39);
}
if (soverig2=="") {
destSheet.deleteRow(38);
}
if (soverig1=="") {
destSheet.deleteRow(37);
}
if (soverig_t=="") {
destSheet.deleteRow(36);
}
if (soverig_m1=="") {
destSheet.deleteRow(35);
}
if (sschuif=="") {
destSheet.deleteRow(34);
}
if (boverig3=="") {
destSheet.deleteRow(26);
}
if (boverig2=="") {
destSheet.deleteRow(25);
}
if (boverig1=="") {
destSheet.deleteRow(24);
}
if (boverig0=="") {
destSheet.deleteRow(23);
}
if (boverig_t=="") {
destSheet.deleteRow(22);
}
if (boverig_m1=="") {
destSheet.deleteRow(21);
}
// export url
var url = 'https://docs.google.com/spreadsheets/d/'+destSpreadsheet.getId()+'/export?exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=A4' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true' // fit to page width, false for actual size
+ '&scale=4' // 1 = Normal 100% -- 2 = Fit to width -- 3 = Fit to height -- 4 = Fit to Page
//+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
// + '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
//+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&horizontal_alignment=CENTER' // LEFT/CENTER/RIGHT
//+ '&vertical_alignment=MIDDLE' // TOP/MIDDLE/BOTTOM
// SET ALL MARGINS IN ORDER FOR IT TO WORK
+ '&top_margin=0.4' // set top margin
+ '&bottom_margin=0.4' // set bottom margin
+ '&left_margin=0.7' // set left margin
+ '&right_margin=0.7' // set right margin
+ '&gid='+destSheet.getSheetId(); // the sheet's Id
var token = ScriptApp.getOAuthToken();
// request export url
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
var theBlob = response.getBlob().setName(pdfName+'.pdf');
// delete pdf if already exists
var files = folder.getFilesByName(pdfName);
while (files.hasNext())
{
files.next().setTrashed(true);
}
// create pdf
var newFile = folder.createFile(theBlob);
// return true;
//Delete the temporary sheet
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}

I have also noticed "Service error: Spreadsheets" when trying to create a pdf; this started occurring sometime between 7 April and 28 April.
I have narrowed it down to to a column in my spreadsheet which contains images using the =IMAGE() function. If these are copied using Range.setValues() the "Service error" occurs. This in itself seems to me to be a bug as it's not very informative about what's wrong. It's arguable whether the =IMAGE() should be copyable using setValues()
However, I've discovered it is possible to copy the image formula using Range.setFormulas(), and it's also possible to use Range.setValues() on an image created using menu>insert>image but setValues() does not work if the image is created from a script using SpreadsheetApp.newCellImage()
The last mentioned problem means there is no bypass for scripts to avoid "Service error: spreadsheets" for scripts without some fiddly recoding.
Here's a shared spreadsheet which illustrates the problem: https://docs.google.com/spreadsheets/d/1xJoOf0DJBRUTbaskjKsly3pAGNJ72sT_EV8Tn246Ufo/edit?usp=sharing
"Service error" is also reported on the google issuetracker

Based on the comment of #Tyler2P, I created a workaround, replacing the part from //repace cell values with text with the following code:
var destSheet = destSpreadsheet.getSheets()[0];
var destSheet = destSpreadsheet.getSheetByName(sheetName);
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourceRange1 = sourceSheet.getRange(1,1,3,sourceSheet.getMaxColumns());
var sourceRange2 = sourceSheet.getRange(7,1,(sourceSheet.getMaxRows()-7),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var sourcevalues1 = sourceRange1.getValues();
var sourcevalues2 = sourceRange2.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
var destRange1 = destSheet.getRange(1,1,3,sourceSheet.getMaxColumns());
var destRange2 = destSheet.getRange(7,1,(sourceSheet.getMaxRows()-7),sourceSheet.getMaxColumns());
destRange1.setValues(sourcevalues1);
destRange2.setValues(sourcevalues2);

Related

Adding separate artboard for each layer based on the content width using Illustrator scripting

I have already written a code to extract content from a csv file and add each chunk of text on each layer of illustrator. At the moment I could add content for fixed artboard. But I need to add separate art board while keeping the width 23mm. So, artboard height similar to content height of the particular layer. I'm getting an error "TypeError: text.parentArtboard is undefined"
Kindly help me to resolve the above issue.
var csv = '''clipName,Trans,fname
A1 ,test Text1,A1_ENG
A2 ,Pigment dyed fabric w.,A2_ENG
A3 ,UPF 50+ fabric. Only covered areas are protected. To maintain this level of protection the garment must be rinsed in fresh water after each use.,A3_ENG
A4 ,Light colours may become transparent when wet,A4_ENG'''
/* var csv_file = File.openDialog();
csv_file.open("r")
var csv = csv_file.read();
csv_file.close() */
var lines = csv.split("\n");
// MAIN -------------------------------------------------------------
// make character styles
var FONT1 = make_style("font1", "ArialMT", 5.5);
// process lines
for (var i=1; i<lines.length; i++) {
var data = get_data_from(lines[i]);
make_layer(data.name)
var text = make_text(data.contents);
apply_styles(text);
put_in_center(text);
// Create a new artboard
//artboard()
}
// END
// functions --------------------------------------------------------
function make_style(style_name, font_name, size) {
// try to add a new style
try { var style = app.activeDocument.characterStyles.add(style_name) }
// or pick a style with the same name if it exists already
catch(e) { var style = app.activeDocument.characterStyles.getByName(style_name) }
//var style = app.activeDocument.characterStyles.add(style_name);
style.characterAttributes.size = size;
style.characterAttributes.textFont = textFonts.getByName(font_name);
return style;
}
function addArtboard(text, artboardWidth ) {
// Get all layers in the text
var layers = text.layers;
// Set the artboard width to 23
var artboard = text.parentArtboard;
artboard.width = artboardWidth;
// Declare variable to keep track of content height
var contentHeight = 0;
// Loop through all layers
for (var i = 0; i < layers.length; i++) {
var layer = layers[i];
// Position the layer
layer.x = 0;
layer.y = contentHeight;
// Update content height
contentHeight += layer.height;
}
// Set the artboard height based on the content height
artboard.height = contentHeight;
}
function get_data_from(line) {
var arr = line.split(",");
var fname = arr[2]
var VL1 = arr[0];
var VL2 = arr[1];
//alert("Your message here...", fname);
//return {"name":Fname, "contents":[VL1, VL2, VL3, VL4,VL5, VL6, VL7, VL8, VL9]};
return {"name":fname, "contents":[VL2]};
}
//string.includes(substring)
function rearrangeText(text, artboardWidth) {
// Split the text into words
var words = text.split(" ");
var rearrangedText = "";
var currentLine = "";
// Loop through each word
for (var i = 0; i < words.length; i++) {
var word = words[i];
// If the current line + the next word would exceed the artboard width
if (currentLine.length + word.length > artboardWidth) {
// Add the current line to the rearranged text
rearrangedText += currentLine + "\n";
// Reset the current line
currentLine = "";
}
// Add the word to the current line
currentLine += word + " ";
}
// Add the last line to the rearranged text
rearrangedText += currentLine;
return rearrangedText;
}
function make_layer(layer_name) {
var new_layer = app.activeDocument.layers.add();
new_layer.name = layer_name;
}
function make_text(array) {
var text = app.activeDocument.textFrames.add();
text.contents = rearrangeText(array.join("\n"),23);
addArtboard(text.contents,23) //Try to add new artboar based on the text content size
return text;
}
function artboard() {
var layers = app.activeDocument.layers;
var textLayers = [];
for (var i = 0; i < layers.length; i++) {
if (layers[i].kind == LayerKind.TEXT) {
textLayers.push(layers[i]);
}
}
var width = 23; // width in mm
for (var i = 0; i < textLayers.length; i++) {
var textLayer = textLayers[i];
var textBounds = textLayer.bounds;
var artboardWidth = width * app.activeDocument.rulerUnits;
var artboardHeight = textBounds[3] - textBounds[1];
var artboard = app.activeDocument.artboards.add(textBounds);
artboard.width = artboardWidth;
artboard.height = artboardHeight;
textLayer.move(artboard);
}
}
function to_center(artboard, item){
var artboard_x = artboard.artboardRect[0] + artboard.artboardRect[2];
var artboard_y = artboard.artboardRect[1] + artboard.artboardRect[3];
var x = (artboard_x - item.width)/2;
var y = (artboard_y + item.height)/2;
item.position = [x, y];
}
function apply_styles(text) {
// not the best piece of code, I'm sure it can be done better
text.textRange.paragraphAttributes.justification = Justification.CENTER;
FONT1.applyTo(text.textRange);
}
function put_in_center(obj) {
var rect = app.activeDocument.artboards[0].artboardRect;
var page_w = rect[2] - rect[0];
var page_h = rect[1] - rect[3];
var shift_x = page_w/2 - obj.width/2;
var shift_y = -page_h/2 + obj.height/2;
obj.position = [rect[0] + shift_x, rect[1] + shift_y];
}
When I remove the function addArtboard(text, artboardWidth ) all content will be nicely rearrange based on the width of the artboard. But, artboard size doesn't change due to the code error.
Experts kindly help me with this.
Image of the content arrangement on illustrator layers are mention below
If understand the task correctly you can add this function into the code:
function distribute_texts_and_create_artboards() {
const mm = 2.83465; // mm/pt
var shift_x = 30 * mm; // horizontal shifs for texts
var width = 25 * mm; // artboard width
var doc = app.activeDocument;
var frames = doc.textFrames;
// move all the frames and create an artboard for the every text frame
for (var i=0; i<frames.length; i++) {
var frame = frames[i];
frame.translate(shift_x * i, 0); // move the frame horizontally
var frame_x1 = frame.geometricBounds[0];
var frame_y1 = frame.geometricBounds[1];
var frame_x2 = frame.geometricBounds[2];
var frame_y2 = frame.geometricBounds[3];
var artboard_x = frame_x1 - (width - frame.width)/2;
var artboard_y = frame_y1;
var artboard_w = frame_x2 + (width - frame.width)/2;
var artboard_h = frame.height;
var artboard_rect = [artboard_x, artboard_y, artboard_w, -artboard_h];
var new_ab = doc.artboards.add(artboard_rect); // create a new artboard
new_ab.name = frame.parent.name; // artboard name = layer name
// frame.position = [frame.position[0], frame.position[1]/2]; // to centred the frame vertically
}
doc.artboards[0].remove(); // delete the old first artboard
}
and call it at the end of the 'MAIN' section:
...
}
distribute_texts_and_create_artboards();
// END
...
and comment out the line:
// put_in_center(text);
It should give you the layout as follows:
Probably it makes sense to tweak the vertical position of the frames of the artboards a bit. But since you haven't provided an example of desired result I left it as is, for now. Let me know if you need it or uncomment the line:
frame.position = [frame.position[0], frame.position[1]/2];
And you don't need the functions addArtboard() and artboard() anymore in the code.
Update
Here is the function that saves all the text frames from layers as separate PDFs into the same folder as current AI file. File names are the same as layer names.
function save_layers_as_pdfs() {
var doc = app.activeDocument;
// pdf options
var options = new PDFSaveOptions();
options.compatibility = PDFCompatibility.ACROBAT5;
options.generateThumbnails = true;
options.preserveEditability = false;
options.preset = "[Smallest File Size]";
var mm = 2.83465; // mm/pt
var width = 25 * mm; // artboard width
var layers = doc.layers;
for (var i=0; i<layers.length; i++) {
var layer = layers[i];
layer.hasSelectedArtwork = true;
if (app.selection.length == 0) continue;
app.copy();
app.selection = null;
// create a new file
var file = File(doc.path + '/' + layer.name + '.pdf');
var new_doc = app.documents.add();
app.paste();
// change the artboard size
var frame = new_doc.textFrames[0];
var frame_x1 = frame.geometricBounds[0];
var frame_y1 = frame.geometricBounds[1];
var frame_x2 = frame.geometricBounds[2];
var frame_y2 = frame.geometricBounds[3];
var artboard_x = frame_x1 - (width - frame.width)/2;
var artboard_y = frame_y1;
var artboard_w = frame_x1 + frame.width + (width - frame.width)/2;
var artboard_h = frame_y1 - frame.height;
doc.artboards[0].artboardRect = [artboard_x, artboard_y, artboard_w, artboard_h];
// save a pdf and close document
new_doc.saveAs(file, options);
new_doc.close(SaveOptions.DONOTSAVECHANGES);
}
}
You can call this function at the end of the MAIN section.
The result:
And make sure your csv data is correct: the separators, quote marks, tabulations, etc.

How to set a time-driven (clock) trigger for a function that needs parameters sent to it?

I'm trying to have a function run with a time-driven (clock) trigger. My issue is, the function needs variables sent to it as parameters. Normally if I'm not using a trigger I'll just send it directly like this.
<script>
function runFunction(){
google.script.run.myFunction(x,y,z);
}
</script>
And on the server side, I'll call them easily.
function myFunction(x,y,z){
var a = x;
var b = y;
var c = z;
Logger.log(a+b+c);
}
But when I'm using a time-driven (clock) trigger to run the function how can I get x,y,z into the function.
I searched around and saw one method of creating scriptProperties out of the parameters in the trigger function like this.
EDITED This is the actual code.
Client side.
<script>
function sendCall() {
var testNumber = document.getElementById('numberCall').value;
var testGroup = document.getElementById('groupsCall').value;
var testtime = document.getElementById('scheduleCall').value;
var now = document.getElementsByName('sendTimeCall')[0].checked;
var number;
if(testNumber == ''){
number = null;
}else{
number = testNumber;
}
var group;
if(testGroup == ""){
group = null;
}else{
group = testGroup;
}
var time;
if(testtime == ''){
time = null;
}else{
time = testtime;
}
var file = document.getElementsByName('audio')[0].files[0];
var name = file.name;
var reader = new FileReader();
reader.onload = function (e) {
var content = reader.result;
google.script.run.withSuccessHandler(success2).triggerCall(group, number, content, time, now, name);
return false;
}
reader.readAsDataURL(file);
}
</script>
Server side - Trigger Function.
function triggerCall(group, number, content, time, now, name){
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperties({
'GROUP_CALL': group,
'AUDIO': content,
'NUMBER_CALL': number,
'FILE_NAME': name
});
var status;
if(now){
status = 'Call Sent';
}else{
status = 'Call Scheduled';
}
if(now){
return makeCall(status);
}else{
// Set here the date you want to schedule the one-time trigger
var rawdate = time;
var today_D = new Date(new Date().toLocaleString("en-US", {timeZone: "America/New_York"}));
var scheduled_D = new Date(rawdate);
var time_af = Math.abs(scheduled_D - today_D) / 36e5;
ScriptApp.newTrigger("makeCall")
.timeBased()
.after(time_af * 60 *60 * 1000)
.create();
}
return status;
}
Server side - Here is The function that actually does the work.
function makeCall(status) {
var scriptProperties = PropertiesService.getScriptProperties();
var blob = scriptProperties.getProperty('AUDIO');
var number = scriptProperties.getProperty('NUMBER_CALL');
var group = scriptProperties.getProperty('GROUP_CALL');
var name = scriptProperties.getProperty('FILE_NAME');
var fullNumber;
if(group){
var ss = SpreadsheetApp.openById('xxxxxxxxxxxxxxxxxxx');
var sheet = ss.getSheetByName(group)
var length = sheet.getLastRow();
var values = sheet.getRange(1, 1, length).getValues();
fullNumber = values.flat();
}else{
var num = number;
var prefix = '+1';
var removeDashes = num.replace(/-/g,"");
var format = prefix + removeDashes;
var comma = format.replace(/ /g, ' +1');
fullNumber = comma.split(' ')
}
//upload file to drive
var folder = DriveApp.getFolderById('xxxxxxxxxxxxxxxxxxxxxx');
var blob = blob.split(",");
var blob = Utilities.newBlob(Utilities.base64Decode(blob[1]), 'audio/mpeg', name);
var file = folder.createFile(blob);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
var id = file.getId();
for (var i = 0; i < fullNumber.length; i++){
//the url with HTTP request to create a call and parameters
var callsUrl = "https://api.twilio.com/2010-04-01/Accounts/ACxxxxxxxxxxxxxxxx/Calls.json";
var payload = {
"To": fullNumber[i],
"From" : "+177777777",
"Twiml" : "<Response><Play>https://docs.google.com/uc?export=play&id=" + id + "</Play></Response>",
};
var options = {
"method" : "post",
"payload" : payload
};
options.headers = {
"Authorization" : "Basic " + Utilities.base64Encode("xxxxxxxxxxx:xxxxxxxx")
};
UrlFetchApp.fetch(callsUrl, options);
}
scriptProperties.deleteProperty('AUDIO');
scriptProperties.deleteProperty('NUMBER_CALL');
scriptProperties.deleteProperty('GROUP_CALL');
scriptProperties.deleteProperty('FILE_NAME');
return status;
}
The problem is when I run the above code without the file input it works, But when I run it as above the function doesn't work. I did some trouble shooting and I think it has to do with transferring the file as a Data URL via the properties method. Is there a limit to how long of a string the VALUE can be?
in a nut shell these are the 2 points of my question.
Any other ideas how to send parameters to a triggered function
How I could do it using PropertiesService.
I would like to propose the following modification.
Modification points:
I think that the reason of your issue might be due to the maximum data size for PropertiesService. In the current stage, it seems that "Properties total storage" is "500kB / property store". I thought that in this case, when you upload a file, the file size might be over than it.
In order to remove your issue, how about creating content as a temporal file and put the file ID to the PropertiesService?
When above points are reflected to your script, it becomes as follows.
Pattern 1:
In this pattern, content is saved as a temporal file and that is used in the function of makeCall().
triggerCall()
function triggerCall(group, number, content, time, now, name){
var scriptProperties = PropertiesService.getScriptProperties();
var tempFile = DriveApp.createFile("tempFile.txt", content, MimeType.PLAIN_TEXT); // Added
scriptProperties.setProperties({'GROUP_CALL': group,'AUDIO': tempFile.getId(),'NUMBER_CALL': number,'FILE_NAME': name}); // Modified
var status;
if(now){
status = 'Call Sent';
}else{
status = 'Call Scheduled';
}
if(now){
return makeCall(status);
}else{
var rawdate = time;
var today_D = new Date(new Date().toLocaleString("en-US", {timeZone: "America/New_York"}));
var scheduled_D = new Date(rawdate);
var time_af = Math.abs(scheduled_D - today_D) / 36e5;
ScriptApp.newTrigger("makeCall").timeBased().after(time_af * 60 *60 * 1000).create();
}
return status;
}
makeCall()
function makeCall(status) {
var scriptProperties = PropertiesService.getScriptProperties();
var tempfileId = scriptProperties.getProperty('AUDIO'); // Modified
var number = scriptProperties.getProperty('NUMBER_CALL');
var group = scriptProperties.getProperty('GROUP_CALL');
var name = scriptProperties.getProperty('FILE_NAME');
var fullNumber;
if(group){
var ss = SpreadsheetApp.openById('xxxxxxxxxxxxxxxxxxx');
var sheet = ss.getSheetByName(group)
var length = sheet.getLastRow();
var values = sheet.getRange(1, 1, length).getValues();
fullNumber = values.flat();
}else{
var num = number;
var prefix = '+1';
var removeDashes = num.replace(/-/g,"");
var format = prefix + removeDashes;
var comma = format.replace(/ /g, ' +1');
fullNumber = comma.split(' ')
}
var folder = DriveApp.getFolderById('xxxxxxxxxxxxxxxxxxxxxx');
var tempFile = DriveApp.getFileById(tempfileId); // Added
var text = tempFile.getBlob().getDataAsString(); // Added
tempFile.setTrashed(true); // Added
var blob = text.split(","); // Modified
var blob = Utilities.newBlob(Utilities.base64Decode(blob[1]), 'audio/mpeg', name);
var file = folder.createFile(blob);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
var id = file.getId();
for (var i = 0; i < fullNumber.length; i++){
var callsUrl = "https://api.twilio.com/2010-04-01/Accounts/ACxxxxxxxxxxxxxxxx/Calls.json";
var payload = {"To": fullNumber[i],"From" : "+177777777","Twiml" : "<Response><Play>https://docs.google.com/uc?export=play&id=" + id + "</Play></Response>"};
var options = {"method" : "post","payload" : payload};
options.headers = {"Authorization" : "Basic " + Utilities.base64Encode("xxxxxxxxxxx:xxxxxxxx")};
UrlFetchApp.fetch(callsUrl, options);
}
scriptProperties.deleteProperty('AUDIO');
scriptProperties.deleteProperty('NUMBER_CALL');
scriptProperties.deleteProperty('GROUP_CALL');
scriptProperties.deleteProperty('FILE_NAME');
return status;
}
Pattern 2:
In this pattern, content is saved to a file as the decoded data and that is used in the function of makeCall().
triggerCall()
function triggerCall(group, number, content, time, now, name){
var scriptProperties = PropertiesService.getScriptProperties();
var folder = DriveApp.getFolderById('xxxxxxxxxxxxxxxxxxxxxx'); // Added
var blob = content.split(","); // Added
var blob = Utilities.newBlob(Utilities.base64Decode(blob[1]), 'audio/mpeg', name); // Added
var file = folder.createFile(blob); // Added
scriptProperties.setProperties({'GROUP_CALL': group,'AUDIO': file.getId(),'NUMBER_CALL': number,'FILE_NAME': name}); // Modified
var status;
if(now){
status = 'Call Sent';
}else{
status = 'Call Scheduled';
}
if(now){
return makeCall(status);
}else{
var rawdate = time;
var today_D = new Date(new Date().toLocaleString("en-US", {timeZone: "America/New_York"}));
var scheduled_D = new Date(rawdate);
var time_af = Math.abs(scheduled_D - today_D) / 36e5;
ScriptApp.newTrigger("makeCall").timeBased().after(time_af * 60 *60 * 1000).create();
}
return status;
}
makeCall()
function makeCall(status) {
var scriptProperties = PropertiesService.getScriptProperties();
var fileId = scriptProperties.getProperty('AUDIO'); // Modified
var number = scriptProperties.getProperty('NUMBER_CALL');
var group = scriptProperties.getProperty('GROUP_CALL');
var name = scriptProperties.getProperty('FILE_NAME');
var fullNumber;
if(group){
var ss = SpreadsheetApp.openById('xxxxxxxxxxxxxxxxxxx');
var sheet = ss.getSheetByName(group)
var length = sheet.getLastRow();
var values = sheet.getRange(1, 1, length).getValues();
fullNumber = values.flat();
}else{
var num = number;
var prefix = '+1';
var removeDashes = num.replace(/-/g,"");
var format = prefix + removeDashes;
var comma = format.replace(/ /g, ' +1');
fullNumber = comma.split(' ')
}
var file = DriveApp.getFileById(fileId); // Modified
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
var id = file.getId();
for (var i = 0; i < fullNumber.length; i++){
var callsUrl = "https://api.twilio.com/2010-04-01/Accounts/ACxxxxxxxxxxxxxxxx/Calls.json";
var payload = {"To": fullNumber[i],"From" : "+177777777","Twiml" : "<Response><Play>https://docs.google.com/uc?export=play&id=" + id + "</Play></Response>"};
var options = {"method" : "post","payload" : payload};
options.headers = {"Authorization" : "Basic " + Utilities.base64Encode("xxxxxxxxxxx:xxxxxxxx")};
UrlFetchApp.fetch(callsUrl, options);
}
scriptProperties.deleteProperty('AUDIO');
scriptProperties.deleteProperty('NUMBER_CALL');
scriptProperties.deleteProperty('GROUP_CALL');
scriptProperties.deleteProperty('FILE_NAME');
return status;
}
Note:
In this modified script, when a file is uploaded, content is saved as a temporal file and the file ID is put to the PropertiesService. When makeCall is run, content is retrieved by the file ID and convert to the blob and saved it as a file. And the temporal file is removed. By this, the limitation of PropertiesService can be cleared.
But, in the current stage, the maximum blob size of Google Apps Script is 50 MB (52,428,800 bytes). So when the uploaded file size is over 50 MB, an error occurs. In this case, the file is converted to the base64 data. So, the maximum size is the size of base64 data. Please be careful this.
Reference:
Current limitations

google-apps script - loops

I have a project I been working on to read data from one sheet and iterate through the designated columns grab that data and place it on a template that will be uploaded to Google ads. See the code below (A SIMPLE LOOP IS NEEDED SEE MY CODE FOR COMMENTS!). Please note that i'm new to this and it may look like an eye sore. I been stuck on this for a while now trying literally everything I found with no success. if you need additional attachments or clarification please let me know.
//============================================================================*
// This program is an inventory based program that will create AdGroups based *
// On your current inventory Spreadsheet on your G-Drive *
// ===========================================================================*
function main() {
var feedCSVname = "FILE_NAME.csv"
var feedSheetName = "INVENTORY SHEET"
var TemplateURL = "SHEET_URL_HERE"
var Template_sheetName = "TEMPLATE_SHEET_NAME"
var push_sheetName = "Upload"
var done = "Done"
// ==== Parse CSV file and dump all contents to inventory sheet =========
var feedFile = DriveApp.getFilesByName(feedCSVname).next();
var feedData = Utilities.parseCsv(feedFile.getBlob().getDataAsString());
var Template = SpreadsheetApp.openByUrl(TemplateURL);
var feedSheet = Template.getSheetByName(feedSheetName);
feedSheet.clear().getRange(1, 1, feedData.length, feedData[0].length).setValues(feedData);
// Create a new columns "Status" so we can loop through
feedSheet.getRange('BI1').setValue('Status');
//==================== Feed Column Locations ============================
var CidColumn = 2;
var sellingPriceColumn = 10;
var yearColumn = 13;
var makeColumn = 14;
var modelColumn = 15;
var trimColumn = 16;
var dealernameColumn = 39;
var countofmakemodelyearColumn = 47;
var lowest_priceColumn = 52;
var mm_lowest_priceColumn = 53;
var m_lowest_priceColumn = 54;
var URL1Column = 58;
var URL2Column = 59;
var URL3Column = 60;
var status = 61;
var feedRows = 2 // Since row 1 has headers
var lastrow = feedSheet.getLastRow();
//======================= Feed Row Data ================================
var sellingPrice = feedSheet.getRange(feedRows, sellingPriceColumn).getValue();
var year = feedSheet.getRange(feedRows, yearColumn).getValue();
var make = feedSheet.getRange(feedRows, makeColumn).getValue();
var model = feedSheet.getRange(feedRows, modelColumn).getValue();
var dealername = feedSheet.getRange(feedRows, dealernameColumn).getValue();
var URL1 = feedSheet.getRange(feedRows, URL1Column).getValue();
var URL2 = feedSheet.getRange(feedRows, URL2Column).getValue();
var URL3 = feedSheet.getRange(feedRows, URL3Column).getValue();
//==================== Write Data To Template ==========================
// I need to loop this process
var templateSheet = Template.getSheetByName(Template_sheetName);
templateSheet.getRange('B2').setValue("Campaign Name");
templateSheet.getRange('B3').setValue(make);
templateSheet.getRange('B4').setValue(model);
templateSheet.getRange('B5').setValue(year);
templateSheet.getRange('D2').setValue(URL1);
templateSheet.getRange('D3').setValue(URL2);
templateSheet.getRange('D4').setValue(URL3);
feedSheet.getRange(feedRows, status).setValue(done);
// when this column and row = "Done" then go to next row
var readStatusColumn = feedSheet.getRange(feedRows, status).getValue();
if (readStatusColumn == done) {
//Then go to next row loop process
}
//================ Copy all data to push sheet =========================
//Copy contents of the Template sheet and paste to the Upload sheet
var copyRange = templateSheet.getRange('A8:J113');
var pushSheet = Template.getSheetByName(push_sheetName);
var paste2pushSheet = pushSheet.getRange(pushSheet.getLastRow() + 1, 1);
copyRange.copyTo(paste2pushSheet, {
contentsOnly: true
});
//End Loop
}

Scrape part of web page html and test it against condition

This is part of the html code from "view page source" I want to scrape
<span>Loyalty cijena</span>
<strong>863,84 KN</strong>
This is my code to scrape part which is now "863" and start my function if that number is under 863. But now the function start no matter condition is set [0-862] or [0-864]. Where is my mistake?
function createBackup() {
var folder = getFolder(FOLDER_NAME);
var exportUrl = RESOURCE_URL;
var response = UrlFetchApp.fetch(exportUrl);
var htmlBody = response.getContentText();
var scraped = htmlBody.match(/Loyalty cijena<\/span>\s*<strong>(\d+),(\d+) KN<\/strong>/m)[1];
if(scraped.match(/[0-862]/)); {
createBackupFile(folder, FILE_NAME, fetchData());
}
}
Edit: This is full code
var RESOURCE_URL = 'https://www...',
BACKUP_FOLDER_ID = 'xxx',
FOLDER_NAME_FORMAT = 'yyyy-MM-dd',
FILE_NAME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss",
FILE_EXT = '.pdf',
now = new Date(),
FOLDER_NAME = Utilities.formatDate(now, 'CET', FOLDER_NAME_FORMAT),
FILE_NAME = Utilities.formatDate(now, 'CET', FILE_NAME_FORMAT) + FILE_EXT;
function createBackup() {
var folder = getFolder(FOLDER_NAME);
var exportUrl = RESOURCE_URL;
var response = UrlFetchApp.fetch(exportUrl);
var htmlBody = response.getContentText();
var scraped = htmlBody.match(/Loyalty cijena<\/span>\s*<strong>(\d+),(\d+) KN<\/strong>/m)[1];
console.log(scraped,parseInt(scraped) > 0 && parseInt(scraped) < 863); {
createBackupFile(folder, FILE_NAME, fetchData());
}
}
function getFolder(name) {
var backupFolder = getBackupFolder(),
folders = backupFolder.getFoldersByName(name);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = backupFolder.createFolder(name);
}
return folder;
}
function getBackupFolder() {
return DriveApp.getFolderById(BACKUP_FOLDER_ID);
}
function createBackupFile(folder, filename, data, overwrite) {
if (overwrite) {
var existingFiles = folder.getFilesByName(filename);
while (existingFiles.hasNext()) {
var file = existingFiles.next();
folder.removeFile(file);
}
}
}
function fetchData() {
var exportUrl = RESOURCE_URL;
var response = UrlFetchApp.fetch(exportUrl);
var htmlBody = response.getContentText();
var blob = Utilities.newBlob(htmlBody, 'text/html').getAs('application/pdf').setName(FILE_NAME);
return folder.createFile(blob);
}
The pattern [0-862] matches text that ends with 62 and where the first symbol is 0 to 8, so for example 562 but not 561. In your case you should compare the number represented by those characters, instead of matching against a regular expression.
Don't use regex to match a value:
var htmlBody = `<span>Loyalty cijena</span>
<strong>863,84 KN</strong>`
var scraped = htmlBody.match(/Loyalty cijena<\/span>\s*<strong>(\d+),(\d+) KN<\/strong>/m)[1];
console.log(scraped,
parseInt(scraped) > 0 && parseInt(scraped) <= 863); // false if you change to < 863

Google script - parse HTML from Website Forum - and Write Data to Sheet

I'm getting HTML from a forum url, and parsing the post count of the user from their profile page. I don't know how to write the parsed number into the Google spreadsheet.
It should go account by account in column B till last row and update the column A with count.
The script doesn't give me any errors, but it doesn't set the retrieved value into the spreadsheet.
function msg(message){
Browser.msgBox(message);
}
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu("Update")
.addItem('Update Table', 'updatePosts')
.addToUi();
}
function getPostCount(profileUrl){
var html = UrlFetchApp.fetch(profileUrl).getContentText();
var sliced = html.slice(0,html.search('Posts Per Day'));
sliced = sliced.slice(sliced.search('<dt>Total Posts</dt>'),sliced.length);
postCount = sliced.slice(sliced.search("<dd> ")+"<dd> ".length,sliced.search("</dd>"));
return postCount;
}
function updatePosts(){
if(arguments[0]===false){
showAlert = false;
} else {
showAlert=true;
}
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var accountSheet = spreadSheet.getSheetByName("account-stats");
var statsLastCol = statsSheet.getLastColumn();
var accountCount = accountSheet.getLastRow();
var newValue = 0;
var oldValue = 0;
var totalNewPosts = 0;
for (var i=2; i<=accountCount; i++){
newValue = parseInt(getPostCount(accountSheet.getRange(i, 9).getValue()));
oldValue = parseInt(accountSheet.getRange(i, 7).getValue());
totalNewPosts = totalNewPosts + newValue - oldValue;
accountSheet.getRange(i, 7).setValue(newValue);
statsSheet.getRange(i,statsLastCol).setValue(newValue-todaysValue);
}
if(showAlert==false){
return 0;
}
msg(totalNewPosts+" new post found!");
}
function valinar(needle, haystack){
haystack = haystack[0];
for (var i in haystack){
if(haystack[i]==needle){
return true;
}
}
return false;
}
The is the first time I'm doing something like this and working from an example from other site.
I have one more question. In function getPostCount I send the function profileurl. Where do I declare that ?
Here is how you get the URL out of the spreadsheet:
function getPostCount(profileUrl){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var thisSheet = ss.getSheetByName("List1");
var getNumberOfRows = thisSheet.getLastRow();
var urlProfile = "";
var sliced = "";
var A_Column = "";
var arrayIndex = 0;
var rngA2Bx = thisSheet.getRange(2, 2, getNumberOfRows, 1).getValues();
for (var i = 2; i < getNumberOfRows + 1; i++) { //Start getting urls from row 2
//Logger.log('count i: ' + i);
arrayIndex = i-2;
urlProfile = rngA2Bx[arrayIndex][0];
//Logger.log('urlProfile: ' + urlProfile);
var html = UrlFetchApp.fetch(urlProfile).getContentText();
sliced = html.slice(0,html.search('Posts Per Day'));
var postCount = sliced.slice(sliced.search("<dd> ")+"<dd> ".length,sliced.search("</dd>"));
sliced = sliced.slice(sliced.search('<dt>Total Posts</dt>'),sliced.length);
postCount = sliced.slice(sliced.search("<dd> ")+"<dd> ".length,sliced.search("</dd>"));
Logger.log('postCount: ' + postCount);
A_Column = thisSheet.getRange(i, 1);
A_Column.setValue(postCount);
};
}
You're missing var in front of one of your variables:
postCount = sliced.slice(sliced.search("<dd> ")+"<dd> ".length,sliced.search("</dd>"));
That won't work. Need to put var in front. var postCount = ....
In this function:
function updatePosts(){
if(arguments[0]===false){
showAlert = false;
} else {
showAlert=true;
}
There is no array named arguments anywhere in your code. Where is arguments defined and how is it getting any values put into it?

Categories