Google Apps Script Substring - google-apps-script

In my spreadsheet I have a file path to an image stored in a sub folder. The image is stored like this because I am making an app for my employer using appsheet.com. They want this app developed using AppSheet, which does not allow images to be stored directly into the spreadsheets that are used to build these app.
The data stored in the spreadsheet must also be extracted and applied to a template and I am making a Google Apps Script to do this. I have a functional script which finds the template and extract the users chosen data row, but I cannot extract the image needed.
I have been trying to make a substring of the file path to get the file name, but I have been unable to do this.
In the block of code where I am trying to create the substring, I am getting an error on the line sig = signature.getText();. The error is TypeError: signature.getText is not a function (line 176, file "Code").
This is what the file path looks like Signatures/FT101.Signed (%SIGNED%).103735.png, and the substring I need would look like this FT101.Signed (%SIGNED%).103735.png
I have tried multiple methods that were provided by other questions asked on stack overflow as well as any potential methods available in the Google Apps Script reference.
The following function takes the users input from a prompt and uses it to find the desired row number. It then takes the data from that row and applies it to a template based on the category the data falls under.
var response = {};
var sign = "";
function chooseRowMethod(templateId){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var data = sheet.getRange(2, 2, 11, 18).getValues();//starting with row 2 and column 1 as our upper-left most column, get values from cells from 1 row down, and 15 columns along - hence (2,1,1,15)
var docTitle = sheet.getRange(2, 2, 11, 1).getValues();//this is grabbing the data in field B2
var docTitleTagNumber = sheet.getRange(2, 3, 11, 1).getValues();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
Logger.log(i);
var row = data[i - 1];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
body.replaceText("%SITEID%", row[0]);
body.replaceText("%TAG%", row[1]);
...
body.replaceText("%SAT%", row[14]);
var signature = sheet.getRange(2, 18, 11, 1).getValues();
var sig;
var sign = {};
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
sig = signature.getText();
sign[i][0] = sig.substring(sig.indexOf("/") + 1);
}
}
}
var sigFolder = DriveApp.getFolderById("1LiJKGjTbpvRZ5RrMTQoyTuAjrozA14FN");
var file = sigFolder.getFilesByName(sign);
var image = file.getId();
body.appendImage(image);
doc.saveAndClose();
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("16wRGBVdV0OZ5YfKhqEQSFMsux-ekGCCa");
newFolder.addFile(file);
var newDocTitle = docTitle[i - 1][0];
var newDocTagNumber = docTitleTagNumber[i - 1][0];
doc.setName(newDocTitle + " " + newDocTagNumber + " " + today);
}
}
}
}
This is where I have been attempting to get the image.
var signature = sheet.getRange(2, 18, 11, 1).getValues();
var sig;
var sign = {};
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
sig = signature.getText();
sign[i][0] = sig.substring(sig.indexOf("/") + 1);
}
}
}
var sigFolder = DriveApp.getFolderById("1LiJKGjTbpvRZ5RrMTQoyTuAjrozA14FN");
var file = sigFolder.getFilesByName(sign);
var image = file.getId();
body.appendImage(image);
This next function gives the user the prompt and applies the correct template.
function chooseRow(){
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.prompt('Please enter the Tag number of the row you wish to print.', ui.ButtonSet.OK_CANCEL);
var button = result.getSelectedButton();
response = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
ui.alert('Your tag number is' + response + '.');
} else if (button == ui.Button.CANCEL) {
// User clicked X in the title bar.
ui.alert('You closed the dialog.');
return 'the end';
}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var category = sheet.getRange(2, 4, 11, 1).getValues();
var templateId = {};
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response && category[i - 1][0] == "Instrument"){
templateId = "1cx2-6ju-o7DaRPnbuYxxdvVVFeGQzpTXaXV3wMuRpqo";
chooseRowMethod(templateId);
return "";
} else if(values[i][j] == response && category[i][0] == "Motor" || values[i][j] == response && category[i][0] == "Valve"){
templateId = "1sYx_JcoDHY-pzjEDlxMMa3dtdzOOE8CyyLGQk8WHg7s";
chooseRowMethod(templateId);
return "";
}
}
}
}
The expected result is a substring of the file path that can be used to retrieve an image that can be appended to the body of a document.
Here is a link to the spreadsheet.

I did several changes to your code, including the edit proposed by Cooper. It's retrieving the substring and adding the image to the file successfully. I hope it works for you:
function chooseRow(){
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.prompt('Please enter the Tag number of the row you wish to print.', ui.ButtonSet.OK_CANCEL);
var button = result.getSelectedButton();
response = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
ui.alert('Your tag number is' + response + '.');
} else if (button == ui.Button.CANCEL) {
// User clicked X in the title bar.
ui.alert('You closed the dialog.');
return 'the end';
}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var tags = sheet.getRange(2, 4, 11, 1).getValues();
var category = sheet.getRange(2, 3, 11, 1).getValues();
for(var i = 0; i < tags.length; i++){
if(tags[i][0] == response && category[i][0] == "Instrument"){
var templateId = "my_template_id";
chooseRowMethod(templateId, i);
return ""; // You don't need to return empty string, just return null
} else if(tags[i][0] == response && category[i][0] == "Motor" || tags[i][0] == response && category[i][0] == "Valve"){
var templateId = "my_template_id_bis";
chooseRowMethod(templateId, i);
return ""; // You don't need to return empty string, just return null
}
}
}
The function chooseRowMethod gets the row index chosen by the user in chooseRow so that it doesn't have to be looked for again:
function chooseRowMethod(templateId, rowNumber){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var data = sheet.getRange(2, 2, 11, 18).getValues();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
var row = data[rowNumber];
var docTitle = row[1];
var docTitleTagNumber = row[2];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
body.replaceText("%SITEID%", row[0]);
body.replaceText("%TAG%", row[1]);
// ...
body.replaceText("%SAT%", row[14]);
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("my_sigfolder_id");
var files=sigFolder.getFilesByName(sign);
var n = 0;
while(files.hasNext()) {
var file=files.next();
n++;
} if(n>1) {
SpreadsheetApp.getUi().alert('There is more than one file with this name: ' + sign);
}
body.appendImage(file);
doc.saveAndClose();
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("my_newfolder_id");
newFolder.addFile(file);
doc.setName(docTitle + " " + docTitleTagNumber + " " + today);
}

Related

google apps script: get gmail jpg attachments & insert them into google sheets

The code finds all the jpg's from a sender and then creates an array with those images and then is supposed to insert the received images into the cells of the sheet.
Instead, the code inserts the word Blog into the cells instead of the images.
I've tried several methods to solve the problem but have not had any luck.
function importEmailsWithJPGs() {
var sheet = SpreadsheetApp.getActiveSheet();
var emailAddress = "a#gmail.com";
var threads = GmailApp.search("from:" + emailAddress + " filename:jpg");
var attachments;
var date;
var rowData = {};
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
attachments = messages[j].getAttachments();
date = messages[j].getDate();
var dateString = Utilities.formatDate(date, "GMT", "yyyy-MM-dd");
if (!rowData[dateString]) {
rowData[dateString] = [date];
}
for (var k = 0; k < attachments.length; k++) {
if (attachments[k].getContentType().indexOf("image/jpeg") !== -1) {
//var image = attachments[k].getAs('image/jpeg');
// var imageName = attachments[k].getName();
// var blob = new Blob([image], { type: 'image/jpeg' });
// rowData[dateString].push(blob);
//var image = attachments[k].getAs('image/jpeg');
// var imageName = attachments[k].getName();
// var byteArray = image.getBytes();
// var base64EncodedImage = Utilities.base64Encode(byteArray);
// var blob = Utilities.newBlob(base64EncodedImage, 'image/jpeg', imageName);
// rowData[dateString].push(blob);
var image = attachments[k].copyBlob();
var imageName = attachments[k].getName();
var blob = Utilities.newBlob(image.getBytes(), 'image/jpeg', imageName);
rowData[dateString].push(blob);
}
}
}
}
for (var key in rowData) {
sheet.appendRow(rowData[key]);
}
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
for (var j = 1; j < data[i].length; j++) {
var cell = sheet.getRange(i + 1, j + 1);
var value = data[i][j];
if (value && value.getBytes) {
//var image = value;
var image = value.getAs('image/jpeg');
//var imageData = new Uint8Array(image.getBytes());
var cellWidth = cell.getWidth();
var cellHeight = cell.getHeight();
var imageWidth = image.getWidth();
var imageHeight = image.getHeight();
var ratio = Math.min(cellWidth / imageWidth, cellHeight / imageHeight);
cell.setValue("");
// cell.setImageData(imageData);
cell.setImageData(image);
}
}
}
}
Modification points:
First, I think that the reason that the text of Blob is put to the cell is due to that appendRow cannot put the image blob to the cell.
After that line of var data = sheet.getDataRange().getValues();, I think that value && value.getBytes is always false. Because, value.getBytes is undefined, and also, there is no method of setImageData in Class Range at cell.setImageData(image);. And, an error occurs at value.getAs('image/jpeg') because value has no method.
Unfortunately, from your reply, I cannot understand the relationship between for (var key in rowData) { sheet.appendRow(rowData[key]); } and the script below var data = sheet.getDataRange().getValues();. But, if you want to just put the image data from the image blob to the cells instead of the text of Blob, how about the following modification?
In this modification, a Google Apps Script library is used. Because in the current stage, unfortunately, the image blob cannot be directly put into a cell. So, I created this library. In order to use the following modified script, please do the following flow.
Usage:
1. Install Google Apps Script library.
You can see how to install it at https://github.com/tanaikech/DocsServiceApp#how-to-install. (Author of this library: me)
2. Enable Drive API.
Please enable Drive API at Advanced Google services.
3. Modified script:
Please modify your script as follows.
From:
for (var key in rowData) {
sheet.appendRow(rowData[key]);
}
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
for (var j = 1; j < data[i].length; j++) {
var cell = sheet.getRange(i + 1, j + 1);
var value = data[i][j];
if (value && value.getBytes) {
//var image = value;
var image = value.getAs('image/jpeg');
//var imageData = new Uint8Array(image.getBytes());
var cellWidth = cell.getWidth();
var cellHeight = cell.getHeight();
var imageWidth = image.getWidth();
var imageHeight = image.getHeight();
var ratio = Math.min(cellWidth / imageWidth, cellHeight / imageHeight);
cell.setValue("");
// cell.setImageData(imageData);
cell.setImageData(image);
}
}
}
To:
var lastRow = sheet.getLastRow();
var v = Object.values(rowData);
var values = v.map(([a]) => [a]);
sheet.getRange(lastRow + 1, 1, values.length).setValues(values);
var obj = v.map(([, blob], i) => ({ blob, range: { row: lastRow + i + 1, column: 2 } }));
DocsServiceApp.openBySpreadsheetId(sheet.getParent().getId()).getSheetByName(sheet.getSheetName()).insertImage(obj);
When this script is run, the text value and the image data are put into the columns "A" and "B", respectively.
Reference:
DocsServiceApp (Author: me)

Sending an Email from google sheets only once

The code below works perfectly and sends the email I need it to send on an "OnChange" trigger. However, it sends an email for EVERY checked box in row 7 which is overload, it just needs to send an email for newly checked boxes.
Any advice on how to add a condition in the below code for this?
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Service');
var data = sheet.getDataRange().getValues();
for (var i = data.length - 1; i >= 1; i--) {
if (sheet.getRange(i,7).isChecked()){
var name = sheet.getRange(i,1).getValue();
var last = sheet.getRange(i,2).getValue();
var body = name + " " + last
var subject = 'New Service Item in Stock'
MailApp.sendEmail('me#mycompany.com', subject, body);
}
}
}
I haven't tried anything because there is nothing I could find online to get me in the right direction.
https://i.stack.imgur.com/cKfLv.png
Try unchecking them after you send them:
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Service');
var data = sheet.getDataRange().getValues();
for (var i = data.length - 1; i >= 1; i--) {
if (sheet.getRange(i, 7).isChecked() && sheet.getRange(i,8).getValue() != "Sent") {
var name = sheet.getRange(i, 1).getValue();
var last = sheet.getRange(i, 2).getValue();
var body = name + " " + last
var subject = 'New Service Item in Stock'
MailApp.sendEmail('me#mycompany.com', subject, body);
sheet.getRange(i, 8).setValue("Sent");
}
}
}
Performance improvement:
function sendEmails() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('Service');
var vs = sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn()).getValues();
vs.forEach((r, i) => {
if (sh.getRange(i + 2, 7).isChecked() && r[7] == "Sent") {
let body = `${r[0]} ${r[1]}`;
let subject = "New Service Item in Stock";
MailApp.sendEmail('me#mycompany.com', subject, body);
sh.getRange(i, 8).setValue("Sent");
}
});
}

Create an array of values that were looped through

I have a spreadsheet where each row in "MASTER" sheet is a task. It has checkboxes to check if it's done.
On another sheet, "Sin despachar", the data is filtered so that only the tasks which are not done show. There I have empty checkboxes to check when an incomplete task is completed.
I wrote a script so that when a button is pressed, it loops through the whole column in "Sin despachar" to see if checkboxes are checked, and for those that are, it changes the value in a master sheet.
I would like to have a confirmation pop up that mentions all the tasks it'll edit, but for that I need to somehow retrieve that list when the script runs, and I don't know how.
So what I need is to create an array or something else that contains the names of the tasks with the checkbox checked.
Does anyone know how to do this?
The task names are in column 4, and the checkboxes are in column 18.
Thanks in advance!!
function marcarEtiquetado() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Sin despachar");
var sheetRange = s.getDataRange();
var sheetValues = sheetRange.getValues();
for (var i=sheetRange.getLastRow()-1; i>1; i--) {
// si valor en columna R (etiquetado) = TRUE
if ( sheetValues[i][17] === true) {
var checkboxRange = s.getRange(i, 18);
var usuariosRange = s.getRange(i, 3)
Logger.log(usuariosRange)
var targetSheet = ss.getSheetByName("*MASTER*");
var targetRow = sheetValues[i][1];
var targetRange = targetSheet.getRange( targetRow, 16);
var targetTS = targetSheet.getRange( targetRow, 17);
checkboxRange.setValue(false)
targetRange.setValue(true);
targetTS.setValue(new Date()).setNumberFormat("dd-mm-yy hh:mm")
} ;
}
s.getRange(3, 18, s.getLastRow()-2).setValue(false)
}
You use alerts to confirm for every checked box individually either and action shall be carried out.
Sample:
function marcarEtiquetado() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Sin despachar");
var sheetRange = s.getDataRange();
var sheetValues = sheetRange.getValues();
var ui = SpreadsheetApp.getUi();
for (var i=sheetRange.getLastRow()-1; i>1; i--) {
// si valor en columna R (etiquetado) = TRUE
if ( sheetValues[i][17] === true) {
var checkboxRange = s.getRange(i, 18);
var usuariosRange = s.getRange(i, 3)
var targetSheet = ss.getSheetByName("*MASTER*");
var targetRow = sheetValues[i][1];
var targetRange = targetSheet.getRange(targetRow, 16);
var targetTS = targetSheet.getRange(targetRow, 17);
var response = ui.alert('Do you want to set the timestamp for row ' + (i+1) + '?', ui.ButtonSet.YES_NO);
// Process the user's response.
if (response == ui.Button.YES) {
checkboxRange.setValue(false)
targetRange.setValue(true);
SpreadsheetApp.getActiveSpreadsheet().toast("Timestamp will be set for row " + (i+1));
targetTS.setValue(new Date()).setNumberFormat("dd-mm-yy hh:mm")
} else {
SpreadsheetApp.getActiveSpreadsheet().toast("Timestamp will not be set for row " + (i+1));
}
} ;
}
s.getRange(3, 18, s.getLastRow()-2).setValue(false)
}
If this is not what you wanted and you prefer to have a single confirmation dialog for all checked boxes, you can implement a help array and string as following:
function marcarEtiquetado2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Sin despachar");
var sheetRange = s.getDataRange();
var sheetValues = sheetRange.getValues();
var ui = SpreadsheetApp.getUi();
var rowKeeper = [];
var rowArray = ['\n'];
for (var i=sheetRange.getLastRow()-1; i>1; i--) {
// si valor en columna R (etiquetado) = TRUE
if ( sheetValues[i][17] === true) {
rowKeeper.push(i);
rowArray.push(i+1);
}
}
for (k = 0; k < rowArray.length-4; k += 5) {
rowArray[k+4] = rowArray[k+4] + "\n";
}
var rowString = rowArray.join(" ");
Logger.log(rowString);
var response = ui.alert('Do you want to set the timestamp for rows ' + rowString + '?', ui.ButtonSet.YES_NO);
// Process the user's response.
if (response == ui.Button.YES) {
for (var j = 0; j < rowKeeper.length; j++){
var i = rowKeeper[j];
var checkboxRange = s.getRange(i, 18);
var usuariosRange = s.getRange(i, 3)
var targetSheet = ss.getSheetByName("*MASTER*");
var targetRow = sheetValues[i][1];
var targetRange = targetSheet.getRange(targetRow, 16);
var targetTS = targetSheet.getRange(targetRow, 17);
checkboxRange.setValue(false)
targetRange.setValue(true);
targetTS.setValue(new Date()).setNumberFormat("dd-mm-yy hh:mm")
}
} else {
SpreadsheetApp.getActiveSpreadsheet().toast("No action will be carried out");
}
s.getRange(3, 18, s.getLastRow()-2).setValue(false)
}

"TypeError: Cannot read property 'getBlob' of undefined" when trying to get two different images and insert them into a document

I have a Google Apps Script that automatically extracts data from a Google Sheet and inserts it into a pre specified template. The data is found using unique tagNumbers/identifiers.
The data being extracted includes 3 signatures. I can only extract one of these signatures before I encounter the aforementioned error: TypeError: Cannot read property 'getBlob' of undefined.
This code is being used in two different functions, using all the same variables and names. I have tried changing variable names but this has resulted in the same TypeError.
The spreadsheet can be found here.
The script is here.
And the document template being filled is here.
Here is the code.
function electInstallSignature(row, body){
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
var sigElectInstaller = "%SIGNELECTINSTALL%";
var targetRange = body.findText(sigElectInstaller); // Finding the range we need to focus on
var paragraph = targetRange.getElement().getParent().asParagraph(); // Getting the Paragraph of the target
paragraph.insertInlineImage(1, file.getBlob());// As there are only one element in this case you want to insert at index 1 so it will appear after the text // Notice the .getBlob()
paragraph.replaceText(sigElectInstaller, ""); // Remove the placeholder
}
function commEngineerSignature(row, body){
var signature = row[35];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
var sigCommEngineer = "%SFCE%";
var targetRange = body.findText(sigCommEngineer); // Finding the range we need to focus on
var paragraph = targetRange.getElement().getParent().asParagraph(); // Getting the Paragraph of the target
paragraph.insertInlineImage(1, file.getBlob());// As there are only one element in this case you want to insert at index 1 so it will appear after the text // Notice the .getBlob()
paragraph.replaceText(sigCommEngineer, ""); // Remove the placeholder
}
As you can see, the code is the exact same in both functions, but only works in the electInstallSignature(row, body) function.
Below you can find where the row and body parameters are declared.
function chooseRowMethodI(templateId, rowNumber){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var data = sheet.getRange(2, 2, 10, 41).getValues();//starting with row 2 and column 1 as our upper-left most column, get values from cells from 1 row down, and 15 columns along - hence (2,1,1,15)
var docTitle = sheet.getRange(2, 2, 10, 1).getValues();//this is grabbing the data in field B2
var docTitleTagNumber = sheet.getRange(2, 5, 11, 1).getValues();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
Logger.log(i);
var row = data[rowNumber];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
//************************** All Instruments data in here**********************
instrumentDetails(body, row);
electInstallSignature(row, body);
commEngineerSignature(row, body);
doc.saveAndClose();
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("1Jylk3uO_WU0ClLQdm9y-mwRfHxlh2Ovn");
newFolder.addFile(file);
var newDocTitle = docTitle[i - 1][0];
var newDocTagNumber = docTitleTagNumber[i - 1][0];
doc.setName(newDocTitle + " " + newDocTagNumber + " " + today);
}
}
}
}
Should it be required, I have included the function where everything is run from (note that any ui and user input code is tabbed out to avoid having to navigate back to the spreadsheet every time the code is run).
var response = "FT101";
function chooseRow(){
// var ui = SpreadsheetApp.getUi(); // Same variations.
// var result = ui.prompt('Please enter the Tag number of the row you wish to print.', ui.ButtonSet.OK_CANCEL);
//
// // Process the user's response.
// var button = result.getSelectedButton();
// response = result.getResponseText();
// if (button == ui.Button.OK) {
// // User clicked "OK".
// ui.alert('Your tag number is' + response + '.');
// } else if (button == ui.Button.CANCEL) {
// // User clicked X in the title bar.
// ui.alert('You closed the dialog.');
// return 'the end';
// }
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var category = sheet.getRange(2, 3, 11, 1).getValues();//Needs to be verified to ensure correct cell is chosen by script
var tags = sheet.getRange(2, 5, 11, 1).getValues();//Needs to be verified to ensure correct cell is chosen by script
for(var i = 0; i < tags.length; i++){
if(tags[i][0] == response && category[i][0] == "Instrument"){
var templateId = "1N3o951ECS5CAVGE6UgqBiCPC7H7LiJbL7Cd59G1xTnA";
chooseRowMethodI(templateId, i);
return "";
} else if(tags[i][0] == response && category[i][0] == "Motor" || tags[i][0] == response && category[i][0] == "Valve"){
var templateId = "1cSPD23qFd-34-IIr5eJ5a5OgHp9YR6xav9T28Y4Msec";
chooseRowMethodMV(templateId, i);
return "";
}
}
}
This errors TypeError: Cannot read property 'getBlob' of undefined means that the object you are trying to getBlob from it does not have any blob data.
The only difference with the frist function and the second one is the first line: row[17] instead of row[35] this means the following:
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
So, you are probably never accessing to the while loop:
while(files.hasNext())
because var files = sigFolder.getFilesByName(sign); never had a next, and thus, as file is not initialized, it is undefined.
In summary, the error you are getting is:
file is undefined
This means that you never assigned anything on this variable, which only happens if you never accessed the while, which only happens if files never had a next.
Which happens because there aren't any files at all there, this means that there isn't any file with the name sign on the sigFolder. Or that the row[17] does not contain any substantial information about the filename you want to access.
So, check this.
Also, take into account the following documentation about iterators like the one you are handling on files:
When you do files.next() you are accesing the first element of the iterator:
File iterator
General documentation on JavaScript iterators:
Iterators and generators on Javascript

Changes made in one spreadsheet must get reflected in another spreadsheet

I have a google spreadsheet "shared" where it consists of all the bill of materials. I want to keep another spreadsheet "master" such that only owner can access that. Any data inserted in the shared spreadsheet should get reflected in master spreadsheet, but if we edit shared spreadsheet it should not get reflected in master spreadsheet.
Any help would be appreciated.
Well it depends on what you mean by "changes", but you could put =Master!A1 in cell A1 of the slave sheet, then drag the bottom right corner all the way down, then drag the bottom right corner of that selection all the way across. So cell D8 will have =Master!D8, and so on.
Google Spreadsheets does have a scripting capability. It also has a public gallery script.
image http://img593.imageshack.us/img593/5410/screenshot20110720at736.png
One of the public scripts is edit to another spreadsheet
edit to another spreadsheet
update in another spreadsheet the changes in the current one
ticcaje (at) gmail.com
image http://img97.imageshack.us/img97/240/picture1nns.png
unfortunately it has not been updated in some time, and after looking at the code I don't think it was ever actually completed, as there is a dialog message and then a return statement.
image http://img718.imageshack.us/img718/5264/pictureja.png
I think this could be a really useful script and so I've done a little editing with it, but actually there are no comments in it, and I don't have the time to get it working 100% right now, but I wanted to post it here in hopes that somebody could actually pick it up and run with it.
function onEdit(){
var sourceSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var targetSpreadsheet = SpreadsheetApp.openById('0AntUWac3dtkUac3dtnhTjMwac3dtVjBiac3dtOXcac3dt'); //put in your spreadsheet key here
var sourcesSheet = sourceSpreadsheet.getSheets()[0];
var targetSheet = targetSpreadsheet.getSheets()[0];
var currentSourceCellIndex = SpreadsheetApp.getActiveRange().getRow(); //ActiveCell().getValues();
var selectedCell = SpreadsheetApp.getActiveSpreadsheet().getActiveSelection().getA1Notation();
var targetRowsCount = targetSheet.getLastRow();
var targetColumns = sourcesSheet.getLastColumn();
var targetRange = targetSheet.getRange(1, 1, targetRowsCount, targetColumns);
var targetSources = targetRange.getValues();
var sourceRows = sourcesSheet.getLastRow();
var sourceColumns = sourcesSheet.getLastColumn();
var sourcesRange = sourcesSheet.getRange(1, 1, sourceRows, sourceColumns);
var sources = sourcesRange.getValues();
var compareName = sources[currentSourceCellIndex-1][0];
return;
//Browser.msgBox("currentSourceCell: "+sources[currentSourceCellIndex-1][0]);return; // ActiveCell()
//Browser.msgBox("currentSourceCell: "+targetRowsCount);
// return;
for (var i = 1; i < targetRowsCount; ++i) {
if (targetSources[i-1][0] == compareName){
targetSheet.deleteRow(i);
break;
}
}
// var sourceRows = sourcesSheet.getLastRow();
// var sourceColumns = sourcesSheet.getLastColumn();
// var sourcesRange = sourcesSheet.getRange(1, 1, sourceRows, sourceColumns);
// var sources = sourcesRange.getValues();
//if ((sources[sourceRows-1][0] == "")||(sources[sourceRows-1][1] == "") ||(sources[sourceRows-1][2] == "") || (sources[sourceRows-1][3] == "") || (sources[sourceRows-1][4] == "") || (sources[sourceRows-1][5] == ""))
//return;
//currentSourceCell = sourceSheet.getActiveCell();
rowToInsert = targetSheet.getLastRow();
targetSheet.insertRowAfter(rowToInsert);
var insertRange = targetSheet.getRange(rowToInsert + 1, 1, 1, targetSheet.getLastColumn());
var kolonnen = [];
var tbText = [];
for (var i = 1; i < 16; ++i) {
kolonnen.push(i);
tbText.push(sources[sourceRows-1][i-1]);
}
//Browser.msgBox("source is: "+tbText);return;
for (j = 0; j < kolonnen.length; j++) {
var zellRange = targetSheet.getRange(rowToInsert+1, kolonnen[j], 1, 1);
zellRange.setValue(tbText[j]);
}
}
The original code can be accessed by editing the script, but if you want it I'll just go ahead and post it here, too:
function onEdito() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {
name: "go",
functionName: "insertRow"
}
// ,
// {
// name: "Build Journal",
// functionName: "collectJournal"
// }
];
ss.addMenu("pastePlus", menuEntries);
}
function onEdit(){
var sourceSpreadsheetName = Browser.inputBox("source spreadsheet");
//var sourceSpreadsheetName = SpreadsheetApp.getActiveSpreadsheet().getName();
var files = DocsList.getFilesByType("spreadsheet");
//var files = SpreadsheetApp;
Browser.msgBox("currentSourceCell: " +files);
//var sourceSpreadsheet;
for (var i = 0; i < files.length; ++i) {
var filename = files[i].getName();
if (filename == sourceSpreadsheetName) {
var sourceSpreadsheet = SpreadsheetApp.openById(files[i].getId());
var sheets = sourceSpreadsheet.getSheets();
var sourcesSheet = sheets[1];
break;
}
}
var targetSpreadsheetName = "Probando Script"; //DocsList.getFilesByType("spreadsheet");
//var targetSpreadsheet;
for (var i = 0; i < files.length; ++i) {
var filename = files[i].getName();
if (filename == targetSpreadsheetName) {
var targetSpreadsheet = SpreadsheetApp.openById(files[i].getId());
var sheets = targetSpreadsheet.getSheets();
var targetSheet = sheets[1];
break;
}
}
var currentSourceCellIndex = SpreadsheetApp.getActiveRange().getRow(); //ActiveCell().getValues();
var selectedCell = SpreadsheetApp.getActiveSpreadsheet().getActiveSelection().getA1Notation();
var targetRowsCount = targetSheet.getLastRow();
var targetColumns = sourcesSheet.getLastColumn();
var targetRange = targetSheet.getRange(1, 1, targetRowsCount, targetColumns);
var targetSources = targetRange.getValues();
var sourceRows = sourcesSheet.getLastRow();
var sourceColumns = sourcesSheet.getLastColumn();
var sourcesRange = sourcesSheet.getRange(1, 1, sourceRows, sourceColumns);
var sources = sourcesRange.getValues();
var compareName = sources[currentSourceCellIndex-1][0];
//Browser.msgBox("currentSourceCell: "+sources[currentSourceCellIndex-1][0]);return;
Browser.msgBox("currentSourceCell: "+targetRowsCount);return;
for (var i = 1; i < targetRowsCount; ++i) {
if (targetSources[i-1][0] == compareName){
targetSheet.deleteRow(i);
break;
}
}
var sourceRows = sourcesSheet.getLastRow();
var sourceColumns = sourcesSheet.getLastColumn();
var sourcesRange = sourcesSheet.getRange(1, 1, sourceRows, sourceColumns);
var sources = sourcesRange.getValues();
//if ((sources[sourceRows-1][0] == "")||(sources[sourceRows-1][1] == "") ||(sources[sourceRows-1][2] == "") || (sources[sourceRows-1][3] == "") || (sources[sourceRows-1][4] == "") || (sources[sourceRows-1][5] == ""))
//return;
//currentSourceCell = sourceSheet.getActiveCell();
rowToInsert = targetSheet.getLastRow();
targetSheet.insertRowAfter(rowToInsert);
var insertRange = targetSheet.getRange(rowToInsert + 1, 1, 1, targetSheet.getLastColumn());
var kolonnen = [];
var tbText = [];
for (var i = 1; i < 16; ++i) {
kolonnen.push(i);
tbText.push(sources[sourceRows-1][i-1]);
}
//Browser.msgBox("source is: "+tbText);return;
for (j = 0; j < kolonnen.length; j++) {
var zellRange = targetSheet.getRange(rowToInsert+1, kolonnen[j], 1, 1);
zellRange.setValue(tbText[j]);
}
}