Showing posts with label Google Scripts. Show all posts
Showing posts with label Google Scripts. Show all posts

Google App Maker


Andrew Stillman of AmplifiedIT took some time to run through an overview of Google's App Maker. If your are looking to dig in here's Developer documentation.

Andrew Stillman bearded speaking in to a mic held by a hand as he types on a laptop.
First lets go over what it's not:
  • A replacement for the Forms to Sheets workflow. App Maker works with a login on the domain. So a public facing App is not a thing here. A Form would be a better choice to collect things like Parent Emails. 
  • A Mobile App maker. My instinct when I hear App is a mobile solution. This is geared to Web app workflows.
  • A no code solution. As slick as it is with drag and drop, autocomplete and GUI it does require knowledge of coding. Specifically HTML, CSS, Javascript and by extension Google Apps Script. 
  • App Maker is similar in ways to Access, or FileMaker Pro. You can work with related data tables and events. 
  • The data can be organized and displayed by user type in some really slick looking Googley ways. 
  • It's a Low code environment so can be worked with by anyone with some computational skills. 
  • Integrates with the G-Suite system through coding including the directory to provide a level of security to the data being worked with. 



But what it is an can be makes it powerful for any environment with a lot of data flow and systems management.




App Maker allows the creation of some really great workflows and automations. SignUps, Permission Slips, Admissions, Book Reports and more become a customizable operation. These things can be developed or even better reach out to AmplifiedIT to develop it for you or just walk you through the process.

Andrew said it best, "Sheets and Forms have gotten us this far but App Maker is the next step." We all have those systems we are trying to improve, simplify or automate. App Maker makes this a real possibility without having to shell out the funds for software. It's collaborative and relatively inexpensive. $15 gets you started on the low end for a GoogleCloud account and gives you a change to play. From there your creations can scale up for various environments. I'm excited to explore more and create some better workflows.

Check out the
App Maker Bootcamp




Collect Quick Formative Feedback

We often send our students to complete independent writing work. We circulate the room and the students that are vocal get the attention. But what about those students that don't raise their hand to ask for help? How can we provide an easier way for them to reach out to us? Well, here's a way to create a custom menu for a document and collect some formative feedback.

Sample:
Spreadsheet collecting data, Document which can be copied with the code.

Our starting function is the onOpen function. This is a reserved word which the doc looks for in the script. As the document is opened this script is run. In this case we engage the DocumentApp library of code. The getUi begins the user interface. We createMenu and name it something. I've named it with a emoji of a hand to support the idea that we don't always need menus with words. This menu will show up as a new main menu like File or Edit. We then add a menu item with addItem. The first item is a sad face. the sad face is connected to the function of helpMe. The second item is a thumbs up connected to the function called doingGood. Lastly we all it all to the user interface with addToUi. These menu items are on separate lines but they are really strung together commands. The semicolon identifies the end of the line of commands.

function onOpen(e) {
  DocumentApp.getUi()
      .createMenu('✋')
      .addItem('😕', 'helpMe')
      .addItem('👍', 'doingGood')
      .addToUi();
}

The helpMe and doingGood functions are nearly identical except for the message we set in the secon column. I'll use the helpMe function to detail what's happening. The same will be true for the doingGood and any others you want to make.

To begin we set a global variable as the sheet ID. A global variable is set outside of the functions and is used for all the functions. Because our variable isn't changed by the program we could also declare it as a constant but we'll keep it simple and just use var to declare the sheetid variable. The sheet id can be found in the url of the sheet. The sheet needs to be shared as editable for it all to work.

Our helpMe function starts by assigning the spreadsheet by the id and the specific sheet by name to the sheet variable. We next identify the last row used and assign it to the variable lrow. We then get the range of the last used row represented by lrow plus 1. This would be the next empty row. This row with the column of 1 is the cell we get and set the value of the currently logged in user email. The range then is grabbed for the second column and the value is set to the words 'Help me!'.

var sheetid = '1C1bUbiouvFgeE6qfZjknW9zumpMLOHYcqQNjO39sVAI';

function helpMe() {
  var sheet = SpreadsheetApp.openById(sheetid).getSheetByName('Sheet1');
  var lrow = sheet.getLastRow();
  sheet.getRange(lrow+1, 1).setValue(Session.getActiveUser().getEmail());
  sheet.getRange(lrow+1, 2).setValue('Help me!');
}

function doingGood() {
  var sheet = SpreadsheetApp.openById(sheetid.toString()).getSheetByName('Sheet1');
  var lrow = sheet.getLastRow();
  sheet.getRange(lrow+1, 1).setValue(Session.getActiveUser().getEmail());
  sheet.getRange(lrow+1, 2).setValue('Doing Good');
}

This is a pretty simple code to create a quick menu for formative feedback. Consider stopping the class a few minutes into independent work and asking the class to select the menu item that best identifies how they currently feel about the topic. You would get immediate feedback on the spreadsheet. They may have to accept permissions the first time around. You can also add a timestamp to another column by setting the value to .setValue(new Date()) You may also want to set another column with a particular class name. There's alot you can do with this from here.

Auto Add User to Sheets

Ever needed to grab the name of the user that edited a line of a Sheet? Sure you can comb through the version history but isn't it better to just see the the information right on the sheet? Google Scripts provides a way to capture the user that edited. Let's take it one step further and make a bathroom log. Students logged in to their Google Account on the same domain as you will be able to click an icon and create a record that they are going to the restroom. No one will be able to mark the sheet while they are out. Sample

So the code we need to know to identify the user currently editing the sheet would be Session.getActiveUser().getEmail()
To break this down, the Session refers to the period a user is logged in. A session can be time limited or limited to the particular device being used. We use the Session to access the method of
getActiveUser()
The active user is the user logged in. From here the only selection is
getEmail()

Stringing these together and we  can identify who is using the sheet.

We begin our function creating our variables to represent the Spreadsheet and the currently active sheet.

function bathroom() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
}

Because we will be adding the user to the last empty row we want to identify which row that is. sheet.getLastRow() returns the value of the last filled row. So the next available row would be sheet.getLastRow() + 1.

function bathroom() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var lastRow = sheet.getLastRow() + 1;
}

We need to consider the structure of our spreadsheet. We'll create a sheet with the following headers.

EmailOutInDuration
We can use the function getColumnByName used in the previous post here. But I'll keep it very simple and we'll use the column numbers. We're going to want to know if a student has come back from the bathroom first. To do this we want to check the last filled row and the 3rd column which is labeled "In".

function bathroom() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var lastRow = sheet.getLastRow() + 1;
  var lastcell = sheet.getRange(lastRow-1, 3).getValue();
Logger.log("Last Row " + lastRow + " Last Cell Value " + lastcell)
}

I've thrown a loger line here to test the code and see what values it returns. It's helpful to do this as your write. I've torn my hair out and spent days staring at code not realizing I only thought I knew the value of a particular variable. Don't assume, debug.

The last entered user is also going to be needed. We can get this in the same manner we retrieved the lastcell variable.

function bathroom() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var lastRow = sheet.getLastRow() + 1;
  var lastcell = sheet.getRange(lastRow-1, 3).getValue();
  var user = sheet.getRange(lastRow-1, 1).getValue();
Logger.log("Last Row " + lastRow + " Last Cell Value " + lastcell + " User " + user)
}

Now we start doing some comparisons to know the condition our sheet is in.
 if (lastcell.length == 0 && user == Session.getActiveUser().getEmail())
Here we are checking if the number of characters in the lastcell variable is equal to 0 AND the user variable has the same email in it as the current user in the sheet then we will do something. If these two things are true then it means the student has come back from the bathroom and is checking in. We will need to enter the current timestamp in the "In" column and we can set the formula to calculate how long they were in the bathroom.
We initialize and set a variable to the result of setting the timestamp. Assigning this to a variable can help to identify errors if things are going wrong but not absolutely needed. We grab the range using the last row variable which we already advanced by 1. To get the last filled row we reduce the variable by 1. The column is column number 3. We set the value to a new date object which we typically read as a timestamp.
we also move forward with setting a formula in Row and Column format. The formula here is relative to the cell the formula is entered in. In this case R[0] stands for the current cell's row. C[-1] is one column less. since we are putting our formula in column 4 one column back would be column 3 which we just put our In timestamp in. We then subtract the C[-2] column  or column 2 of the same row. This gives us a duration of how long they were in the bathroom.

function bathroom() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var lastRow = sheet.getLastRow() + 1;
  var lastcell = sheet.getRange(lastRow-1, 3).getValue();
  var user = sheet.getRange(lastRow-1, 1).getValue();
  if (lastcell.length == 0 && user == Session.getActiveUser().getEmail()){
    var col2 = sheet.getRange(lastRow-1, 3).setValue(new Date());
    var formula = sheet.getRange(lastRow-1, 4).setFormulaR1C1("=R[0]C[-1] - R[0]C[-2]")
  }
}

We now have the if statement identifying when a user returns. But what if the user clicking the icon is not the user in the bathroom. They need to know that they can't go yet. We again check if the lastcell length is 0 meaning it's blank. We also check to determine the user in the last row is not the current user. The usage of != means not equal. If both conditions are true we assign the variable ui an alert with a message.

function bathroom() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var lastRow = sheet.getLastRow() + 1;
  var lastcell = sheet.getRange(lastRow-1, 3).getValue();
  var user = sheet.getRange(lastRow-1, 1).getValue();
  if (lastcell.length == 0 && user == Session.getActiveUser().getEmail()){
    var col2 = sheet.getRange(lastRow-1, 3).setValue(new Date());
    var formula = sheet.getRange(lastRow-1, 4).setFormulaR1C1("=R[0]C[-1] - R[0]C[-2]")
  }else if(lastcell.length ==0 && user != Session.getActiveUser().getEmail()){
    var ui =SpreadsheetApp.getUi().alert('Sorry, someone is currently out.')
    }
  }
}

Finally at the end if the user is not the one returning and if the user is not still out and we can add a new user we do so. The else statement is our default statement. If 1 then 1a, else if 2 then 2a, else default. We can think of the progression like this. In our action for the default response we use the variable col1 to capture the setting of the user. The range is gotten of the last row variable which represents a new row and the column of 1. We set this cell to the value of the current users email address. We then set the second column on the same row as a new date object which we know of as a timestamp.

function bathroom() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var lastRow = sheet.getLastRow() + 1;
  var lastcell = sheet.getRange(lastRow-1, 3).getValue();
  var user = sheet.getRange(lastRow-1, 1).getValue();
  if (lastcell.length == 0 && user == Session.getActiveUser().getEmail()){
    var col2 = sheet.getRange(lastRow-1, 3).setValue(new Date());
    var formula = sheet.getRange(lastRow-1, 4).setFormulaR1C1("=R[0]C[-1] - R[0]C[-2]")
  }else if(lastcell.length ==0 && user != Session.getActiveUser().getEmail()){
    var ui =SpreadsheetApp.getUi().alert('Sorry, someone is currently out.')
  }else{
    var col1 = sheet.getRange(lastRow, 1).setValue(Session.getActiveUser().getEmail());
    var col2 = sheet.getRange(lastRow, 2).setValue(new Date());
  }
}

This function can then be assigned to an image or a menu. Consider using some of these techniques for an onEdit trigger to track who changes a line on a spreadsheet.

Visual Program - A response to "Why it's a bad Idea" by Mike Hadlow


After reading Mike Hadlow’s post on his blog titled “Code Rant: Visual Programming - Why it’s a Bad Idea” I feel I need to respond. Mike references block based coding such as the education tool “Scratch” from MIT. He points out three reasons why he thinks block based coding is bad. "1. Textual programming languages obfuscate what is essentially a simple process. 2. Abstraction and decoupling play a small and peripheral part in programming. 3. The tools that have been developed to support programming are unimportant."

What he doesn’t take into account is how we learn to code and think computationally. There is a process of learning how to identify a problem, pull a problem a part, break it down into abstract representational ideas, and build a step by step procedure to solve the problem. These parts can be looked at using a design thinking flow where we continually reconsider what we are doing and if out solution works. As we teach young students how to think computationally it’s helpful to have a system which can give immediate results. Block-based/visual coding is intended for the novice programmer. It’s there to give quick results without concern for syntax. The blocks don’t allow for syntax errors. 

I'm guilty of spending hours staring at code and going through debugging steps only to learn I spelled something wrong. When you start with code this sort of bug is enough to made you never want to code again. Perseverance does not happen over night. It's built incrementally. Block-based code allows for quick success of code without the headache.

Coding is very different from math but has similarities in how we grasp the concepts. Teaching coding requires giving the code concept to start. Allowing the student to modify the given code to see what happens. Then tasking the student to create something using the code and any other code or creative tools they can bring in. This iterative education process works best with visual code when there is no concern about syntax errors. The only errors are in how the code is put together. 

Do specifically address Mike's points:

1. Textual programming languages obfuscate what is essentially a simple process.
To a seasoned programmer this is very true. After I learned how to code with interpreted languages like JavaScript I've had that question of, "How does it really do that?". Even text-based code can obfuscate a simple process. Many languages have text functions build in, ie Left(data, count of characters). The function is slicer of the data inputed. This is not a concept a novice needs to know as they use the Left function. The real mechanics are hidden. Visual code also hides the actual functions. 

2. Abstraction and decoupling play a small and peripheral part in programming.
I firmly disagree with this. When building a realistic game you need to provide gravity. Well, how do you give an accurate representation of gravity? How do you designate where the floor is? How high can your character jump? These are abstract concepts which can be represented by an algorithm. Students can't figure it out unless they explore the abstraction of gravity. Yes things get complex but part of coding is creating efficient code which interacts seamlessly and simply with other code. Gravity can become a function with quick inputs making it fairly universal for each character. Complexity can increase by adding more features but well built functions help to Make sense of it. 

3. The tools that have been developed to support programming are unimportant.
The tools that have been developed to support programing are what make it accessible to others. I've programmed in a raw text editor for years. I torture myself so I memorize code functionality. For a novice the tools to support programming are what make the difference. I spend a lot of time playing with Google Apps Script. The intelligent suggesting of options support my exploration and make me more efficient. Google provides suggestions to code; intelligent suggestions. I know my variable is not initialized because of the color signifier. It's a pleasure to program with Google and it's the coding environment which keeps me coding there. I promote Google Apps Script because it's easy to code in. 

I will say block based coding can be improved by providing tools to transition from the visual code to a syntactical base. Allow me to pull in the code in visual format. But give me the ability to pull back the visual curtain. Block based code makes it accessible to those that may have previously been intimidated. And let's face it, we can type faster than we can drag in code. once we start getting the hang of it we lean to typing the code. 

Non-technical people need to have access to coding. It's up there with learning math, language, science, history and arts. our lives are split between our digital life and our physical life. I can cook and make a basic meal in my physical life. Coding is relative. People need to know how to craft some basic code to help them solve a problem in their digital life. 


And just for fun here's a Scratch project I did in 10 minutes to teach about conditionals.






Looping Through Data


I don't know about you, but I often organize students with google sheets. I use rows for each student and columns for information related to the student. Sometimes I need to push this informatiuon back to students or parents. Sometimes I have do something else with the information. It’s helpful to know how to get the data and work with it in a loop.

Spreadsheets are a two dimensional arrays. There is information in Rows, dimension 1,  and information in columns, dimension 2. We can target each cell by knowing the proper row and column.

We can also loop through each row and do something with the data. 

Computers start counting from 0. Google Sheets starts counting the informational rows and columns from 1. I will also use my own label in row 1 to identify the column number. This way I can insert columns and move them around and still retrieve the same information. Let’s start with identifying the columns by our own labels and setting the column number to a variable which makes sense to us. 

function loopThroughData(){

  var ss= SpreadsheetApp.getActiveSpreadsheet();
  var sheet= SpreadsheetApp.getActiveSheet();
  var emailcol = getColumnByName(sheet, "Email");
  Logger.log(emailcol);
}

function getColumnByName(sheet, name) {
  var range = sheet.getRange(1, 1, 1, sheet.getMaxColumns());
  var values = range.getValues();
  for (var row in values) {
    for (var col in values[row]) {
      if (values[row][col] == name) {
        return parseInt(col)+1;
      }
    }
  }
}

A function called getColumnByName is used in our loopThroughData function. It’s used with two variables pushed into it. The first is the sheet we are working on and the second is the column name. This function runs and will return the column number if the column name is found. The program returns to the loopThroughData function and completes the script Logging the column number. 
This begins to give us some flexibility with the sheet. We can add and move columns and not work about breaking our program. We can run it and we’ll see the column number returned in the log. The intent here is to be able to loop through and use data in a full sheet. We know how to get the column numbers but what about the rows and the rest of the data. 

Efficiency in code so far doesn’t seem like a big deal. We’ve looked at very little data. But if we were to use an entire sheet composed of the maximum 5,000,000 cells (as of the newest 2018 update) we need to start being aware of how long each step of code takes. Even if we consider using up to 1,000,000 cells it can take longer than we expect to finish our script. Attendance data can exist in large data sets. Consider a school of 400 students * 180 school days * 7 classes each of attendance. 504,000 cells of data. If a step of our program takes .01 seconds it will take 5,040 seconds to complete or 84 minutes or 1 hour and 24 seconds. And that’s just one step. So while we can pull each cell individually with sheet.getRange(row, column).getValue() we need to ask ourselves if it’s the most efficient. 

Our loop will instead pull all the data, once, and reference it in the computer memory. Below I concentrate on our main function our getColumnByName should remain in the script. I usually keep functions I use in larger functions at the bottom on all my scripts. 

function loopThroughData(){
  var ss= SpreadsheetApp.getActiveSpreadsheet();
  var sheet= SpreadsheetApp.getActiveSheet();
  var emailcol = getColumnByName(sheet, "Email");
  var data = sheet.getDataRange().getValues();
  for (var i =1;i<data.length;i++){
    Logger.log(data[i][emailcol-1]);
  }
}

The variable data is the used area of the sheet. It provides data in a matrix format. The first referenced data index is the row. The second is the column. We would use a reference to the index number of the row and column to reference a value. The index starts from 0 so row 0 is our header. In the sheet it’s row 1.Our column   numbers compared to the sheet are all offset by -1. To return the header cell containing “Email” we can use row 0 and column emailcol-1. We’d write it like this in our script.

data[0][emailcol-1]

The square brackets [] are used to indicate an index number. They can be strung together to reference indexes of indexes.

Now that we have a concept of indexes we are going to use a for loop to walk through our rows of data. For loops have three inputs to get them started. First a variable to count with set to the first number; var i = 0. Then the end of the loop as an expression. I usually think “while” in my head as I write it; i<data.length. And finally an increment for our counting variable to increase by with each loop; i++. JavaScript allows the short hand of using ++ to increment by 1 rather than i=i+1.

Here’s our initial for loop. 
for (var i =1;i<data.length;i++)

The loop needs to know what to do each time around. We put an action inside curly brackets { and }. We can also use the i variable in our action. 

data[i][emailcol-1]

With data we are saying return the second row (computers start from 0 and i starts being set as 1 se we are skipping our header row) and the column of the email (we assigned emailcol as the sheet column which is now offset in data by -1 so we offset the email column number by -1).

Putting it all together we can loop through the data and do something with it. Above we are returning whatever is in the email column and putting it in the log. We can instead send emails, create slides, make websites or many other operations which we would do manually but take a lot of time. Here's a sample sheet that sends out emails. 
Sample

Enjoy!

Send an Email with a Function: Part 2

There are times that automated email can be very helpful. The concept I'm going with here is aligning to a form response. When we ask folks to sign up for something we sometimes need them to have materials or may want to customize the response. Yes, in Forms you can customize a response to the form but what if you want to get fancy? Say people are filling out a form for an audition (I was in the entertainment industry so I'm using an example personal to me) they are going to need to audition materials and information for the particular role they are auditioning for. Here's where a script can help you to select who receives what.

Let's go back to our form we were collecting emails on. We'll keep this simple to begin then I'll give you a case where it can be expanded.

Open the Scripts Tools>Script editor. You should have:
function formResponse(e){
   Logger.log(e.values[0]);
}

Try changing it to:

function formResponse(e){
   Logger.log(e.values);
}

Submit a form response again. 

The 0 index of the form response is the timestamp. If we modify our log to get the value of e, Logger.log(e); we will get all the information returned. Logger.log(e.values); will return all the values of the form response. 
We want to identity the index of the email address. If you are collecting emails automatically if will likely be index 1 or e.values[1]. If you are collecting emails in your form it will be listed in the form at an index relative to all other information you may be collecting. If you have turned on the automatic collecting of email and turned it back off you may have also created an index with no information or null. 
Your incoming data may look like this [9/16/2018 11:03:52, clay@claycodes.org, Clay Smith, Danny] or [9/16/2018 11:05:21, , Clay, Danny, clay@claycodes.org] or something else. 

Each index is separated by a comma. If we want to get the values of a particular index we can use the index number. There are other methods but this is a good concept to start with. 

function formResponse(e){
   Logger.log(e.values);
}

Our function returns all the values to the log. It's now time to start doing something with the values. We want to send an email when some one submits the form. Let's set a variable to be the email address. We can do this with: 

var email = e.values[4]; 

The 4 index is from the image sample above. Your index may be different.

Next let's identify a file URL we want to include in our email. To make it easy here's a link to Bloom's Taxonomy verbs. https://drive.google.com/file/d/0BxUJf-bQ7_PzNnFKMXdOUjJGZGM/view?usp=sharing
We can assign this link to a variable by typing: var fileurl = 'https://drive.google.com/file/d/0BxUJf-bQ7_PzNnFKMXdOUjJGZGM/view?usp=sharing';
I've made up the variable name fileurl just like I did email. I'm using the apostrophe, ', mark to indicate the data is a string or typed letters and not something to be calculated. The apostrophe encloses the information on each end. 

We'll make a variable for the body and include some characters which will be escaped. Meaning they are normally reserved for programming but we will tell the program not to read them as part of the programing. 

var body = 'Thanks for filling out the form here\'s a document to read before we begin. \n ' + fileurl;
The \ is used to escape characters. The apostrophe typically represents the end of a string of characters, in this case we're using it grammatically. So we escape it with \'. Looking at the other escape character we see \n representing a new line. The + symbol is used to put things together. just like we would add 2 and 2 to get 4 a string gets added to make a longer string. The fileurl is a variable so it doesn't go in apostrophes or the system will think it's the word fileurl. 

Now let's put these elements together as an email. 

var mail = GmailApp.sendEmail(email, 'Subject goes here' , body);

The GmailApp loads a library of possible functions. We are using the pre-made sendMail which takes in three inputs, an email address, the subject of the email, and the body of the email. We could have used a variable for the subject but here I've entered it as a string to build on the idea that we don't have to always create variables. we could also replace the variable email with e.values[4] and it will still function.

Putting it all together looks like:

function formResponse(e){
  Logger.log(e.values);
  var email = e.values[4];
  var fileurl = 'https://drive.google.com/file/d/0BxUJf-bQ7_PzNnFKMXdOUjJGZGM/view?usp=sharing';
  var body = 'Thanks for filling out the form here\'s a document to read before we begin. \n ' + fileurl;
  var mail = GmailApp.sendEmail(email, 'Subject goes here' , body);
}

Google handles a lot of security for you. To be sure all the security is engaged for our function let's try running it. It will give us an error but before it does it will go through the permission checks and allow the mail feature to work. Click Run>Run function>formResponse
Accept the permissions and we'll be ready to submit through the Form. 




Go ahead and submit a new form with your email address. Check your email. You should get the response from the script you wrote. Congratulations!!!

So here's a use case which may help. Let's use the audition example. We'll add a dropdown of role with two selections. Danny or Sandy. We'll create an if statement to change the fileurl based on the role. We could also change the text of the body and subject by using the conditional statement but we'll keep it simple. 

function formResponse(e){
  Logger.log(e.values);
  var email = e.values[4];
  var role = e.values[3];
if(role == 'Danny'){
   var fileurl = 'https://drive.google.com/file/d/1-hGi-F7EAi4pBXoDoZHzLQ2qasJ4ie2F/view?usp=sharing';
}else if(role=='Sandy'){
   var fileurl = 'https://drive.google.com/file/d/1t7bVocmj9QaHApIIlc91l3RARg4gXoKa/view?usp=sharing
}
  var body = 'Thanks for filling out the form here\'s a document to read before we begin. \n ' + fileurl;
  var mail = GmailApp.sendEmail(email, 'Subject goes here' , body);
}

onFormSubmit Do Something: Part 1


Did you know Google Scripts can be triggered when a form submits to do something? Assigning a function to the onFormSubmit trigger and it will call that function. Let’s build one now to email a response to a submitted form.

We all have sign-ups we use with our school community. Sometimes when some one signs up we need to provide them with a document and other information. With Google Apps Scripts we can automatically send an email to the person that signed up with attachments.  

Let’s explore. 
Start by creating a form. Be sure to turn on the Email capture or make a field for people to type their email and validate it as an email. Add any other questions to your form that you like. Keep the Allow only 1 response off so we can test with our own email multiple times.

Our script will be built in Google Sheets. IN the form responses link the responses to a new sheet. Open the sheet and select Tools>Script editor. The editor will open in a new tab. Give a name to your script. Remove the pre-populated function, myFunction, so we are working with a blank area. Let’s begin our function:

function formResponse(e){
}

I’m calling it formResponse because it makes sense to me. I made up this name for the function. It’s also case sensitive. So if I use it later I have to be sure it’s typed exactly the same way. 
This function is going to take an input of e.  e represents the form response. Let’s first take a look at what e looks like when it’s received. We’ll use the Logger for this. In the function type Logger.log(e);

function formResponse(e){
   Logger.log(e);
}

Now let’s hook up the trigger before testing. Click on the button that looks like a clock. 

This will open the Trigger settings. Click "No triggers set up. Click here to add one now." to add a trigger.






It will likely automatically populate the function we create. The Run column identifies the function to activate. The Events column gives the selection to happen either when something in the spreadsheet happens or time based. The last column is the specific event triggering the function. We can add more than one function to activate when triggered and multiple triggers.


Click Save to save the trigger to our form. You will be asked to approve some permissions. Click Review Permissions. Select your account, then click Allow on the next screen.


Now let's see what data looks like when it comes into the form. Open the form in submission mode. Fill it out and click submit. Our function will have captured the submission in the Logger. To get to the logger click View>Logs. This will open a window floating over our script with the data from the form submission.

When we look at the Logger we are seeing the form submission in the form of a program object. Let’s look closely at the label values. Notice it equals an open square bracket, "[",  then some comma separated information followed by a closed square bracket, "]". This is the information submitted through the Form as a list. A list is separated data with an unseen reference called an index. The index starts counting from 0. The first piece of data in the list is the timestamp. Let’s grab the timestamp and show it in our log. To return that value only we would use the input object e and the array values in e with the index 0. Logger.log(e.values[0]);

function formResponse(e){
   Logger.log(e.values[0]);
}

When we look at the Logger now we see only the timestamp. With this method we can identify the Email or other response data. We'll look further into how to use the response to do something in the next post. 

Social Emotional Well-Being During Online Teaching and Learning

The world is a bit nutty right now because of the COVID-19 pandemic. Schools are switching to online learning and parents are working from...