Make & Send Kids Reports

In this post, we’re going to look how we can create kids reports from a Google Sheet and convert them into individual PDFs made from Google Docs, then email them to the parents. This could of course be adapted to send any types of reports.

Kids Reports - 54

The teachers will fill in the spreadsheet and as its a Google Sheet will be able to do it at the same time as each other, using drop-down menus to make it quick and easy to do. The code will convert this information into individual PDF reports, which will be stored on our Drive and then sent via email directly to the parents. The process is completely paperless and free!


Setting up the spreadsheet

In one sheet, I’ve set up the columns as below:

Kids Reports - 2

Kids Reports - 3

We have two classes with a few students in each. Then we have the areas we want to report to the parents. In the blue part, their language skills and knowledge; in the pink part their behaviour; their attendance, exam marks, and the recommendation for the following year. In the final column, we will add the links to the individual PDFs.

Create a second sheet and call it “Options”. This is where we’re going to store the options for some drop-down menus on the main sheet. First, let’s set up the drop-down menus for the language feedback.

Kids Reports - 4

Select the cells where you want to add the drop-down menus.

Kids Reports - 5

Right-click and select Data validation from the menu.

Kids Reports - 6

In the dialogue box that will appear, click on the box next to “List from a range”.

Kids Reports - 7

This will ask you for where that data is. Instead of typing in the box, open the Options sheet.

Kids Reports - 8

Then select the feedback options for language.

Kids Reports - 9

This will add the range in the dialogue box.

Kids Reports - 10

Click ok, then in the main dialogue, I usually select “Reject input”, so that teachers can’t add any other comments in this columns. Click Save.

Kids Reports - 11

As we can see, it’s now added the drop-down menus.

Kids Reports - 12

Clicking on one of the cells, will open the menu and the teacher can just select the comment they want to add.

Kids Reports - 13

Now do the same for the behaviour comments.

Kids Reports - 14

So, you should have a different set of feedback options this time.

Kids Reports - 17

Now in the exam marks, let’s prevent the teachers adding numbers that are not between 0 and 10. Here in Spain, the norm is to receive a mark out of ten, which sometimes our teachers don’t realise or forget and they add a percentage mark. Select the cells, then open the data validation dialogue as before.

Kids Reports - 18

This time select “Number” from the criteria and “between”. Then add 0 and 10 in the boxes. This time I’ve ticked the “Show validation help text”. This will show the message in the box, if they don’t write a number between 0 and 10.

Kids Reports - 19

Here’s the help text they would see.

Kids Reports - 20

Finally, as this is the end of the year, the parents will want to know, if their child can go to the next level, needs to repeat the year, or can jump a level. So, for each we will add the 3 options. As the options will depend on their current level, we’ll need to do this per level. So, first select the recommendation cells for class A1.

Kids Reports - 21

Open the data validation and select the 3 options next to Class A1. As you can see, the menu options don’t have to be in columns, but can be across rows too.

Kids Reports - 26

Kids Reports - 25

As we can see, it’s added the options available for that level. We then do the same for Class A2, and so on.

Kids Reports - 24

Now the teacher fills out the sheet, choosing the options from the menus we’ve created, which makes it really quick for them to fill out. They also add the attendance figures and exam results. I usually add some conditional formatting to the menus, as it makes it more visually clear who’s doing well or not. See my post on Conditional Formatting if you’re not sure what to do.

Kids Reports - 27

Kids Reports - 28


Report template

Now, we need a blank report template. Here, I’ve created one in a Google Doc using tables to organise the information. As we’ll see in the code later on, accessing tables is easy to do, and so we will be able to add our students’ data with ease.

Kids Reports - 29

Create a folder where you’re going to store the finished reports. Then get the URL of the folder and paste it into the Options Sheets in cell A10, and the URL of the report template into cell A12.

Kids Reports - 50


The code to make the PDF reports

Once the teachers have filled in the data, we can run the program to make the individual reports in PDF format. From the Google Sheet open the Script Editor from the Tools menu. Delete the default code in there. I’m going to assume you know the basics of Apps Script, but I will explain what each step is doing.

Kids Reports - 30

First, I want to add a menu where we can run our code from.

Line 1: Call the function onOpen() so that when the spreadsheet is opened, the menu will appear.

Line 2: To set up a menu we need to get the spreadsheet UI (user interface). We do that by using getUi().

Line 3: We then create the menu on the menu bar using createMenu() and in the brackets give it a name. This is the name of the menu on the menu bar. Note, no semi-colon at the end.

Line 4: Then we need to add an item to it, which will run the createReports function we’re going to write. So, first we use addItem(), then in the brackets we need to state the name of the menu item, and the function we want connected to it. So, here I’ve called it ‘Make pdfs’ and it will run the createReports function when clicked.

Lines 5-6: Finally, we need to add it to the UI. We do that by using addToUi(). Then we close the function with a curly bracket.

You’re likely to use this chunk of code for numerous programs, so I would save it as a script editor file in your Drive, so you can just copy and paste it in.

Kids Reports - 31

Line 8: Now, we write a new function called createReports.

First, we need to get some of the data from our active sheet. Make sure, when you run the code that the first page with the report information is open and not the Options page.

Line 10: Store the active sheet in the variable ss.

I’ve used getActiveSheet() here and not the sheet by name just because if you have lots of students, you may split them into different sheets, so you would open the sheet you wanted and then run the code for that set of students. However, if you have a smaller set of students, you may prefer to do it all on one sheet, and therefore, use the getSheetByName() method.

Line 11: Then let’s get all the data on our sheet and store it in the variable ssData. We do this by using getDataRange() then getValues(). Later on we will get the data we need from this variable, rather than get it from the sheet every time, which would slow down the program.

Line 12: Next, we need to know where our data ends, so we get the last row number, using getLastRow().

Line 13: The start row number will be row 2, let’s set up the variable rowNumber with 2. This is the counter we’re going to use to go down each row and get that particular student’s data.

Line 14: We will sometimes be using row numbers and sometimes the row number within an array, and as arrays numbers start at 0 not 1, we need a number that is 1 less than the rowNumber. We’ll store it in the variable rowNumbersA. I could have just written = 1, but if we ever change our sheet and the start row number changed, we would only have to change one variable.

Now, let’s get the report folder URL and the report template URL from the Options sheet.

Kids Reports - 50

Kids Reports - 32

Line 17: First, let’s get the Options sheet by name, using getSheetByName().

Line 18: Then, we get the URL which is in A10 (row 10, column 1).

Line 19: We’ve got the URL but what we need is the folder ID. We can extract that from the URL by removing the first part of the URL up until /folders/. Here, we get the URL stored in reportFolder and use the replace() method, to replace one thing with another. In the brackets, I’ve added the first part of the URL, which we want to remove, then after the comma, add two quote marks, to show that we’re replacing the text with nothing. I.e. We’re removing it.

Kids Reports - 33

Line 20: Similar to before, but this time we get the report template URL.

Line 21: File URLs are a little more complicated, as we need to remove the first part, like above and also the /edit part at the end. The best way to do this is to use a regular expression. This will look for a particular pattern in the URL and remove the parts we don’t need. First we use the match() method and then in the brackets add the regular expression. It looks strange, but just add what’s there, believe me it works!

Note, all the code written so far, is before the loop we’re about to make, as it only needs to be run once, so we don’t want it within the loop, as it would be run multiple times, slowing down the program. Now, we create a loop to move down the rows of data, and for every row, it will grab the data needed and create a report from it.

Kids Reports - 34

Line 24: We set up our for loop, starting at row number 2, continuing until the row number is equal to the last row (the last student on our sheet), and it will move down 1 row at a time.

Now, let’s get the data about the student, which we stored in ssData. Every line is similar, it’s just getting a different position each time which corresponds with the different columns on the sheet.

Kids Reports - 35

Line 27: We set up the variables and start off with the student’s name, which we’ll store in studentName. The variable ssData is an array of data, as it contains all the data from all the rows and all the columns on our sheet. So, to get the specific data we want we need to tell it, which ‘row’ it’s on and the ‘column’. Our references will have to be array references so will be one less than are on the sheet. The row we use the rowNumberA variable, so the first time we go around the loop, this will be 1 and this relates to row 2 on the sheet. The column is 0 as this relates to column 1 on the sheet.

At the end, add a comma, to connect it to the next variable, without having to repeatedly write var every time.

Continue doing the same for all the pieces of data we need and on the final line, close it by adding a semi-colon.

This chunk of code is optional, as we could put the ssData parts directly in the report part below, but using easy to understand variable names it helps us remember which piece of information is which, so we don’t mix any up.

Now, we need to make a copy of the blank template.

Kids Reports - 36

Line 46: First, we get our template file by its ID. Use the DriveApp Class and getFileById() and add the variable reportTemplateKey where we stored the ID in.

Line 47: Now, make a copy of it, by using makeCopy() of the variable reportTemplate we just created. In the brackets we state the name of the new file. Here, I’m going to call it “Informe-Class name-Student’s name”, so I state the text, then, as the class and student’s name will come from the data for that particular student, we add the variables class and studentName. So, we will have something like, “Informe-Class A1-Barney Pebble”.

In case you were wondering, I’ve just realised I left the first bit (“Informe”) in Spanish, yours would be ‘Report’. The code I’ve adapted this from, was for our reports, which are all in Spanish.

Line 48: Finally, we need to get the ID of the new file, so we can then work with it. Use getId() for this.

Now let’s get our new report and add the student’s data into it.

Kids Reports - 37

Line 51: To edit the document we need to ‘open’ it by using openById() and adding its ID in the brackets.

Line 52: To write something on a document, we first need to get its body. So, logically we use getBody() and store this in the variable body.

Now, we need to access the tables one by one and the rows and cells within each one. We then set the text in this cells using the variables we created in lines 27 to 44.

Kids Reports - 38

Line 55: First, we need to get the first table in the document. We get the body, then get the table using getTables() and state which table number we want. As with arrays, the first table will be table 0, so we add a 0 in square brackets.

Line 56: Now, we need to get the table row we need and the cell number on that row. We use getRow() to get the row, and getCell() to get surprise, surprise, the cell. So, for the student’s name we need the first row and the second cell in that row, so we state getRow(0) and getCell(1). Then we use setText() to state what text we want to add in the cell. In the brackets we add the studentName variable.

The rest of the lines are in a similar format, so for example, the class name is in row 2 (1) and cell 2 (1).

Kids Reports - 39

We then get the next table and repeat the same process.

Kids Reports - 40

Kids Reports - 41

Kids Reports - 42

With the exam marks, I want to add the number to one decimal place and add some text to show it’s out of 10.

Lines 79-80: In the brackets for setText we state the variable written and add the toFixed() method and as we want 1 decimal place, we add 1 in the brackets. Then to show it’s out of 10,  we add a plus and the text ” /10″.

Kids Reports - 43

Line 84: As we ‘opened’ the report to edit it, save and close it to save all the changes. We get the new file’s ID in openReport and use the saveAndClose() method.

Now, let’s convert this file into a PDF.

Kids Reports - 55

Line 87: Get the new file by its ID and convert it to a PDF by using getAs() and in the brackets ‘application/pdf‘. Store that in pdf.

Line 88: Then, we need to give the PDF a name. Here, I just want it to be the same as the Google Doc. So, we use setName() and in the brackets, we get the name of the Google Doc using getName() and add the .pdf extension.

Line 89: Now, we create the new PDF using the DriveApp Class and createFile() and in the brackets we pass the pdf variable.

Line 90: Finally, get the ID of this new PDF, which we will use to add its URL to the sheet and in the emails.

This has made a Google Doc and a PDF in our My Drive, so as we want the PDF in our reports folder we need to move it there. Unfortunately, there isn’t a move method, so we’ll have to add a copy and then remove the original.

Kids Reports - 56

Line 93: Get the report folder by getting its ID from the reportFolderKey variable we created in line 19.

Line 94: Add a copy of the pdf to the report folder by getting the folder ID, then using addFile() and in the brackets adding the report PDF file.

Line 95: We then remove the original PDF file from My Drive. With My Drive, we just use DriveApp then the removeFile() method then state the file we want to remove.

Line 96: Finally, we want to remove the original Google doc on our My Drive. Removing a Google document is a bit different from removing a PDF. Here, we get the file ID using getFileById() and then use setTrashed() and state true in the brackets. This will remove it to the trash.

Now, let’s add the link to the new PDF in our report folder on our sheet, so we can access it directly from the Sheet and so that we can add it to the email to the parents.

Kids Reports - 57

Line 99: We get the current row using rowNumber and get column 20(T) the PDF URL column.

Line 100: Then, we set the formula in that cell to show a hyperlink to the PDF report. We do this by using setFormula() then adding our formula. The text parts need to be between quotes and variables without. Pay close attention to the use of quote marks here.

Note, it is possible to get the file’s URL and add that directly here, but it tends to be a bit longer, as it contains unnecessary information at the end of it, so I prefer to do it this way.

Kids Reports - 58

Line 102: We’re at the end of our loop, so we need to increase our array row number counter by one, so we state the variable rowNumberA and add two pluses.

Line 103: Close the loop with a curly bracket.

Kids Reports - 59

I always like to display a message to the user, to show the program has finished. So, here, we’ll add a toast message, which will pop up from the bottom of the screen telling us the reports have been made.

Line 104: Get the active spreadsheet and use the toast() method. Then state the message, title, and how many seconds you want the message displayed.

Line 105: Close the function with a curly bracket.


Making the reports

Now, we’re ready to make the reports. Refresh the page, so that the Reports menu appears. Then click Reports>Make pdfs to run the program. The first time you run it, it will ask for authorisation, click the review and allow buttons.

Kids Reports - 60

As we can see on the sheet, it has added the links to the PDFs for each student.

Kids Reports - 51

In our reports folder, it has created a PDF report for all our students.

Kids Reports - 52

If we open one of the reports, we can see it has been populated with that student’s information.

Kids Reports - 53

It takes about 50 seconds to create all the reports. As there is a runtime limit of 6 minutes, normally you can only make about 50 reports this way in one go. To make more, either store the students on different sheets, or what we do is tweak the code, to ask for a starting row number and finishing row number, so the reports are made in batches. This also means we can make the reports as soon as a particular class’s data is ready.


Emailing the reports to the parents

The final part will be to send the reports to the parents. We could include this code within the one above, but as we don’t want our program running too long, I’ve set it up so it runs separately.

First, let’s update the onOpen() function, so that an “Email reports” option appears in the menu.

Kids Reports - 72

Here, I’ve just added line 5. When we open the report spreadsheet again, we will see both menu options.

Kids Reports - 71

We’re going to send the email below:

Kids Reports - 70

It contains our school’s logo (a fictitious one), it’s personalized by including the student’s name in the email title and in the main text, and it contains a link to the report PDF.

So, how do we do this?

In a new script file (from File>New) write the following code.

Kids Reports - 62

Line 1: Set up a new function called sendEmails.

Line 2: Get the active sheet and store it in sheet.

Line 3-4: Get the last row and column, for later use.

Kids Reports - 63

As we’re going to add the school logo to make it more professional looking, we need to get the image, which is stored on our Drive.

Line 7: We get the image’s ID (the random numbers and letters part in the URL) and use getBlob(). The blob will contain the data of the image.

Kids Reports - 64b

Now we need to set up a loop to go down the list of students.

Line 10: Set up a for loop, starting on row 2, and which will stop when it gets to the last row and sends the last student’s report.

Kids Reports - 65

Now, we need to get the student’s name, email and pdf link.

Line 13: First, we get the data on the latest row and store it in data.

Lines 14-16: Then, let’s store the student’s name, email, and pdf link in separate variable ready to use in the email. For example, the student’s name is in the first column and so we get it from position 0 in the data array.

Now, we need to set up our message and store it in the variable message.

Kids Reports - 66

Line 19: First, let’s start with the school logo. Here, we’re using a bit of HTML to add the image (using the img tag) and we’ll state the id as logo. I’ve also controlled the height and width here. Pay attention to the quote marks.

Now, let’s add the rest of the message.

Kids Reports - 67

Lines 21-26: Each line is adding to the message variable using +=. To keep it simple, each line will be a separate paragraph (using the < p > tags). We open the < p > tag add the text we want and then close it using < /p >. Text needs to be within quote marks and variables like name, need to be without but are connected with the plus marks.

Kids Reports - 68 (1)

Finally, we need to send the email. This contains 3 main parts: email you’re sending to, email title, and email message.

Line 29: We use the MailApp and sendEmail() to send emails.

Line 30: We use emailTo to state the current email address we want to send to. Then in the email title I want to call it “Report-and the student’s name”, so the parents can see it’s a message about their child. The message part we’ll just add 2 double quote marks, as the next lines will add the details of the message. End the line with a comma.

Line 31: In curly brackets we’ll add the options we want. First, add the inline image, using inlineImages: then within another set of curly brackets, state the id logo: and the variable image (where the image blob is stored).

Line 32: Then we also need to add the body, which is a HTML body (htmlBody:) and the message variable, which contains the message we wrote above.

Lines 33-34: Close the loop and then close the function.

In seconds, this will send the personalised emails to all the parents.

Kids Reports - 69

Kids Reports - 70

We introduced a system similar to the one I’ve described above, which replaced an antiquated card report system and its made the whole process so much easier for everyone involved, whilst providing the necessary information to the parents in a more modern way.

One limitation with this system is that you can only send 100 emails a day, so if you have more students and want to send all the emails in 1 day, you will need to use a Google Sheets add-on like YAMM and pay for more emails (e.g. up to 400 in a day), or send the emails from different email accounts, not ideal but a workaround.


Here’s a copy of the spreadsheet with the code and here’s the report template.

You can find the code here at GitHub.


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Baz Roberts (Google+Flipboard / Twitter)


Apps Script – Issues reporting form, log & email

In this post, we’ll look at a way to create a quick and simple system to report maintenance and IT issues in the classroom.

The teacher fills out a Google Form on their phone, this gets logged on a Google Sheet, and as we work in a multilingual environment, it uses Google Translate to automatically translate the issue before emailing the relevant people.

Issue reporting - 32


Setting up the form and sheet

I will assume you know how to create Google Forms and link them to Google Sheets. If don’t then check out my posts on Google Forms.

I’ve created a simple form for the teacher to fill out. They fill out the problem, whether it’s an IT or maintenance issue, their name, and choose one of the rooms from the drop down menu. So, nice and quick to fill out.

Issue reporting - 3

Then link that with a Google Sheet and you should then have the questions in that sheet, as below (on the sheet called “Form responses 1”):

Issue reporting - 4

Then, add 3 more columns on the end and label them, Translation, Comments, and Status. Plus, I’ve added a bit of formatting.

Issue reporting - 5

Then, add a second sheet and rename it “Emails”. Add in the first column, the emails of those who will receive a maintenance issue and in the second column, those who will receive an IT issue.

Issue reporting - 11


The code

Now, we need to add the code to the sheet. Open the Script Editor from the Tools menu.

Issue reporting - 6

Rename the file, e.g. Maintenance-IT Log.

Issue reporting - 7

Delete the default code that’s in there.

Issue reporting - 18

Line 1: First, we set up the function. Here, I’ve called it onFormSubmit, just to remind myself that I will need to set up an onFormSubmit trigger later on.

Line 2: We then need to get the active spreadsheet and store it in the variable ss.

Line 3: Then we need to get the sheet where the responses will appear, called “Form responses 1”, using the getSheetByName() method.

Issue reporting - 19

In this part, I just want to format the data on the sheet, so that the data is all centered, it all has text wrap, and that it has borders around it. Basically, to makes it look nice and more readable.

Line 6: We get the sheet with frSheet and get the range of all the data currently on that sheet, using the getDataRange() method. We then add our formatting to that range. Starting with centering the data, using setHorizontalAlignment(). Note, there’s no semi-colon at the end here.

Line 7: Then we add text wrap to all the data, using setWrap() and setting it to true. Here, as we are still attached to the getDataRange() part, we only need to use a dot and then setWrap(true).

Line 8: Finally, we add the borders to the data, by using setBorder(). There are 6 potential places for the lines (top, left, bottom, right, vertical, horizontal), but as I want all the borders to have lines, I state true for each position. This time we add a semi-colon, as we’ve come to the end of formatting this range.

Issue reporting - 20

Now, we want to get the last row, which will contain the latest data, and the last column. We’ll use this later on.

Line 11 & 12: To do this we use getLastRow() and getLastColumn().

Now we want to add a couple of pieces to the latest row. The first will be the status of the issue, which will be open at first. Then, as we want to be able to work with multiple languages, I want to translate the problem reported into Spanish, as I’m working in Spain and the maintenance and IT guys are Spanish.

Issue reporting - 21b

Line 15: First, let’s get the status cell on the latest row. As this is in the last column, we simply get the range using the lastRow and lastColumn variables we just set up. Then, set the value to “Open”.

Issue reporting - 22

Line 19: First, we need to get the cell where we’re going to add the translation. Here, I’m going to use A1 notation with the getRange() method. So, I know it will be in column F, and I already know the last row number, which is in the variable lastRow, so I combine the two together, using the plus sign.

Line 20: Here, we’re going to add a formula to the translation cell. We’re going to use the GOOGLETRANSLATE and DETECTLANGUAGE functions. See my post on how these work in Sheets. As the cell reference will be different every time, we need to refer to the numbers of rows and columns away from the translation cell the original problem text is.

So, in the brackets, we add single quote marks, then our formula. This will look at the cell on the same row as the translation cell (row 0) but 4 columns to the left (row -4, i.e. column B), it will detect what language it is and translate it to Spanish (“es”).

Here’s what it will look like:

Issue reporting - 10

Issue reporting - 23

Now, let’s get the data from the latest row, as we want to include that in our email to maintenance or IT. We could just write the lastRowValues parts directly in the email part, but remembering which number refers to what information, can be confusing sometimes, as in our email the information isn’t in the same order as it is on the sheet, so let’s store them in easy to understand variables.

Line 23: First, we get the values all along that latest row and store them in lastRowValues.

Lines 24-29: Now, we get the individual pieces of information stored in the lastRowValues array. So, for example, to get the timestamp, we look at position [0][0]. The first 0 is always 0 as we only have 1 line of data, so the row will always be 0. And the second 0, refers to the place in the array the timestamp is, which as it’s the first piece of data (column 1), the position is 0. Remembering that arrays always start with 0 not 1.

Issue reporting - 24

Dates, are always a bit more complicated to deal with, as if we use the timestamp as it is, it will come out like this: Fri Apr 14 2017 18:43:13 GMT +0200 (CEST), not the most succinct date. So, we need to shorten it to a format we want.

Line 32: First, we need the time zone we’re working in. Use Session.getScriptTimeZone() for that. Note, the capital “S” in Session, as it’s a class.

Line 33: Now, let’s shorten the timestamp. To do this, we use Utilities.formatDate(). This needs 3 arguments, the date & time you want to convert, the timezone, and the format of the date and time you want. The new format is between quote marks.

Issue reporting - 25

At the bottom of the email to maintenance or IT, I want to add a link to the log, so they can access it directly from the email, to update what they’ve done in the Comments column.

Line 36: This is easily done, just get the URL of the active spreadsheet (ss), using getUrl().

Now, we need to prepare the email we want to send. If you’ve read my previous posts on sending automatic email from form submissions, then you’ll know how easy it is. In those posts, I just used basic formatting in the email. Here, let’s use some basic HTML to make the email easier to read and a little more professional. Plus, see this site for more info on HTML.

Now, we need to state what we want to include in the body of the email. Lines 40-51 is all stored in the variable emailBody. Below is the email format I want. There’s an “Issue” header, then the the various bits of information from the form. Note, the title of each is in bold. Plus, I’ve added some lines, organise it a bit better. Then, at the bottom is the link to the log sheet.

Issue reporting - 31

Issue reporting - 26

Line 39: First, we need a subject for our email. Here, I want it to state “Issue -“plus the date of the issue, using the shortTimestamp variable.

Line 40: As a general rule, the fixed text we want to add needs to be in quote marks and the variables we use to add the form data, are without them. So, first I want the title “issue” to be an underlined header. So, I’ve added a header 3 tag < h3 >and an underline one < u >. Then, I close the tags with </ h3 > and </ u>. Note, that every line except the last one will need a backwards slash \.

Line 41: I want the word ‘Problem’ in bold, so I use the < strong > tag to do that. Then I close it before the form data, so that it’s not bolded. Note, the use of the pluses to connect the information together. Plus, I use the line break tag < br /> to create a new line.

Most of the other lines are similar. In Line 43, I want to add a horizontal line to separate the information, so I use the < hr /> tag to do that.

Line 51: At the end, close the quotes and end with a semi-colon.

Issue reporting - 27

Now, we need the email addresses of those we are going to send the email to.

Issue reporting - 11

Line 54: As you’ll remember, we set up an Emails sheet. So, first we get that sheet.

Line 55: Then, let’s find out how many rows have email addresses, so let’s just use getLastRow() to do that. Note, if you have one column with more emails than the other, it doesn’t matter, as if you’ve got some blanks as the email service will just ignore them.

Now, we need to decide which column we’re going to use. To do this, we use an if statement, which will look to see if it’s an IT issue, and if not, it will default to the other emails.

Issue reporting - 28

Line 57: We check to see if the value in column C (“IT or Maintenance?”) is equal to “IT”. Remember to use the triple equal signs for ‘equals to’ not a single equals sign.

Line 58: If it is, then it gets the email addresses from column 2.

Line 59-61: The alternative is that it isn’t (else). In which case, it gets the email addresses from the first column.

Issue reporting - 29

Now, we put it all together, to send the relevant people the email.

Line 64: We use GmailApp.sendEmail() to send the email. We have 3 main arguments: who the email is going to (emailTo), the email subject (emailSubject), the body of the email (emailBody). As we’re using HTML, we also need to add the option of htmlBody. Options go between curly brackets. First, write htmlBody: then the emailBody variable.

I also want a specific person to receive an email if someone replies to this email. Remember this email is being sent from the account in which the code has been set up under, but you may not want that account to be emailed to. To do so, just add replyTo: and the email address you want between quotes.

Line 65: Close the function with a curly bracket.


Setting up the onFormSubmit trigger

We want the email to be sent automatically when the form is submitted, so we need to set up the onFormSubmit trigger to do this.

Issue reporting - 12

Click on the clock symbol in the toolbar.

Issue reporting - 13

Click “No triggers setup”.

Issue reporting - 14

Here, you’ll need to change the Events.

Issue reporting - 15

Change “Time-driven” to “From spreadsheet”.

Issue reporting - 16

Change “On open” to “On form submit”.

Issue reporting - 17

Then press “Save”.

The first time press the play button on the toolbar, to prompt the authorisation of the script.

Issue reporting - 8

Click “Review permissions”.

Issue reporting - 9

Then click “Allow”.


Reporting an issue

The teacher fills in the form and submits it.

Issue reporting - 10

The information appears in the sheet and the code then adds the translation and the Open status. It then send the relevant parties an email with the summary:

Issue reporting - 31

As you can see, the issue is clearly presented, allowing the person to take action.

Once the issue is resolved, the person would then add the action taken in the comments on the sheet and change the status to Closed. As the sheet is shared with everyone, the open issues are clearly visible, prompting quick resolution of them.


Here’s the link to the sheet containing the code.

https://docs.google.com/spreadsheets/d/1i2paFxUjxun6pvLgfAf247Uw6eJGIwK19SLWrMD4Vdc/copy

Here’s the form:

https://docs.google.com/forms/d/1j9hC0S-P7BsbX5873v9BGptw2PbELOI8uuWb5QvjrdM/copy


Here’s the full code:

Issue reporting - 33Issue reporting - 34


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Baz Roberts (Google+Flipboard / Twitter)


Forms & Sheets – Attendance System

In this post we’re going to make an attendance system using Google Forms and Sheets. The user will fill in the attendance on the form (possibly on their mobile) and the data will then be stored and organised automatically per student. This will use a Form add-on called FormRanger and will use a range of Sheet functions to do organise the data for you.

We will make an attendance sheet like this:

Attendance - 75

And an attendance summary like this:

Attendance - 76


Setting up the form

Create a new form. The form itself is very basic and just includes the date of the class and a question where we’re going to populate the class list. Make the date question a required one, just to make sure it’s always entered.

Attendance - 1

Click on the settings cog and under General, the only option I select is “Edit after submit”. This can be useful if a student arrives a bit late and I’ve already done the attendance. It will allow me to go back to the class list and update it.

Attendance - 2

Under Presentation you may want to tick the Show link to submit another response, if you do, the attendance of more than one date at a time. Change the default confirmation message.

Attendance - 3

Then we need to link the form to a Sheet. Click on Responses and the green sheet icon to create a spreadsheet.

Attendance - 4

By default, it’ll create a new one. You may want to change the title here. Click Create.

Attendance - 5

The Sheet will open and we can see the 3 columns it’s set up.

Attendance - 6

Create the following sheets:

Attendance - 24

I’ve changed the name of the ‘Form Responses 1’ sheet to G1, partly so we now it’s referring to group 1 and partly because it’s a shorter name! I’ll go through each sheet later on.

For now, just add your class list to the ‘ClassLists’ sheet like this:

Attendance - 7


Populating the class list on the form

We could just type in the student names on the form, but I often use an add-on called FormRanger, which connects a question in the form to a range on a sheet. This then updates the form automatically if there are any changes to that range. In other words, if the students change in the class, the form will be updated.

If you don’t already have it, download the add-on by clicking on the 3 dots menu and choosing add-ons.

Attendance - 56

Then type in formranger and click on the blue ‘Free’ button, to add it.

Attendance - 55

Click on the jigsaw piece icon.

Attendance - 8

Then select formRanger.

Attendance - 9

Click Start.

Attendance - 10

A menu will appear which has two main parts, the top part is where you link the question to the sheet and the bottom grey part is where you can set it up to populate the question automatically.

Attendance - 11

Click on the class list question in your form, then tick “Populate from values list”.

Attendance - 12

Then click on the grey drop-down menu and select “new values list”.

Attendance - 13

This will open a dialogue box, which contains 3 parts: Which spreadsheet you want the information from, which sheet, and then name the list.

Find the spreadsheet you want to use. If you’ve just been using it, it normally appears first. Click “Select”.

Attendance - 14

Now we need to tell it which sheet we want. Click on the “Select Sheet” drop-down menu.

Attendance - 15

Then select the sheet you want. Here, I want the sheet “ClassLists” where we added the class list earlier.

Attendance - 16

Now we need to tell it which column we want to use on that sheet. As our class list is in the first column, the column with the header “G1” is already showing. If you have a sheet with a number of classes on it, you’d have to click on the “Select column header” drop-down menu and select the specific column.

Note, as this works with columns, it’s important not to have anything else in the column, as this would appear in the form too. So, when adding class lists, just use that sheet to add columns of class lists.

Attendance - 17

On the right-hand side, we can see that it shows us the contents of that column, which we can see is the class list we want.

Attendance - 18

Finally, we need to name this value lists, with whatever name that makes sense.

Attendance - 19

So, for example, in the “Give this values list a name” box write “G1”.

Attendance - 20

Then, click the blue button.

Attendance - 21

On our form we’ll see the class list is now populated with the contents from that sheet.

Attendance - 22

If you expect possible changes to the class list or maybe you want to use the form for a different group in the future, then you can toggle the auto-populate questions options to on, so that the form checks every form submission or hour, for any changes and updates itself if there are.

Attendance - 23


Setting up the spreadsheet

Now, let’s set up the spreadsheet. We have 5 sheets. The data from the form will arrive and be stored in “G1”. Then a copy of it will be stored in “G1c”. Then this will be converted into an attendance sheet in “G1A”. The overall attendance figures per student will be displayed on the “Attendance” sheet, pulling in the data from “G1A”.

Attendance - 24

So, first let’s set up sheet “G1c” so that it automatically makes a copy of the data on sheet “G1”. In cell A1 type the following ARRAYFORMULA function. This will get all the data in column A on sheet “G1” and fill column A on sheet “G1c”. Copy and paste this into cells B and C, to do the same for those columns.

Attendance - 26

So, you should now have 3 columns like this:

Attendance - 25

Why do we need to make this copy? If we didn’t then what happens is that any row numbers we use on the next sheet will change as a form submission comes in. For example, if we want to use the data in cell A3, when a new form submission comes in, it will push that cell down, so that it becomes A4, and then it will update the reference on the other sheet to A4, which continue to be blank, as it should still be looking at cell A3, which has the data we want. The work around for this is to create a copy sheet as described above. (If anyone reading this knows a simple way to get around this I’d love to know it!)


Setting up the attendance sheet

Now let’s set up the sheet “G1A”. This is where we’ll see the attendance data more visually. We’ll see the list of students and their attendance will be shown per class date in the rows below.

Attendance - 29

At the top I’ve added the start and end dates.

Attendance - 27

As these will also appear on the first page “Attendance” then let’s add a reference to those cells. So, in cell B1 add this reference:

Attendance - 28

Cell B2 will have the same except it will refer to Attendance!B2.

Now, let’s add the list of students in row 3. On the ClassLists page the list is vertical, but here we need to show it horizontally, so we need to use the TRANSPOSE function to change it from vertical to horizontal.

In cell B3, type the TRANSPOSE function below:

Attendance - 30

The list of students will appear in each of the cells. Any changes to the ClassLists page will be replicated here automatically.

To work out the attendance, we first need to know how many classes there were in that particular period. To do this, we’ll count how many class dates are shown in column A from cell A5 downwards.

Attendance - 31

So in cell A4 we write the following COUNTA formula:

Attendance - 57

Now, we want to filter the attendance so that we only show the attendance of the classes between the start date and end date. To do this we’ll use the following QUERY function:

Attendance - 58

Phew! Long isn’t it? Don’t worry let’s go through it part by part.

First, we want to look at the data on the “G1c” sheet and we’re only interested in column B (the class dates).

Attendance - 59

Then we select just the class dates (column B).

Attendance - 60

Then we want to filter the dates by the dates that are the same or later than the start date on the Attendance sheet. Be careful with the syntax with this.

Attendance - 61

AND we want to filter the dates by the dates that are the same or earlier than the end date.

Attendance - 62

Finally, we sort the results in ascending date order.

Attendance - 63

At the moment, nothing exciting happens as we have no data, but you should get a message saying that the output is empty.

Attendance - 39

If you want to know more about how the QUERY function works, check out my QUERY post.

If the student attended, I want to add an X in the cell, if they didn’t then to leave it blank.

Attendance - 64

In cell B5, type the following formula:

attendance new1

It looks scarier than it really is. Let’s look at it in parts.

attendance new2

First, I use ARRAYFORMULA so that this formula works all the way down the column, without having to copy and paste the actual formula into the cells.

Then we have check to see if the cell B3 is blank, and if so, it leaves the cell blank. So, basically it’s checking to see if a student’s name is in cell B3, if it isn’t, it leaves it blank. More info on the IF function here.

attendance new3

Finally, if there is a student’s name and there’s a class date, it looks to see if that student’s name appears in the form responses on the “G1c” sheet. Using the VLOOKUP function, it looks for the student’s name from B3, then looks up the date from column A and looks on sheet G1c for that date and looks at the students that attended in column C on that date. If it finds the student, i.e. that they attended, it adds an X in the cell, otherwise, it leaves it blank. The IFERROR function is just there to leave it blank if it doesn’t find anything.

The final part on this sheet, is to count how many times a student has attended. We can do this by counting the number of Xs in that student’s column.

Attendance - 40

In cell B4, type the following formula:

Attendance - 41

This checks to see if B3 is blank, i.e. there’s is no student’s name. If it is, it leaves the cell B4 blank. If there is a name, it counts how many Xs there are in that column. See my post on countif if you want to know more about that function.

Copy cells B4 and B5 and paste them across to cells K4 and K5, so that there are 10 columns with the same formulas in them.

So, you should be left with a sheet like this:

Attendance - 29

I usually add a little bit of conditional formatting to show the Xs (i.e. class attended) more clearly. I just use the “Cell is not empty” option and fill the cell with a colour. See my post on conditional formatting if you don’t know how to do that.

Attendance - 71


Calculating and showing the students’ attendance

Now, the final part. I want a sheet that displays a summary of the students’ attendance for that period.

On the “Attendance” sheet, set up a table like this:

Attendance - 65

Now let’s grab the data from the different sheets. First, let’s add the class list.

Attendance - 47

In cell A4, add this formula:

Attendance - 67

This will get the class list from column A on the ClassLists sheet.

Then, let’s add the classes attended per student.

Attendance - 49

We get this from the “G1A” sheet (attendance sheet) from row 4. We use the TRANSPOSE function to convert it from horizontal information to being stored vertically. In cell B4 write the following formula:

Attendance - 50

We then want to add the total number of classes there were.

Attendance - 69

So, again we get that from the “G1A” sheet and the number was in cell A4. I’ve added an IF function, so that if there isn’t a student name in column A, it leaves the cell blank. In cell C4, write the following formula:

Attendance - 52

Finally, we work out the attendance percentage for each student.

Attendance - 68

We divide column B (attended) by column C(total). We need to add the full range (i.e. B4 to B13) as we are using the ARAYFORMULA function. I’ve also added an IF function to leave a space if there is no data in column B. In cell D4, write the following formula:

Attendance - 54

This leaves us with a summary of our class. Note, the total is 1 as even though we haven’t entered any classes yet, it’s counted the QUERY function as 1. You could change the total formula to get rid of this, but once the classes start being added this doesn’t get included.

Attendance - 66

I usually add a bit of conditional formatting to the attendance % column, to highlight those with bad attendance (in red) and those with good attendance (in green).


Putting it into practice

Let’s add some attendance and see what the final product is like.

The teacher opens the form on their mobile or on the computer and quickly adds who was there and submits the form.

Attendance - 70

Once submitted, they have the option of editing that response or submitting another.

Attendance - 72

The responses arrive in the “G1” sheet.

Attendance - 73

These are copied in the “G1c” sheet.

Attendance - 74

On the “G1A” sheet, this data is converted into the an easy to understand attendance sheet.

Attendance - 75

On the “Attendance” sheet we can clearly see the attendance of all our students.

Attendance - 76

To view the attendance for different periods all you have to do is change the dates and it will update the attendance accordingly.

You can make a copy of the sheet and linked form here.


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Baz Roberts (Google+Flipboard / Twitter)


Apps Script Basics – Variables and getting & setting values

In this post, we’re going look at how variables are set up and how they can store various pieces of information. We’re also going to look at reading and writing data from a spreadsheet, which is one of the most common tasks when working with one.

I’m going to use a simple example, where we have some data about three different students and we want to put all that data together in one single table in a different part of a sheet. Then, we’re going to format it a little bit to make it look better.

So, we will be getting data from these columns:

3Variables - 17

And writing it into these columns, all with Apps Script.

3Variables - 16

So first, what is a variable? It’s basically a container that stores information. You give it a name and state what’s in the container. All variables start with the keyword var. Then followed by the name for that variable. Apart from using keywords, you can call your variable pretty much anything you like.

3Variables - 20

Above, we have two examples of variables. The first is variable called studentsName and we have assigned the name John to that variable. So, every time we refer to studentsName we are referring to the name John. The same goes for numbers. The second variable is called numberOfStudents and we have stated that the number of students is 10.

Note, that text (or a “string”) is written between quote marks. Whereas, numbers don’t need quote marks, if they are being used as numbers. We use the equals sign to show that the text or the number has been assigned to that variable. In other words, that is what has been put in that particular container. The equals sign doesn’t mean equals to as in maths. For that a double (==) or triple (===) equals sign is used.


The code

Create a new spreadsheet and open the script editor from the Tools menu.

3Variables - 3

Leave the default function text in there. In between the curly brackets, I’ve created a variable called ss and I’ve assigned the class SpreadsheetApp and getActiveSpreadsheet method to it.

So, why have I done that? SpreadsheetApp.getActiveSpreadsheet() refers to the current active sheet in my spreadsheet and if I want to do anything in that spreadsheet I need to refer to this. What a variable allows me to do is instead of writing out SpreadsheetApp, etc every time, I can put it in a variable and just refer to that variable from now on. So, every time I refer to ss now, I’m actually referring to the active spreadsheet.

Next, as we’re going to refer to the sheet numerous times when reading and writing data to and from it, let’s store the active sheet in a variable called sheet. We now need to get the active sheet, so we need the getActiveSheet() method. We could write it out long hand like this:

SpreadsheetApp.getActiveSpreadsheet().getActiveSheet() but as we already have the first part stored in the variable ss, we can just use that.

3Variables - 4

Start by typing ss followed by a dot. As we can see it brings up the possible methods we can use with getActiveSpreadsheet().

3Variables - 5

We scroll down and select the getActiveSheet() method.

OK, now we want to start to create our table in a sheet.

3Variables - 18

First, type sheet follow by a dot. This refers to the current active sheet. We then need to get a range on that sheet. As we can see above, there are various options with the method getRange. For now, let’s just use the last one, which will require us to state the cell references we want to get.

3Variables - 6

Now, type the rest of the line. This will get the range A1 and set its value to the word Name. In other words, it will write the word Name in cell A1 on our sheet, like this:

3Variables - 21

3Variables - 7

Lines 6 and 7 are similar. We get cells B1 and C1 and write the words Subject and Mark. It sets up the header row like this:

3Variables - 22

Now we have our header row, let’s get the student data from columns F to H.

3Variables - 8

Here I create another variable called student1. This time I’m going to get the data in a row of 3 cells (F2 to H2). As there is more than one piece of data, we use the method called getValues(). This will store the three pieces of information: John, Maths, 65 in the variable student1. Yes, variables can store more than one bit of information!

3Variables - 9

Lines 10 to 11: We then do the same for the other two students. Now we have the 3 details of the three students in three different variables.

Now we’ve read the data in the sheet and stored it, we need to write it on the other part of the sheet.

3Variables - 10

I want to write the first student’s data in row 2, so I get the range A2 to C2 and then set the values of those cells to the contents of the variable student1.So, what will happen is that in cell A2 the John will be written, in B2 Maths and in C2 65, as we can see below:

3Variables - 23

3Variables - 11

Lines 14-15: We carry out the same process for the other two students, this time writing the data to rows 3 and 4, by using the variables student2 and student3. So, now we have our basic table:

3Variables - 24

Let’s make the table a little prettier by adding some formatting.

3Variables - 12

First, let’s get the header row, which is A1 to C1. We use the getRange() method and store that range in the variable header. Now, we have that header range we can use it to then add some formatting to it.

3Variables - 13

Line 18: Let’s change the background colour to yellow. To do this, we get the header range (header) and use the method setBackground(). Then we put the colour we want in the brackets with quote marks.

Line 19: Let’s also add bolding to the words. To do this, we again get the header and this time use the setFontWeight() method and in the brackets we add the word bold in quote marks.

As we can see, this has formatted the header.

3Variables - 25

Finally, let’s align all the cells in the table centrally.

3Variables - 14

Line 21: First, we need to get the range of cells in the table (A1 to C4). Here I’ve stored that range in the variable table.

3Variables - 15

Line 22: We then get that table range, add the setHorizontalAlignment() method to it and in the brackets state center with quote marks.

So, let’s run the program. This will read the data, write the data, and finally format it all in one go. The first time we run it, it will ask us to authorise the access we want. Just click “Review permissions” and then the blue button “Allow”.

3Variables - 1

3Variables - 2

As we can see this has now centred all our cells and the table looks much better.

3Variables - 16

By using variables, we were able to write a shorter piece of code and this code now also allows us to create a table with data for a different set of students.

Apart from the use of variables, hopefully, you can see how easy it is to get data from a sheet and to write data to it.

Here’s a link to make a copy of the spreadsheet, which also contains the code.

https://docs.google.com/spreadsheets/d/1eB0HMYsfaJbL7snqVIJ7QI79pSRi-UC2m5c-Jtl6teU/copy

If you want to learn more about variables visit the W3schools site: Variables

If you’d like to learn more about formatting your sheet, check out my post: Pimp up your sheet


Here’s the full code:

3Variables - 19


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Baz Roberts (Google+Flipboard / Twitter)


Questionnaires – Automatically send feedback to individuals

It’s common to use questionnaires at the end of a course to get feedback on the course and teacher. When you have a lot of teachers, it can be hard work analysing that feedback and splitting into the different teachers. Here we’re going to look at how we can automatically send the individual feedback to each teacher, as soon as its completed by the student. Plus, retain a master feedback sheet with all the teachers on it for the head of department’s use.

There are 4 documents we’re going to create:

  • Questionnaire form
  • Questionnaire master responses sheet
  • Individual feedback sheet per teacher
  • Questionnaire links sheet

Here’s how they link together:

Questionnaire - 42

Those who have been following my posts will see that this one combines various previous posts into one. Namely, the use of the QUERY and IMPORTRANGE functions, setting up forms, and creating short URLs.


Setting up the Form

Let’s start with the form. Create a new form and add the questions you want the student to answer. An example of one is below:

Questionnaire - 1

Questionnaire - 2

Questionnaire - 3

Then connect that form to a spreadsheet to store the responses. Click on “Responses”, then the green spreadsheet icon.

Questionnaire - 4

We want a new spreadsheet, so just click “Create”.

Questionnaire - 5

This populates the Sheet with our questions.

Questionnaire - 6


Setting up the master feedback sheet

We have a sheet called “Form Responses 1” where all the form responses will be stored, and now we need a sheet for each teacher, where we’re going to store the feedback of that particular teacher.

Add a new sheet and type the name of the first teacher in cell A1.

Questionnaire - 7

In cell A2 we’re going to add a QUERY function, which will filter the feedback on the Form Responses 1 sheet and will display only the feedback for Barrie Roberts. Here’s the formula. The QUERY function is explained in detail in my post on the QUERY function.

Questionnaire - 9

In summary, it looks at the Form Responses 1 sheet, gets all the responses, looks for ones where in column E it matches the name in cell A1 (i.e. Barrie Roberts), then sorts it by date (column A), with the most recent at the top. With no responses, you’ll get the error message “#N/A”, which will disappear as soon as you start receiving responses.

Here, I’ve named the sheet “Barrie”.

Questionnaire - 8

To just test it, I’ve filled out the form with a response for Barrie Roberts and on the sheet “Barrie” the feedback automatically appears. I normally use this as an opportunity to format the sheet a little, e.g. change the column widths, etc. You can always delete this test responses from the form responses 1 page.

Questionnaire - 37

Then duplicate this sheet for every other teacher you have. The only things you will need to change is the name in cell A1 and the sheet name.


Setting up the Individual feedback sheet

The spreadsheet above has everyone’s feedback on it and usually we won’t want to share that with all the teachers. So, let’s set up the teacher’s individual feedback sheet.

First, create a new spreadsheet. Then we’re going to copy one of the teacher’s sheets to the new file. We do this, so that the formatting is the exactly the same in the new sheet, saving us some time and ensuring they look the same. So, from the master spreadsheet, click on a teacher’s sheet and select “Copy to”.

Questionnaire - 11

Then, click on “Recent” and select the new spreadsheet you created. Here, I had already called it “Questionnaires – Barrie Roberts”.

Questionnaire - 12

The sheet will appear and by called “Copy of…”. Delete Sheet 1 as it’s not needed and rename the ‘Copy of’ sheet.

Questionnaire - 17

Now, we will need to delete the name and QUERY function from cells A1 and A2.

Questionnaire - 13

Then add an IMPORTRANGE function in cell A1. This will look at the master sheet, and import the information from the sheet and cells specified into the individual sheet. For more information on how the IMPORTRANGE function works, see my IMPORTRANGE post.

In the brackets and between quote marks, I add the URL of the master sheet, add the sheet called Barrie, and state the cell reference I want. Note, I’ve used an open-ended one, so that it will important all the data from the sheet, no matter how many rows there are.

Questionnaire - 14

We then need to authorize the connection between the 2 sheets. Just hover over cell A1 and click “Allow access”.

Questionnaire - 15

As we can see, it’s imported the same information from the master sheet and will update automatically as more form responses are received in the master sheet.

Questionnaire - 38


Setting up the Questionnaire links

The last thing to do is to set up the links to the questionnaire. To aid analysis afterwards and for the whole system to work automatically, it’s essential that the information is in a specific format.

The most important thing in this example, is that the teacher’s name is always the same, i.e. “Barrie Roberts”. As, if we left it up to the students to type in the teacher’s name, we could get all sorts of variations, like Mr Roberts, B Roberts, etc. Then the QUERY function wouldn’t work. In our academy we also want feedback on things like the rooms, so, we also make sure we control the format of those.

To do this, we just create a sheet where we add the information about each class, then use that to create a pre-filled link, which will be shared with the students. In this example, that pre-filled form will include the class name, level and the teacher’s name. The good thing is that you only need to set this sheet up once.

Going from left to right, we add columns for the class, level, and teacher. Then a column for the short link we’re going to create. A column for where the full link we be created. In column F we’ll copy and paste the pre-filled link from the form. Then in columns G to I, we will take the infomation in columns A to C and automatically replace any spaces with “%20”, which will make sure the URL we finally create doesn’t have any spaces in it, which can break the link.

Questionnaire - 19

So, in cell G2 we add this substitute function. This will replace any spaces in cell A2 with %20. See my post on creating short URLs for more information.

Questionnaire - 20

We do the same for column H and I, for the level and teacher.

Questionnaire - 21

Questionnaire - 22

So, adding the class, level, and teacher to our sheet, we can see that in columns G to I, its replacing the spaces with the %20 character.

Questionnaire - 39

Now we need our pre-filled link from the Form. Open the form and click on the 3 dot menu and then “Get pre-filled link”.

Questionnaire - 23

Then complete the fields you want to pre-fill with placeholders. Here, I’ve just used the same name as the questions, so CLASS, LEVEL, and TEACHER.

Questionnaire - 25

Click Submit and at the top of the page, a pre-filled link will appear. Click on that and press ctrl+C to copy it.

Questionnaire - 26

Then paste it in cell F2.

Questionnaire - 24

Finally, in cell E2, we need to replace the placeholders with the information on our sheet in that particular row. To do this, we use the SUBSTITUTE function. This is looking at the URL in F2 and replacing the word CLASS with the contents of cell G2 (the class name), replacing LEVEL with cell H2 (the level), and TEACHER with I2 (the teacher’s name).

Questionnaire - 27

In cell E2, we now have a link which we could share with the students, which will open the form and already have the class, level, and teacher pre-filled out.

Questionnaire - 40


URL shortener

This next part is optional, but the one problem with the link we’ve created above is that it is very long. So, I usually create a short URL from that, which is much easier to share and use. The following is explained in more detail in my post on creating short URLs.

The code here is a little different from my earlier post on creating short URLs, in that you don’t need to select the range of URLs with this code, it automatically creates short URLs for all the full URLs in column E.

Add this code in the script editor.

Questionnaire - 35 (1)

To be able to run it you’ll have to activate the UrlShortener service. Click on Resources, then Advanced Google services.

Questionnaire - 30

Scroll down and turn UrlShortener on. Then click on Google API console at the bottom in blue.

Questionnaire - 31

In the bottom right corner you’ll see URL Shortener API. Click on that.

Questionnaire - 32

Then click Enable.

Questionnaire - 33

Click the arrow back and click OK.

Questionnaire - 34

The code is now ready to be run. Click Shorten URL and Shorten.

Questionnaire - 29

The first time you run it you’ll have to accept the usual authorisation checks, just click the blue buttons.

Then your short link will appear. This is what you would share with the students.

Questionnaire - 36

Clicking on the link will open the questionnaire:

Questionnaire - 28

As you can see, it has the fields we wanted pre-filled out.

I wouldn’t suggest going through all these steps just for just one class, as the sheet is set up to create links for numerous classes. Just copy down the cells E to I to as many rows as you like and the program will create URLs automatically for all of them based on the information you enter in columns A to C. So, you’ll end up with something like this:

Questionnaire - 41

Here are the links to make copies of the files above:

End of course questionnaire spreadsheet (the form is linked so will copy automatically)

https://docs.google.com/spreadsheets/d/1F4TXCUFBz5GREd8z8ilxDaSZpU6WKyqhY9-RK1NUrmI/copy

Individual questionnaire feedback spreadsheet

https://docs.google.com/spreadsheets/d/1ZnZ8S2_9oK6NNOE3ETgp0URI5EjM2_0D8VX6K4BrtVE/copy

Questionnaire links spreadsheet (including the short URL code above)

https://docs.google.com/spreadsheets/d/14bf_xG_cRfYOPj5Ku8ymLy-m3OZeXRYIZ-oZcHxhScQ/copy


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Baz Roberts (Google+Flipboard / Twitter)


Apps Script – Creating & sharing Class folders in Drive

Teachers using Drive often need to set up folders for their class and for their individual students. Doing it in Drive is not the most exciting job to do. So, to make your life easier, here’s a script, which will set up your folders for you. I’ve made it so there is a little bit of flexibility depending on what folders you need, but of course the beauty of having the script is that you can always tinker with it to meet your exact needs.

It will create a class folder in My Drive, a folder for each student, and if you want a separate classwork folder and a separate homework folder. It will share the class, classwork, and homework folders with all the students, but only share the individual folders with the specific students.


Class Folder Creator sheet

Here’s the Sheet that we will use to fill out the information required and to run from the script from.

Class Folders - 3

Class Folders - 4

At the top, you add the class name, and then Y or N (yes or no), as to whether you want a classwork folder or homework folder or not.

Class Folders - 5

Underneath you add the students’ names and email addresses. These could easily be pasted in if you already have a list.


The code

Here are the main steps of the code:

  • The code will get the details entered on the Sheet and store them in the variables.
  • Then create the Class folder in My Drive.
  • Add the students as editors to that folder.
  • Check to see if you want a classwork folder or a homework one, and if so, create them.
  • Finally, it will create the individual student folders.

There are some similarities to the code I used in my post “Multiple folder maker“, so here, I will focus more on the new parts. Let’s look at it line by line:

Class Folders - 10

Lines 1 and 2: First we set up the function and get the Sheet.

Class Folders - 11

Line 5: We then get the sheet called “ClassFolders”.

Line 6: We get the class name we entered in cell B2. Here I’ve used the number of rows and columns, i.e. (2, 2). I could have in fact just used “B2” in the brackets.

Class Folders - 12

Line 9: Here we find out if the user wants a classwork folder or not by getting the value in cell B3, i.e. (3,2). We store it in the variable classwork.

Line 10: Similarly, we find out if the user wants a homework folder. We store this in the variable homework.

Class Folders - 13

Line 13: We get the row with the last student name and student email on it, which will the last row on the Sheet.

Line 14: Now we need to get the list of students. Here we start at row 6 and column 1 (A) and go down until the last row minus 5 (we don’t count the first 5 rows as we started on row 6), and we are just getting 1 column so we finish the range with a 1, then get the values in that range. We store these values in the variable studentsNames.

Class Folders - 14

Lines 17 and 18: We repeat the same for the students’ email addresses, which are in column 2 (B). We store these in the variable studentsEmails.

Class Folders - 15

Now we need to create the class folder.

Line 21: We’re going to create it in My Drive, so we use the DriveApp class followed by the createFolder() method.  Then we add the class name variable, className. Then we store this in the variable newFolder.

Line 22: We then get the ID of that new folder. Note, we could save a line of code here, by just adding .getId() on the end of line 21, like this:

Class Folders - 22

But, I’ll leave the steps in, so you can see what’s happening more clearly.

Line 23: Then we need to get that folder by its ID to be able to use it. We use getFolderById() to do so. Then we store it in the variable classFolder.

Class Folders - 16

Now, we add the students as editors of that folder. We do this by looping through the students’ emails and adding them one by one to the folder.

Line 26: We set up a simple for loop and continue it while its less than the number of email addresses (using the length of the array studentEmails).

Line 27: Each time we go around the loop, we want to add a student as an editor to the folder. We get the folder (classFolder) and use the addEditor() to add them. We use the variable s in square brackets to show where in the array we are. So, it will start in position 0 and continue to the end of the list.

Class Folders - 17

Now we want to see if the user wants to create a classwork folder and if so, create one in the class folder we recently created.

Line 31: We use an If statement to see if the classwork cell contains a “Y”.

Lines 32: This is similar to line 21, except here we’re going to create the folder not in My Drive but in the class folder. So, we get the classFolder variable and create the folder using that, and call it “Classwork”.

Class Folders - 18

Lines 36-38: We do exactly the same for the homework folder.

Class Folders - 19

Now we want to create the individual student folders. The folder creation is similar to before, except this time we will be looping down the list of student names previously stored in the variable studentsNames.

Line 41: We set up a for loop to go down the list of student names, using the i variable to track where we are in the list.

Line 43: We then create a folder in the class folder and name it with the current student name we are at in the loop. We do that by using studentsNames[i].

Lines 44-45: Then we get the ID of the student just created and get the folder by that ID.

Class Folders - 20

Now, we need to change the access rights for the student folders. As the student folders will be created in the class folder, they automatically assume the same access rights as the class folder. So, for each individual folder, we need to remove all the students except for the one who needs access to their personal folder. To do that we remove all the students as editors, then add the specific student.

Line 48: We set up a second loop, this time using the variable j to keep count. This will loop down the list of student emails.

Lines 49-50: Every time the loop goes around, it will remove a student email from being an editor of this folder. To do this we use the removeEditor() method and pass the studentsEmails variable to it. Then we close the j loop.

Lines 51-52: Then we need to add the student we want, who is the same as the student we created the folder for. So, we use addEditor() to add them and we use the i variable to identify which student it is. Then we close the i loop.

Class Folders - 21

Lines 55-56: Finally, I think it’s good to tell the user that the process has finished. So, here we display a toast message, using the toast method. Then in line 56 we close the function.

To run the code from a menu on the Sheet, I’ve also added this onOpen function (explained in previous posts):

Class Folders - 23


Running the code

Now let’s run an example with the information entered as below:

Class Folders - 4

Class Folders - 5

Class Folders - 24

Click on the “Folders” menu and then “Make & share class folders”.

The first time you’ll be asked to authorise the script, Just click the blue boxes.

Class Folders - 1

Class Folders - 2

This will take about 10 seconds and once you see the toast message, we will see the new folder called “Maths 101″in our My Drive.

Class Folders - 6

Double-clicking on that, we can see the sub folders we have created. There are the classwork, homework, and student ones.

Class Folders - 7

If we look at the access rights the class folder has (and also the Classwork and Homework have), we’ll see it’s been shared with everyone on my little list. Note, as I included myself in the list of “students”, I’m both a student and owner of the folder.

Class Folders - 9

Looking at the access rights of the “Baz Roberts” folder we’ll see it’s only been shared with me (Barrie Roberts-the owner) and the student “Baz Roberts”.

Class Folders - 8

The students get an automatic invite to the class folder and an invite to their individual folder.

Class Folders - 27


Hopefully, this will save you some time and effort. You can make a copy of the file and code here. In the same file, I’ve included this script, and the ones from previous posts: file maker and folder maker.

Here’s the full code:

Class Folders - 25Class Folders - 26


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Follow my Google+ Learning Google Apps Script Collection

Baz Roberts (Flipboard / Twitter / Google+)


Placement Test

Students who want to study a language at a language academy, have to do a placement test, which confirms their current level in that language and then tells the tester in which level they should be placed.

Here we’re going to see how a combination of Google Forms and Sheets, the functions VLOOKUP and ARRAYFORMULA and conditional formatting, can produce a simple but effective placement test.


SETTING UP THE FORM

First, we create the test using Forms. If you’re new to using Forms, then see my posts on how to use Google Forms.

In the test, I want the students to record some contact information and then below will be the test questions.

Here we have the fields to record their email address, name and telephone number, all of which are obligatory.

level-test-1

To set up the email address field so that it checks to see it’s a valid one, we go to the settings cog (on the right here).

level-test-2

Then tick “Collect email address”. Then click “Save”. This will add an email address field with data validation, which will check to see if an email has been entered. Note, that this always puts this field at the top of the form. Personally, I’d prefer to be able to move this, but at present that’s not possible.

level-test-3

Then we add the multiple-choice questions in our test.

level-test-4

I want the test to automatically correct this, so  click on the settings cog and this time select “Quizzes”. Then select the “Make this a quiz” option. Under “Release mark” choose “Later, after manual review”, so that the student doesn’t have the option of seeing their marks as soon as they’ve finished the test. I also untick all the options under “Respondent can see”.

level-test-5

Back in the questions part, we now need to tell the form,which questions are correct. Click on a question and then “Answer Key”.

level-test-6

Then click the correct answer. This will add a green tick next to it. Also, change the points to 1.

level-test-7

Repeat this for all the questions. Now we need to link the form to a Sheet. Under “Responses”, click the green Sheet icon.

level-test-8

We want to create a new spreadsheet, so leave the option selected and click “Create”.

level-test-9b


SETTING UP THE SHEET

This will open the Sheet and you will see the different fields from the Form on the sheet.

level-test-11

I usually format this a little and sometimes move the columns around to suit my needs, for example, like this:

level-test-12

Add a new page and let’s call it “Levels”. This is where we’re going to add the levels which correspond with the result from the test. In this example, we have 3 possible levels A1, A2, B1. In columns A and B and add the ranges for each of those levels. So, for example, if someone gets 6/10 in the test, the level they will go in is A2.

level-test-13

I’ve added a row (row 3) with just zeros, and nothing in the level column. This is important as we will see later, so that if there is nothing in the results column on the form responses sheet, nothing will be added to the levels column.

Now, let’s see if it works. The first time, I fill it out with the correct answers just to check everything is working ok. It also allows us to check future responses against this answer key, as we’ll see below.

level-test-14

As we can see the first response have been stored in the sheet fine.

level-test-15

Now we want the sheet to automatically add the appropriate level for the result. To do this we use two functions, VLOOKUP and ARRAYFORMULA. The VLOOKUP function, looks at the results in column B from B2 to the end of column B (it’s open-ended and will automatically continue as new rows are added), then looks up that result in the table on the Levels page in range A3:C6, and gets the level that’s stored in the 3rd column.

For more information on VLOOKUP see this post.

We add this to the ARRAYFORMULA function, which will automatically add this formula to all the rows below, so there’s no need to add the formulas for every row.

level-test-17

When we submit another test, as you can see the responses are added to the row below and the level is automatically added.

level-test-18

Note, that no level is added where there is no responses, this is because of the zeros line we added earlier in the levels table.


HIGHLIGHTING WHICH ANSWERS WERE CORRECT OR INCORRECT

Sometimes we also want to see which answers the students got right and wrong. We could do this back in the form, but usually we’re only working with the sheet, so it can be useful to highlight here.

Select the answers from the second response (row 3 (e.g. G3:P3).

level-test-19

Right-click and select Conditional formatting.

level-test-20

This will open the conditional format rules side bar. See my post on Conditional Formatting for more info.

level-test-21

First, let’s apply this formatting to all the columns from G to P. Currently we only have G3:P· selected.

level-test-23

Change the range to an open-ended one, so that the range will always go down to the last row on the sheet. This means even if more rows are added, all responses received will be formatted.

level-test-24

Click on the drop-down menu “Cell is not empty”. Then select “Custom formula is”.

level-test-22

Here, add the formula “=G3=G$2”. This will check if the answer in G3 is the same as G2 and if it is, it will fill the cell green. This will also check all the other cells in the range and will update the cell references accordingly. So, it will check H3 is the same as H2 and so on. The use of the dollar sign means in rows below, the answer row reference won’t change, e.g. it will check that G10 is the same as G2 and so on.

level-test-25

Now we need to add an extra rule to show the incorrect answers in red. Click on “add new rule”.

level-test-26

Here, we want to check if G3 is different from G2 and if so to fill the cell in red. So, we use the not equal symbols: <>

level-test-28

The problem with this is that as we are using an open-ended range, this will fill all the cells on the sheet in red that aren’t the same as the answer key.

level-test-29

So, we need to change the formula so that it checks to see if the answer is different AND if the row isn’t blank. We use the following AND function, which checks both conditions.

level-test-27

Now, it only highlights the rows with answers in them.

level-test-30


Here’s the link to make a copy of the Sheet and Form.


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Follow my Google+ Learning Google Apps Script Collection

Baz Roberts (Flipboard / Twitter / Google+)


Multiple FOLDER maker

Making multiple copies of folders in Google Drive is a slow, boring task. You select the folder you want, make a copy, then have to rename it, then repeat this again and again. Here you’ll learn how to create multiple folders and name them individually, really fast using Apps Script.

If you’ve already read my post “Multiple FILE maker”, then this code is essentially the same, just with two main differences, we use the methods getFolderbyId() and we use createFolder(). So, if you read it, you may want to skip through and just note the differences.

First, we have a Sheet where we will enter the details. There are three parts.

multiple-folder-maker-10

In cell A3 we type the fixed part of the folder name want, i.e. The part that is the same in all the newly-created folders.

multiple-folder-maker-11

In column B, we type the variable parts of the folder name, i.e. The parts we want to be unique in each folder.

multiple-folder-maker-12

Finally, in cell C3 we paste the full URL of the folder we want to put the new folders into.

multiple-folder-maker-13

We then choose ‘Make Multiple Folders’ from the Creator menu.

multiple-folder-maker-14

Here are the folders it made, all individually named:

multiple-folder-maker-15

Obviously, it works best when there are a lot of folders to make.


THE CODE

Let’s go through the code step-by-step:

multiple-folder-maker-2

Line 1: First, we set up our function, here I’ve called it makeFolders().

Line 4: Then, we get the current active spreadsheet, using getActiveSpreadsheet().

Line 5: Then, we get the active sheet, using getActiveSheet().

Line 6: Now, let’s get the fixed name. It’s in row 3, column 1 (I.e. A3), so in the getRange() method we add (3,1), then get its value using getValue().

Line 7: Now, we need the variable names. First, we need to get all the cells in the second column (B), from row 3 to the bottom row. We need to find out where the bottom row is, so here I’ve used getMaxRows() which gets the last row on the sheet, whether it has any values or not. I subtract 2, as we will be starting from row 3, so need to discount the first 2 rows.

Line 8: We use getRange() again, but this time we’re getting a series of values, so we need to tell it where to start and where to end. In the brackets we state: starting row, starting column, number of rows, number of columns. So, here we’re starting in row 3, column 2, down to the bottom row using the bottomRow variable, and we only want 1 column. Then we get all the values with getValues(), which will store them in the variableNames array variable.

Lines 10 to 22, are ways to check the user has entered information into the relevant cells. The program could work without them, but often it’s important to include some kind of checks to deal with users doing things that they shouldn’t, as invariably, they will always do something, that’s not expected or what in your mind wasn’t logical! Here I’m going to use some simple if statements to check that they’ve filled in the sheet correctly before running the program.

multiple-folder-maker-3

Line 11: First, I want to check that they’ve entered something into the fixed name cell. So, I’ve used an if statement to check if the variable fixedName is blank, using the double speech marks.

Line 12: If it is blank, I want it to show an alert message, telling the user it’s blank. To do so, we use getUI() and the type of message is an alert. Then in the brackets we add the text we want to display. Line 13 closes the if statement.

multiple-folder-maker-4

Line 16: Here I get the URL from cell C3 (row 3, column 3) and store it in the variable folderUrl.

Line 17: Then, I check if the variable folderUrl is empty.

Line 18: if it is empty, as before, I want to display an alert.

Now, I want to check that both fixedName AND fileUrl are NOT empty. If they aren’t empty it will run the rest of the code and make the copies, if one is empty, it won’t run the code. This means that if one of the alerts is shown above, it won’t run any code afterwards, making sure no copies are made by mistake.

multiple-folder-maker-5

Line 22: Here we first check if the fixedName isn’t equal to a blank. Then, use the double ampersands, which mean AND, to also check if the folderUrl isn’t blank. If they both aren’t empty it runs the following code.

multiple-folder-maker-6

Line 25: Now, we need the folder ID from the folder URL, in order to create folders in that folder. We use the match() method (more info here) and use some regex to extract just the ID part. Don’t worry about how this works exactly, but if you’re interested in the use of regular expressions, go to this page.

Line 27: Now, we use that ID stored in folderId, to get the folder by its ID, using the DriveApp class and the getFolderById() method, which basically, gets the folder we want to create folders in, then stores it in the variable getRootFolderId.

Now, we finally get to the point where we make the multiple folders. We’ll use a simple for loop for this. We want it to loop down column B making copies as it goes down, but then stop when it reaches an empty cell in that column, i.e.  The end of the list.

multiple-folder-maker-7

Line 31: I’ve set up a counter variable called n, and we’ll keep going down until we hit the bottom row.

Line 32: But to prevent it making files with no variable names, I’ve included an if statement to check to see if the current variable name is blank, using variableNames and the current array number in the square bracket. Remember this is looping down the array variable variableNames, so it’s going from zero to potentially the bottom row. If it does find a blank it breaks out of the loop, i.e. It doesn’t continue going through the array. Also, note the use of a double equals sign, not a triple one, as we are comparing an array value with something empty and they are not the same type, so a triple equals sign will fail.

Line 34: This is the line that creates the folders we want. We use the folder ID stored in the variable getRootFolderId we created in line 27. Then, use the createFolder() method and in the brackets state the name we want. Here, we’ll going to join the fixed name with the variable name). Line 35 closes the loop.

multiple-folder-maker-8b

Line 36: Finally, just to tell the user the process has finished, I’ve added a toast message, which will pop up in the bottom right-hand corner, when the program has finished. Here it frees to the active Spreadsheet ss, then in the brackets contains 3 parts: message, title, number of seconds it will be displayed.

Line 37 and 38: This closes the if statement from line 22, and the function.


Adding a menu

To run the program from a menu in the spreadsheet, I’ve also added a separate script file called onOpen, which will add a little menu to run the code.

Line 1: Call the function onOpen(), so that it opens automatically every time the spreadsheet is opened.

Line 2: Get the active spreadsheet, note I’ve left a comma at the end not a semi-colon, as the next 2 lines are connected to it.

Lines 3-4: Set up a variable called menuItems to add the menu details. The keyword var isn’t needed here as it using the one from line 2. Then we set up an array with the sub-menu name and the function it will run. Carefully note the syntax used here.

Line 5: Now, we add the menu to the spreadsheet using the addMenu() method to the active spreadsheet. In the brackets, we include 2 parts: menu name, and the variable menuItems.

multiple-folder-maker-9


Here’s the link to the Sheet and code.

Here’s the complete code:

multiple-folder-maker-1

The regular expression in line 25 is from here:

http://stackoverflow.com/questions/16840038/easiest-way-to-get-file-id-from-url-on-google-apps-script 


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Follow my Google+ Learning Google Apps Script Collection

Baz Roberts (Flipboard / Twitter / Google+)


Multiple FILE Maker

Making multiple copies in Google Drive is a slow, boring task. You select the file you want, make a copy, then have to rename it, then repeat this again and again. Here you’ll learn how to make multiple copies of a file and name them individually, really fast using Apps Script.

First, we have a Sheet where we will enter the details. There are three parts.

multiple-file-maker-9b

In cell A3 we type the fixed part of the file name want, i.e. The part that is the same in all the copies of the file.

multiple-file-maker-10

In column B, we type the variable parts of the file name, i.e. The parts we want to be unique in each file.

multiple-file-maker-11

Finally, in cell C3 we paste the full URL of the file we want to copy.

multiple-file-maker-12

We then choose Make Multiple Files from the Creator menu.

multiple-file-maker-13

Here are the files it made, all individually named:

multiple-file-maker-15

Obviously, it works best when there are a lot of files to make.


THE CODE

Let’s go through the code step-by-step:

multiple-file-maker-2

Line 1: First, we set up our function, here I’ve called it makeFiles().

Line 4: Then, we get the current active spreadsheet, using getActiveSpreadsheet().

Line 5: Then, we get the active sheet, using getActiveSheet().

Line 6: Now, let’s get the fixed name. It’s in row 3, column 1 (I.e. A3), so in the getRange() method we add (3,1), then get its value using getValue().

Line 7: Now, we need the variable names. First, we need to get all the cells in the second column (B), from row 3 to the bottom row. We need to find out where the bottom row is, so here I’ve used getMaxRows() which gets the last row on the sheet, whether it has any values or not. I take away 2, as we will be starting from row 3, so need to discount the first 2 rows.

Line 8: We use getRange() again, but this time we’re getting a Series of values, so we need to tell it where to start and where to end. In the brackets we state: starting row, starting column, number of rows, number of columns. So, here we’re starting in row 3, column 2, down to the bottom row using the bottomRow variable, and we only want 1 column. Then we get all the values with getValues(), which will store them in the variableNames array variable.

Lines 10 to 22, are ways to check the user has entered information into the relevant cells. The program could work without them, but often it’s important to include some kind of checks to deal with users doing things that they shouldn’t, as invariably, they will always do something, that’s not expected or what in your mind wasn’t logical! Here I’m going to use some simple if statements to check that they’ve filled in the sheet correctly before running the program.

multiple-file-maker-3

Line 11: First, I want to check that they’ve entered something into the fixed name cell. So, I’ve used an if statement to check if the variable fixedName is blank, using the double speech marks.

Line 12: If it is blank, I want it to show an alert message, telling the user it’s blank. To do so, we use getUI() and the type of message is an alert. Then in the brackets we add the text we want to display. Line 13 closes the if statement.

multiple-file-maker-4

Line 16: Here I get the URL from cell C3 (row 3, column 3) and store it in the variable fileUrl.

Line 17: Then, I check if the variable fileUrl is empty.

Line 18: if it is empty, as before, I want to display an alert.

Now, I want to check that both fixedName AND fileUrl are NOT empty. If they aren’t empty it will run the rest of the code and make the copies, if one is empty, it won’t run the code. This means that if one of the alerts is shown above, it won’t run any code afterwards,  a king sure no copies are made by mistake.

multiple-file-maker-5

Line 22: Here we first check if the fixedName isn’t equal to a blank. Then, use the double ampersands, which mean AND, to also check if the fileUrl isn’t blank. If they both aren’t empty it runs the following code.

multiple-file-maker-6

Line 25: Now, we need the file ID from the file URL, in order to make copies of the file. We use the match() method (more info here) and use some regular expressions to extract just the key part. Don’t worry about how this works exactly, but if you’re interested in the use of regular expressions, go to this page.

Line 27: Now, we use that ID stored in fileId, to get the file by its ID, using the DriveApp class and the getFileById() method, which basically, gets the file we want to copy.

Now, we finally get to the point where we make the multiple copies. We’ll use a simple for loop for this. We want it to loop down column B making copies as it goes down, but then stop when it reaches an empty cell in that column, i.e.  The end of the list.

multiple-file-maker-7

Line 31: I’ve set up a counter variable called n, and we’ll keep going down until we hit the bottom row.

Line 32: But to prevent it making files with no variable names, I’ve included an if statement to check to see if the current variable name is blank, using variableNames and the current array number in the square bracket. Remember this is looping down the array variable variableNames, so it’s going from zero to potentially the bottom row. If it does find a blank it breaks out of the loop, i.e. It doesn’t continue going through the array. Also, note the use of a double equals sign, not a triple one, as we are comparing an array value with something empty and they are not the same type, so a triple equals sign will fail.

Line 34: This is the line that makes a copy of the file we want. We use the fileId stored in the variable fileToCopy we created in line 27. Then, use the makeCopy() method and in the brackets state the name we want. Here, we’ll going to join the fixed name with the variable name). Line 35 closes the loop.

multiple-file-maker-8

Line 36: Finally, just to tell the user the process has finished, I’ve added a toast message, which will pop up in the bottom right-hand corner, when the program has finished. Here it frees to the active Spreadsheet ss, then in the brackets contains 3 parts: message, title, number of seconds it will be displayed.

Line 37 and 38: This closes the if statement from line 22, and the function.


Adding a menu

To run the program from a menu in the spreadsheet, I’ve also added a separate script file called onOpen, which will add a little menu to run the code.

Line 1: Call the function onOpen(), so that it opens automatically every time the spreadsheet is opened.

Line 2: Get the active spreadsheet, note I’ve left a comma at the end not a semi-colon, as the next 2 lines are connected to it.

Lines 3-4: Set up a variable called menuItems to add the menu details. The keyword var isn’t needed here as it using the one from line 2. Then we set up an array with the sub-menu name and the function it will run. Carefully note the syntax used here.

Line 5: Now, we add the menu to the spreadsheet using the addMenu() method to the active spreadsheet. In the brackets, we include 2 parts: menu name, and the variable menuItems.

multiple-file-maker-14


Here’s the link to the Sheet and code:

https://docs.google.com/spreadsheets/d/1kYJpljR1rkZZO0eFN8u8UupBsdps_xpL6BHhS7PpSAQ/copy

This code can of course be modified to suit your needs, as for example, you may want to let users create files without a fixed part to the file name or maybe without any variable names so all the files are named the same.

Here’s the complete code:

multiple-file-maker-1

The regular expression in line 25 is from here:

http://stackoverflow.com/questions/16840038/easiest-way-to-get-file-id-from-url-on-google-apps-script 


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Follow my Google+ Learning Google Apps Script Collection

Baz Roberts (Flipboard / Twitter / Google+)


Pimp up your Sheet – Programmatically!

When working with spreadsheets we can spend quite a long time making them look good and if we have to make the same sheets over and over again, that’s a lot of wasted time. Here we’ll look at how you can format a Google Sheet via Apps Script. The focus of this post is to show you some of the common methods available to get Apps Script to format your sheets automatically. Quite often, these would be part of larger program which get the data from somewhere and create a new sheet with it, then format the way you want. However, it could also be used where you want consistency between different sheets.

Due to their simplicity, I also think it’s a great place to start if you are new to Apps Script. If you are new, please see my earlier posts, which explain how to open the Script Editor, etc.

There are 3 scripts. In the first, we’ll cover the following:

  • adding borders
  • changing the font and size
  • setting the cell wrap status
  • setting the horizontal and vertical alignment
  • setting the number format
  • changing to bold
  • changing the cell background colour
  • deleting unwanted rows and columns
  • adjusting the column to specific widths and to adjust to the text

In the second and third scripts, we’ll quickly look at hiding rows and columns, to set up the sheet with the information you want in it, then be able to reset it by showing all the rows and columns again.


SCRIPT 1

As an example, we going to format a list of classes. So we are going to go from this:

pimp-up-your-sheet-36pimp-up-your-sheet-37

To this (which will take the program about 3 seconds):

pimp-up-your-sheet-41

Here are all the steps one-by-one:

pimp-up-your-sheet2

Let’s go through it step-by-step:

First we set up the function, here I’ve called it “formatSheet1” and we get the active Spreadsheet and the sheet we want, in this case, the sheet is “Sheet1”.

pimp-up-your-sheet-9

Then I want to set up some variables to store the rows and columns in the sheet.

Line 8: Here I’ve used getDataRange() to get the range that contains all the data in the sheet and stored it in the variable allCells, which we’ll be using throughout the script.

Lines 9 and 10: Here I also want to store the number of rows and columns for later use. To do so, I use the getLastColumn() and getLastRow() methods.

pimp-up-your-sheet-10

I also want to get a couple of specific ranges that we’ll use later.

Line 13: I get the header row by using getRange() and in the brackets I put (starting row, starting column, number of rows, number of columns). I’ve used the variable numColumns for the number of columns.

Line 14: Plus, I get the comments column, using getRange().

pimp-up-your-sheet-11

Now let’s start formatting the sheet.

Line 17: First, let’s add borders to all the cells which contain data. We use the setBorder() method and use it with the range stored in allCells, i.e. all the data. In the brackets, there are 6 parts: top, left, bottom, right, vertical, horizontal, which refer to the sides of the cells. We state “true” for each of these parts to add complete borders.

pimp-up-your-sheet-12

Now let’s change the font to Calibri and set its size to 11 to all the data.

Line 20: To set the font we use the setFontFamily method and add the name of the font in the brackets. Make sure you use a capital letter, otherwise it won’t work properly. To set this to all the data, we add it to the range stored in allCells.

Line 21: To set the font size, we use the setFontSize() and in the brackets state the size.

pimp-up-your-sheet-13

Line 24: In the comments column I want to set a smaller font size, so, I use setFontSize() with the commentsColumn variable, which contains the comments column range.

pimp-up-your-sheet-14

Line 27: Now let’s set the text so it wraps if it is long. We use the setWrap() method with the header row. To activate it, we put ‘true’ in the brackets.

Line 30: I also want to do the same, for all the comments column, so I use the same method with the commentsColumn variable.

pimp-up-your-sheet-15

pimp-up-your-sheet-16

Now, let’s set the alignment of the text, both horizontally and vertically.

Line 33: First, I want to set the horizontal alignment for all the data to “center”. We use the setHorizontalAlignment() method and state the type in the brackets.

Line 34: Then, I want to set just the horizontal alignment of the comments column to “left”.

pimp-up-your-sheet-17

Lines 39: We do the same for the vertical alignment, using the setVerticalAlignment() method. First, we set the vertical alignment of all the data to ‘middle’.

Line 41: Then we set the vertical alignment of the header row to ‘bottom’.

pimp-up-your-sheet-18

Now let’s set the number format for the class number and dates.

Line 45: First we get the class column via getRange().

Line 46: Then we use the setNumberFormat() methods to that range and state the format in the brackets. In this case, I want the class number to be two digits, so that ‘1’ will be ’01’. To do this, we use the format “00”.

Line 49: Then we get the columns which contain dates, which are from column 8 (column H) to column 18 (column R). Note, we start from column 8 and then including this one, include 11 columns to the right.

Line 50: This time I want the date format to be “dd/mm”, for example, “20/03”.

pimp-up-your-sheet-19

Line 53: Now let’s add bolding to the header row. We use the setFontWeight() method to do so, and in the brackets, add “bold”.

pimp-up-your-sheet-20

Now I want to colour header cells in different colours. First, I generally find it easier to state the hexidecimal codes beforehand and assign them to easy to recognise names. Plus, if different cells are using the same colour, it’s easy to change the colour by just updating the hexidecimal code for that colour.

In the file, I’ve added a sheet which contains all the hexidecimal codes for all the default colours in the colour palette.

Lines 56 to 63: I set up the variables as below. Remember to include the hash.

pimp-up-your-sheet-21

Then we are ready to set the different colours to the backgrounds of the cells.

Line 65: I want to set the first 6 cells in the header to be blue, so first I get the range.

Line 66: Then I set the background colour, by using setBackground() with that range and in the brackets refer to the colour in the variables we set up beforehand, here’s it the variable blue.

Lines 68 to 96: I continue in a similar way across the sheet.

pimp-up-your-sheet-40

One thing I like with Sheets, is the ability to delete unwanted rows and columns. We could hard code this, in other words, state the rows or columns we want to delete, but let’s get the code to work out which rows and columns don’t have data in them, and thus, delete those.

Line 99: First, we need to find out how many columns there are in the sheet. We use the getMaxColumns() to find this out. I’ve stored this in totalColumns.

Line 100: Then we do a bit of maths, we take the number of columns that contain data (stored in the variable numColumns) away from the total number of columns (totalColumns). I’ve stored this in numOfColumnsToDelete.

Line 101: Now we delete the empty columns. We use deleteColumns() and in the brackets there are two parts: the column position we’re going to start at, and how many columns we’re going to delete. The first is the variable numColumns + 1, as we don’t want to delete the last column with data but the one to the right of it. the second, we use the variable numOfColumnsToDelete which we worked out before.

pimp-up-your-sheet-23

Lines 104 to 106: We follow the same process for the rows, just we use the equivalent row methods and variables.

pimp-up-your-sheet-24

The final thing I want to do is change the column widths. The first 7 columns I want to adjust automatically according to the data in them.

Line 109-111: Here we can use a simple for loop to resize each of the columns in turn. I start the variable i at 1 (column 1) and it goes up by 1 each round of the loop until it reaches 7 (column 7). More info on loops here.

Line 110: Each round of the loop, it will automatically resize the column based on the widest piece of data in that column. The first time in the loop the i in brackets will be 1, which refers to column 1.

pimp-up-your-sheet-25

Line 114-116: I use a similar loop to adjust the widths of columns 8 to 18 to ’60’.

Line 115: This time I’m stating the exact width, so we use the setColumnWidth() method. In the brackets we need to state the column number and the width.

Line 119: Here I want to make the width of the comments column ‘120’, so I’ve used the variable numColumns to identify the comments column, as I know it’s the last column, then add the width ‘120’.

pimp-up-your-sheet-26


SCRIPT 2 – Hiding rows and columns

Sometimes we have a sheet which is full of data but we’re just interested in a part of it and only want the relevant parts showing. In this little script, I just want to show how easy it is, to set up a sheet showing just the rows and columns you want, by hiding the unwanted ones.

As in the first script we get the active spreadsheet and Sheet 1.

pimp-up-your-sheet-28

I want to only show term 1 information, so I hide columns L to R as they refer to term 2 and 3.

Line 6: First, I need to get the range of columns use getRange(). Note, the range refers to the first row, which is sufficient as it by default means it includes the columns we want.

Line 7: Then we use hideColumn() to hide those columns.

pimp-up-your-sheet-29

This time I want to hide the rows with the French classes, so I need to hide rows 4 and 10.

Lines 10 and 11: Similar to above, I get the ranges, and here including just column A is fine, it just needs to include the row number.

Lines 12 and 13: Then we use hideRow() to hide the respective rows.

pimp-up-your-sheet-30

So, potentially at a click of a button, we can hide the rows and columns we want, leaving just the relevant information.


SCRIPT 3 – Unhiding all rows and columns

This is great, but we will want to reset the sheet to display all the rows and columns.

As before we get the active spreadsheet and Sheet 1.

pimp-up-your-sheet-31

Line 6: First, I want to unhide all the columns, so I will need to know how many columns there are. So, here I’ve used getLastColumn().

Line 7: Then, we need to get the full range of columns, using getRange() and getting from column 1 to the number of columns in numColumns.

Line 8: Then, we use unhideColumn() to unhide all the columns in the range.

pimp-up-your-sheet-33

Then we do the same for the rows. The only difference is we’re referring to rows not columns, so we use the respective methods.

pimp-up-your-sheet-34


Here’s the link to make a copy of the sheet and code:

https://docs.google.com/spreadsheets/d/1S21Km_0R1-K9An8zX9NntXBHdQWGrm9VY1ryIHs1Gj8/copy

In the spreadsheet, you will find 4 sheets.

‘Sheet 1’: the sheet that the scripts work on

‘unformatted’: a copy of the unformatted data

‘formatted’: what the data looks like once formatted

‘ColourRefs’: As a bonus, I’ve added the hexadecimal references for all the default colours in the colour palette in sheets. In the script edittor, you’ll find a fourth script, which is what I used to get those reference numbers.


Here are the full scripts:

pimp-up-your-sheet-42pimp-up-your-sheet-38pimp-up-your-sheet-39

SCRIPT 2 – HIDING ROWS AND COLUMNS

pimp-up-your-sheet-27

SCRIPT 3 – UNHIDING ALL ROWS AND COLUMNS

pimp-up-your-sheet-32


eBooks available on Drive, Forms, Sheets, Docs, Slides, and Sheet Functions:

Follow my Google+ Learning Google Apps Script Collection

Baz Roberts (Flipboard / Twitter / Google+)