I have looked on SO and researched my many questions on Google, but did not find anything that provided a direct answer. I am setting up code to email a sheet one (or many) times. I am having trouble setting the email address of the recipient(s). I need the script to send out one email for each address in the data range (P122:P126).
I regret that, for privacy reasons, I am unable to share the entire script. (On a positive note, everything except this works.)
The active sheet is "var sourceSheet"
The number of email addresses is a number in cell S121 on the active sheet
All of the email addresses needed are in a vertical column on the active sheet ("P122:P126")
Here is the relevant section of code:
// Sets the recipient of the email message
var numRows = sourceSheet.getRange("S121:S121").getValues(); // Number of rows to process
// Fetch the range of cells P122:P126 that contain email addresses
var dataRange = sourceSheet.getRange(122,16,numRows,1);
// Fetch values for each row in the Range.
What needs to be done to obtain one (or more) email addresses usable by the "GmailApp.sendEmail" statement? No matter how I analyze the problem, it seems to me (and I may be wrong) that a loop is necessary, and I don't know how to use the information I have to create the actions needed.
Try this:
var numRows=sourceSheet.getRange("S121").getValue();
var dataRange=sourceSheet.getRange(122,16,numRows,1);
Related
I need to associate information with a row in a Google Sheet programmatically, with minimal user interaction. The association must stay associated with the same row, even when a row is added or deleted above. Also, the information should be deleted if the row is.
To me, this sounds like a job for DeveloperMetadata.
I would like to do this without the API for now - to avoid adding more variables. I would use named ranges before I would use the API.
Anyways, the Spreadsheet, Sheet, and Range classes all have the method: addDeveloperMetadata, with various inputs - none of which have a DeveloperMetadataLocationType input (problem #1).
Interestingly, as suggested by the answer here, the following code DOES add developer metadata to a column:
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("H:H").addDeveloperMetadata("columnName", "price");
However, I can not for the life of me figure out how to create a "ROW" range to add metadata. I have tried the following:
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(1, 1, 1, sheet.getMaxColumns()).addDeveloperMetadata("rowStatus", "processing");
and
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("A2:L2").addDeveloperMetadata("rowStatus", "processing");
where L is the last column.
In both cases, I get the following error:
Adding developer metadata to arbitrary ranges is not currently
supported. Developer metadata may only be added to the top-level
spreadsheet, an individual sheet, or an entire row or column.
Is there any way to indicate that a range is a ROW, so I can add metadata to it?
And as soon as I post the question, I figure out the answer.
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("2:2").addDeveloperMetadata("rowStatus", "processing");
Found the answer by pretending to create a Conditional Format Rule, and selected the row number when indicating the range to see how Google Sheets A1 notation represents rows.
I'm making a website for school to connect students and teachers. Teachers fill out a Google Form asking for service (grading papers...) and it is connected to Google Spreadsheets. Then a student can see that Spreadsheet and sign up on a different Google Form to help the teacher, and that response is recorded in the same Google Spreadsheet. When the student submits his/her Google Form, I want the teacher to be notified by email that a student signed up to help them. How can I send that notification? And I don't want the notification to come from my own school address. Is that possible?
Here is the Google website: https://sites.google.com/fcpsschools.net/jmhsservicesignup/subjects/math
This is the code I have so far:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tutors
Signed Up");
var startRow = sheet.getLastRow(); // First row of data to process
var dataRange = sheet.getRange(startRow, 1, 1, 5)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[3]; // Second column
var tutor = row[0];
MailApp.sendEmail(emailAddress,"Tutor Request Filled",""+tutor+" has accepted your request to be tutored! Please notify"+tutor+" if anything changes.");
}
}
The getLastRow doesn't seem to work, and the email sends from my own address, not a Google forms notification if that is even possible.
If this is impossible I might just make the students send the email to the teacher, but I really want to automate the process. If you can think of a workaround, that would also be awesome.
Adding to what Sandy Good and Guilherme mentioned.
Here are a few more tips:
Not clear what data you're trying to get in your function. Are you trying to grab the data from the form submission? In that case you need to set up a trigger from the scripts menu, and connect it to the function (function needs to have an 'event' argument, usually called e).
function processFromSubmission(e) {//get data from e}
Apologies if you're not trying to do that. What happens when you Log sheet.getLastRow()?
Ask the school if they can get you another email address to use for this form, something like notifications#yourschool.edu. Follow the steps here to set up your alias. Do it for the gmail account that "owns" the spreadsheet these forms are sending data to.
Make sure your alias is set up properly. You should be able to see it in the "from" dropdown menu when you compose an email. Also try running the code Logger.log(GmailApp.getAliases()) and check the log to see that it's available.
Use GmailApp.sendEmail to send email, not MailApp.sendEmail. They're almost the same, but it looks like the MailApp version doesn't let you send from an alias. You can specify the 'replyTo' email as well if you want to set it to something else, like the student filling out the form or whatever (still not clear exactly what you're trying to set up!).
Send email like:
GmailApp.sendEmail(emailAddress, subject, body,
{replyTo: replyToEmailAddress_can_be_any_email,
from: 'notifications#yourschool.edu'
});
Note: your link didn't open.
Good day,
Just want to ask if it is possible to access same google-spreadsheet by different google account and show different records depend on the category or primary key that they have or they belong?
thank you so much.
Yes, you can functionally do this, but it isn't neat and creates more problems than it solves. Here's a simple example:
function hideSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Your sheet");
// Get the user accessing the sheet
var user = Session.getActiveUser().getEmail();
// Match an email. If you're using an array of people, you'll need to loop it.
if(user != "email#email.com") {
// If they don't match the test, hide the sheet.
ss.hideSheet();
} else {
ss.showSheet();
}
Set an onOpen() trigger for the function. The users will need editing rights (which kind of defeats the purpose) or the script won't run. If it's a shared sheet, it will also hide for you unless you test for your account, but showing the sheet will also show for shared users.
A better solution would be to start a master sheet and =IMPORTRANGE(url, range) on slave sheets.
Create a master sheet only you have access to. Title each sheet by region. Create slave sheets titled by the region you want to give to someone else. In cell A1, use =IMPORTRANGE("theMasterSheetUrl","New York!A:Z"). Changing the range value allows you to share only portions of a master spreadsheet, so the New York rep doesn't see the San Francisco, etc. It avoids all the hassle of adding/removing permissions through a script and ensures up to date data on the region they need access to.
Q: How can an AppsScript attached to a Form store an extra piece of data into the Sheet?
Situation: We have a (long) Google Form that stores many pieces of data into a Google Sheet. Often the entries need to be edited, and it is much easier to edit using the original form than trying to edit directly into the sheet. (Some of the items are text, several paragraphs long.) I would like to store into the spreadsheet one additional piece of data, specifically the URL that an editor can use to edit the row entry using the form.
I can already get all the form data and I can get the right URL with formResponse.getEditResponseUrl(). And I can send all of that in an email to a user, usually the editor who is collecting all the form entries. (Thanks to many helpful answers in StackOverflow for getting me this far!) But the editor has to manually copy and paste the URL into an additional column in the proper row of the spreadsheet.
I see an interface in class Sheet to add a column to the spreadsheet, but I don't see how to populate that extra column for the particular row that the form just stored. We have added the column manually, and have verified that it is not overwritten by Google when editing via the form. How do I store that one little piece of data into the sheet?
What am I missing? Any help will be greatly appreciated. Thanks.
[added clarifications 2015-02-06]
We have a long form that some people submit and other people edit. Editing is to be done using the form, not editing directly in the spreadsheet, so we need the URL that permits the editors to re-edit the response.
I would like to store that URL into the spreadsheet during the form submission, so that the editors, who have access to the sheet, can find it.
In a script on the Form side, I can easily calculate that URL, but now how do I store it into the sheet in an extra column?
In my Form-side script at the moment, I get the URL and send it, along with all the form data, in an email to the editors' distribution list. One of the editors then copies the URL from the email and pastes it into the sheet. (Most of the time, into the correct row, even. :-) This is a potentially error-prone manual step.)
A secondary question: what is up with the row numbers in the sheet versus the response numbers in the form.getResponses()? The row numbers and response numbers seem to wander as new items are submitted (i.e., new rows), and old items are edited. Can one reasonably predict the sheet's row number in which the editor will find the form data?
Again, thanks for any help you can give me on this. We have a survivable interim solution. However, with a hundred or so form entries coming in the next couple months, I would love to error-proof the process as much as possible.
rick
So, I've just stumbled upon your questions and, hopefully, I've understood it correctly.
Possible problems:
the script is incorrectly bound to the spreadsheet attached to the form and not to the form itself (which is not the problem in your case as far as I understood from your description)
race conditions between submission insertion and additional column edit, or between simultaneous submissions (see lines 27-32 from code)
accessing the spreadsheet directly, without prior selecting a sheet from the spreadsheet, even if it spreadsheet contains only one sheet! (see lines 36-37 from code)
using the column numeric index, instead of the corresponding column letter as argument for getRange() method, which accepts only column letters AFAIK (see lines 42-43 from code)
Below you have the code which should address all these problems (I have not tested it, but it is an adaptation of a perfect working solution for a very similar scenario):
// Converts sheet column numeric index to corresponding column letter
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
The following function must be registered to an "On form submit" event from form - not from the spreadsheet! (Script Toolbar -> Resources -> Current project's triggers -> Add a new trigger)
// Associated the sheet rows with response URLs in an additional column
function onFormSubmit(e)
{
try
{
// Get the response Url, either from FormApp:
var responseUrl = FormApp.getActiveForm().getEditResponseUrl();
// Or alternatively get it from the event:
// var responseUrl e.response.getId().getEditResponseUrl();
// ....................
// Other URL processing
// ....................
// Get a public lock on this script, because we're about to modify a shared resource.
var lock = LockService.getPublicLock();
// Wait for up to 30 seconds for other processes to finish.
lock.waitLock(30000);
// Wait for row insertion to finish, so that sheet.getLastRow() method gets the updated number of rows
Utilities.sleep(1000); // 1 second
// Here insert the URL to your spreadsheet
var spreadsheetUrl = "https://docs.google.com/spreadsheets/d/YGUgHi28_gYUffGYGGH_78hkO1Pk/edit";
// Gets the first sheet inside the spreadsheet (if you have multiple sheets, just change the value [0])
var sheet = SpreadsheetApp.openByUrl(spreadsheetUrl).getSheets()[0];
// Get updated number of rows and columns, after form submit inserted the new row
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
// Get the exact cell, next to the right of the new row, by converting the column index to corresponding letter
var lastCell = columnToLetter(lastColumn) + lastRow.toString();
// Set the content of the cell with the new URL
sheet.getRange(lastCell).setValue(responseUrl);
// Release the lock so that other processes can continue.
lock.releaseLock();
}
catch (error)
{
// If there's an error, show the error message
return error.toString();
}
}
For any other questions, just write a comment. Hope it helps.
You can use the form submit range parameter to get the row / spreadsheet range of the form data being placed in the sheet. Then use the range offset method to push your data into the column after the last column of form data.
Notice if you use the HYPERLINK formula, you must escape the quotes that are passes as parameters.
e.g.
function formProcessing(e){
var formData = e.values;
var dataRange = e.range; // gets the range on the spreadsheet
/*
do all your processing
*/
var url = "http://www.google.com"; // whatever url to put in spreadsheet
// add the url value to the spreadsheet
formRange.getCell(1,formRange.getLastColumn()).offset(0,1).setValue(url);
// or if you want a named link
//formRange.getCell(1,formRange.getLastColumn()).offset(0,1).setFormula("HYPERLINK(\"" + url + "\", \"Edit Form\")");
}
I have Written Script on Google Spreadsheet to send Email when spreadsheet is modified or any Data is added. Email Trigger is working but whenever any data is entered in next Row it send Email to previous email address also.
Please suggest solution
The below is written script :
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 1; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1 , numRows,3) // Fetch the range of cells A2:B3
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[2]; // First column
var message = row[0] + "requested" + row [1]; // Second column
var subject = "Sending emails from a Spreadsheet";
MailApp.sendEmail(emailAddress, subject, message);
}
}
Your question is unclear... nowhere in the script I see something that reads which cell is actually modified... your target range is hardcoded on row 2 so the only row that can be processed is row 2 (and the mail can only be sent once)...
So can you :
explain how it should work
explain how it works now , especially what do you mean by 'previous email'
remove typos in your code (row[2] is not First column)
explain how you trigger this function : the name onEdit(e) suggest an onEdit trigger but simple triggers cannot send mail so I suppose you have set some other trigger.
explain why (e) in your function parameter and not using it ?
EDIT : thanks for the complement of information.
The script you suggest is not sufficient to achieve what you want. The idea here is to check if something in the sheet has been modified either by adding (or inserting) a row of data or (if I understood well) by editing any row in the sheet with a new value.
This is not really as simple as it looks at the first glance ;-)
What I would do it to take a 'snapshot' of the sheet and -based on a timer or onEdit - compare that snapshot to the sheet's current state.
There is more than one way to get that result, you could have a second sheet in your spreadsheet that no one could modify and that is a copy of the main sheet that you update after each modification/mail send. So before updating the script should look for any difference between the sheets and send a report to the corresponding email when a difference is found.
Another way to do that is to store the sheet data converted to a string in the script properties, the principle is the same but it's more 'invisible' for normal users accessing the spreadsheet.
You could also use scriptDb or your userproperties but the script properties is probably better suited (simpler) for this use case.
Tell us what you think/prefer and I (or someone else) could probably give you some code to start with.
It appears that you're using a shared spreadsheet to collect the add-user-requests, and trusting the requesters to fill in the information. In the detail document you shared, it further appears that requests are ADDED, but not EDITED. (That's an important simplifying distinction.)
I suggest that what you really need is to use a form for receiving that input. Using a form will create a "data table" within your spreadsheet, a set of columns that you must not mess with. (You can edit the contents, add and delete rows, but must not add or remove columns.) However, you CAN add columns to the spreadsheet outside of this table, which gives you a handy place to store state information about the status of individual requests.
Further, you can trigger your processing to run on form submit, rather than a simple "onEdit" - this gets away from the problem that ScampMichael pointed out. Alternatively, you can use an installable edit trigger, as described in this answer.
Try this sheet, and this form. Save yourself a copy, go into the script and remove the comments that are stopping emails from being sent, and try it out. There's a menu item in the spreadsheet that can kick off processing; just clear the "Request State" column to re-run it. You can open the form (and find its URL), and add more entries to experiment.
It's the core of a similar system that I've written, and contains a discreet state machine for processing the requests. My system has large amounts of very complex data in multiple spreadsheets, so it often gets pre-empted, then needs to run again. (I use a timed trigger for that.) That's why requests are handled through states. If you find that too complex, pull out only the parts you need.