Clock In & Out System on Google Form - javascript

I created a Google App Script that creates a form to allow users to choose their name from the dropdown list and it will direct them to the next page. On the next page, with their name and a little hint saying they need to clock out (see the image), as well as their options to choose whether they want to clock in or out.
After I run this script, I created a form with all those options on it. But the question is how to set a description that has the says last time they clock in is "mm/DD/yyyy" + you need to clock OUT" and vise versa.
How would I do this through Google script?
Here is the script:
function setUpForm() {
//Set up form
var form = FormApp.create('Clock In & Out System');
form.setTitle('Clock In Form');
//Set up first page
var item1 = form.addListItem()
.setTitle('Employee Name')
.setRequired(true);
var page2 = form.addPageBreakItem()
.setTitle('Fred');
//Set up second page (Fred)
var item2 = form.setTitle('Fred');
var item2a = form.addMultipleChoiceItem()
.setTitle('Clocking in or out?')
.setChoiceValues(["Clock in", "Clock out"])
.setRequired(true);
//Change last time clock in/out message and update reminder on what to do next
var page3 = form.addPageBreakItem()
.setTitle('Wilma')
.setGoToPage(FormApp.PageNavigationType.SUBMIT);
//Set up third page (Wilma)
var item3 = form.setTitle('Wilma');
var item3a = form.addMultipleChoiceItem()
.setTitle('Clocking in or out?')
.setChoiceValues(["Clock In", "Clock Out"])
.setRequired(true);
var page4 = form.addPageBreakItem()
.setTitle('Betty')
.setGoToPage(FormApp.PageNavigationType.SUBMIT);
//Setup forth page (Betty)
var item4 = form.setTitle('Betty')
var item4a = form.addMultipleChoiceItem()
.setTitle('Clocking in or out?')
.setChoiceValues(["Clock In", "Clock Out"])
.setRequired(true);
//Set up name choices on first page
item1.setChoices([
item1.createChoice("Fred", page2),
item1.createChoice("Wilma", page3),
item1.createChoice("Betty", page4)
]);
}
Here is the image:

The first thing that we should have in mind is that all the changes to the form should be made before respondent open the form.
One way to add the last time that the responded checked in message is to run a function that adds that message to the form. Maybe the most convenient way for most cases is to use an onFormSubmit trigger that updates the form item that should hold the "last clocked in" message.
NOTE: Script adopters should adapt it to their specific needs, but first they should understand what it does and how it does that.
For simplicity let say that we use a Title (Apps Script calls it Section Header) to hold the related message and that we have a form just with on Title and one Multiple Choice question and that the message should use a variables the clock in/out and the response timestamp. The following script will update the Title description on form submit, so the next time the user open the form to submit the clock in/out they will see the timestamp of the last time that they submitted a response. It should be added to a Script project bounded to the form.
/**
* Update help text of first section header on form submit
*
* #param Object e Form submit event object
* #param Object e.authMode A value from the ScriptApp.AuthMode enum.
* #param Object e.response A FormResponse object, representing the user's response to the form as a whole.
* #param Object e.source A Form object, representing the Google Forms file to which the script is bound.
* #param Object e.triggerUid ID of trigger that produced this event.
*/
function updateHelpText(e) {
// Get section headers.
var formItems = e.source.getItems(FormApp.ItemType.SECTION_HEADER);
// Get response timestamp
var timestamp = e.response.getTimestamp();
// Get script timezone
var timeZone = Session.getScriptTimeZone();
// Set timestamp format
var format = 'mm/dd/yyyy hh:mm';
// Get the first section header. This form item will hold the message.
var item = formItems[0];
// Get clocked type
var type = e.response.getItemResponses()[0].getResponse();
// Set message
var message = Utilities.formatString('The last time you %s was %s',
type,
Utilities.formatDate(timestamp, timeZone, format));
// Add message to section header
item.setHelpText(message);
}
/**
* Auxiliary function to validate on form submit function
*
*/
function test(){
// Create a form response
var form = FormApp.getActiveForm();
var items = form.getItems();
var x = items[1].getTitle();
var formResponse = form.createResponse();
formResponse
.withItemResponse(
items[1].asMultipleChoiceItem().createResponse('Clock in')
);
// Create on form submit event object
var e = {}; // Form submit event object
e.authMode = ScriptApp.AuthMode.FULL; // A value from the ScriptApp.AuthMode enum.
e.source = form; // A Form object, representing the Google Forms file to which the script is bound.
e.response = formResponse.submit(); // A FormResponse object, representing the user's response to the form as a whole.
e.triggerUid = '00000000'; // ID of trigger that produced this event.
// Call the on form submit function
updateHelpText(e);
}
Once the above code is added to the form script project, add a trigger following the instructions on Installable triggers.
To adapt the above for the OP case, the adopters could add some logic to replace the 0 indexes by the corresponding indexes for the Title and the Clock in or Clock out? form items and item responses accordingly. Let say that i and j are used to hold the corresponding indexes, then beside adding the code to set that indexes on the following lines should be replaced the 0 indexes by i and j accordingly:
// Get the first section header. This form item will hold the message.
var item = formItems[i];
// Get clocked type
var type = e.response.getItemResponses()[j].getResponse();

Related

How to run Google sheet script from the Google form submit button?

I have a form in the google form that is about getting data from users, I need to get the user's phone number from the google sheet after the user submits his/her information in google form and send the verification SMS to them.
I want to get an Event form to submit button, google has a submit function in its documentary but I don't know how its works or how can I use it because there is no example there!
I wrote all code for this, I just need to talk with the submit button.
You can use the following function -
function onFormSubmit(e) {
var formItems = FormApp.getActiveForm().getItems();
var userPhoneNumber = e.response.getResponseForItem(formItems[x]).getResponse();
// replace 'x' with 0 if phone number is the first item or
// with the actual number depending on the nth item of your form
// (i.e. if userPhoneNumber is the 2nd item, replace x with 1,
// if its the 3rd item, replace x with 2 etc.)
// now you write your function to trigger SMS to the 'userPhoneNumber' value
}
Hope this helps.

Programatically made form doesn't return response on trigger

I made a new form programmatically for verification purpose after another form (pre-made) is submitted. Here is the code (This script is bound to the pre-made form. And this createVerificationForm() is called inside a function triggered by the form onSubmit):
/**
* Create a Google Form to verify the submitted request
* #param {Array<String>} The submitted data
* #return {Form} The created verification form
*/
function createVerificationForm(submissionData) {
// Create Form
var form = FormApp.create("Request Verification");
// Add Data to be verified as description
form.setDescription("Show the details here");
// Add options to verify or deny
form.addMultipleChoiceItem()
.setTitle("Verify the request")
.setChoiceValues([REQUEST_VERIFIED, REQUEST_DENIED])
.setRequired(true);
// Set the form trigger
ScriptApp.newTrigger('setRequestStatus')
.forForm(form)
.onFormSubmit()
.create();
return form;
}
I also create a trigger for the verification form. The setRequestStatus will get the response and use it accordingly. Because I'm unable to get the response, I tried to stringify() the event object to check if the response is actually received by setRequestStatus(). Here is what it looks like
/**
* Set Request Status.
* Triggered by Verification Form
* #param e {Object} Event Object sent by the trigger
*/
function setRequestStatus(e) {
var response = e.response;
var itemResponses = response.getItemResponses();
// I use email because somehow Logger doesn't work
MailApp.sendEmail(REVIEWER_EMAIL, "Verification Result", JSON.stringify(e));
}
And here is the result:
{"response":{},"source":{},"authMode":{},"triggerUid":"xxxxxxxxxxxx"}
Everything but triggerUid is absent. Does it have anything to do with creating form and setting the trigger programmatically?
FYI, looking at the response tab (when you open the form through GDrive), the response is actually recorded.
EDIT:
Per tehhowch suggestion, I tweaked setRequestStatus() into this:
function setRequestStatus(e) {
var response = e.response;
var itemResponses = response.getItemResponses();
console.log("FormResponse: " + itemResponses[0].getResponse());
}
The result of the log is this:
TypeError: Cannot call method "getResponse" of undefined. at setRequestStatus(FormResponder:139)
It seems that there is no ItemResponse returned by getItemResponses(). It's weird considering that checking the response gathered by opening the form directly on Google Drive does show that the response is indeed there.
EDIT2:
I tried to log the event object content in case it helps. Here are the code and the log screencap.
/**
* Set Request Status.
* Triggered by Verification Form
* #param e {Object} Event Object sent by the trigger
*/
function setRequestStatus(e) {
var response = e.response;
var itemResponses = response.getItemResponses();
console.log(e);
console.log(e.response);
console.log(e.response.getItemResponses());
console.log(e.response.getItemResponses()[0]);
}
Notice that the FormResponse object is there but when I tried to access it, it comes out empty. I'm starting to think that this can be an issue Google should look into.
EDIT3
After looking at stepvda's similar code, I assumed that this problem maybe has something to do with my script is bound to a form (and that it already has an onSubmit trigger) or with another functionality (such as sending email and editing spreadsheet). So I tested my script on a new unbound project by simplifying it. Here is the code (I made a couple variation on how the trigger is attached):
function testTrigger() {
// Attach to a premade form (WORKED!)
// var form = FormApp.openById('1lRKrqxuQm50EY4zMsf8b_myrN9KIaooFS7Elv48XD3U');
//
// ScriptApp.newTrigger('logDataFromForm')
// .forForm(form)
// .onFormSubmit()
// .create();
// Attach to a programmatically made form (WORKED!)
// var form = FormApp.create("Auto Trigger Test");
// form.addTextItem()
// .setTitle('How are you?');
//
// ScriptApp.newTrigger('logDataFromForm')
// .forForm(form)
// .onFormSubmit()
// .create();
// Attach to a programmatically made form from a function (WORKED!)
var submissionData = ['Blacky', 'blacky#dog.com'];
createVerificationForm(submissionData);
}
function logDataFromForm(e) {
console.log("I'm auto triggered");
console.log(e);
console.log(e.response);
console.log(e.response.getItemResponses());
console.log(e.response.getItemResponses()[0].getResponse());
}
function createVerificationForm(submissionData) {
// Create Form
var form = FormApp.create("Are you dumb? (test)");
// Add Data to be verified as description
form.setDescription(
"Some random description" +
"\nName\t\t\t\t: " + submissionData[0] +
"\nEmail\t\t\t\t: " + submissionData[1] +
"\n"
);
// Add options to verify or deny
var choices = ['Yes', 'No'];
form.addMultipleChoiceItem()
.setTitle("Are you dumb?")
.setChoiceValues(choices)
.setRequired(true);
// Set the form trigger
ScriptApp.newTrigger('logDataFromForm')
.forForm(form)
.onFormSubmit()
.create();
}
The result is:
It works!!
Then I tried to test this on another project and just like the first time, it is bound to a pre-made form. testTrigger() is now triggered by the bound form's onSubmit trigger.
Here is how it looks like (other functions are the same):
function testTrigger(e) {
// Attach to a programmatically made form from a function
var itemResponses = e.response.getItemResponses();
var submissionData = [itemResponses[0].getResponse(), itemResponses[1].getResponse()];
createVerificationForm(submissionData);
}
And this is the result:
It doesn't work! I can't really confirm this but based on this test, MAYBE the response is not returned with the event object because it has something to do with the script bound to a form with already created trigger (in this case, onSubmit).
I recently developed similar functionality which is working fine. You may want to compare with the code I wrote. Have a look at the createNewForm() and submitForm(e) function here.
You may also want to just look at the contents of the responses array. I noticed that JSON.stringify doesn't reveal everything here. In this scenario you can't use the debugger so you'll have to use the Logger.log() function to see what is really in the responses.

when attempting to use e.value in google sheet script I get TypeError: Cannot read property "Values" from undefined. (line 7, file "Code")

This is a two part question:
I have google sheet with a linked Form. When the form is submitted I want the responses from the form to be copied to another google sheet where I intend to change and reformat and then send via email. The script below is what i have currently written and it has a trigger set up onFormSubmit. However, I keep getting the follow error:
TypeError: Cannot read property "Values" from undefined. (line 7, file "Code")
Code below:
function formSubmitReply(e)
{
var t = "1g-wIs6nGxu3mJYA1vKtPCxBLCsvh1upeVGbCokOOTIw";
var tName = "AggregationOutput";
//Get information from form and set as variables
var email = e.Values[2];
var name = e.Values[3];
// Get template, copy it as a new temp, and save the Doc’s id
var tcopyId = SpreadsheetApp.openById(t).copy(tName+' for '+name).getId();
// Open the temporary document & copy form responses into template copy response sheet
var copyt = SpreadsheetApp.openById (tcopyId);
var copyts = copyt.getSheetByName('Resp');
// Transfers Data from Form Responses to Temporary file
copyts.getRange('A3').setValue(name);
//Sends copy of template in an email as an excel file
var url = "https://docs.google.com/feeds/download/spreadsheets/Export?key=" + copyt.getId();
var subject = 'Aggregaton Output for' + name;
var body = url
MailApp.sendEmail(email, subject, body);
// Deletes temp file
DriveApp.getFileById(tcopyId).setTrashed(true);
}
Part two of my question, even if I can get the code to work what would you recommend when a question is skipped in the form - won't this change the array from e.values. The issue with using the last row as a problem is that I want people to go back and edit responses on the form and then resubmit which means the last row isn't always the row used.
Any and all help is appreciated.
For Part 1, try this:
function formSubmitReply(e)
{
var t = "1g-wIs6nGxu3mJYA1vKtPCxBLCsvh1upeVGbCokOOTIw";
var tName = "AggregationOutput";
//Get information from form and set as variables
var itemResponses = e.response.getItemResponses();
var email = itemResponses[2].getResponse();
var name = itemResponses[3].getResponse();
// Get template, copy it as a new temp, and save the Doc’s id
var tcopyId = SpreadsheetApp.openById(t).copy(tName+' for '+name).getId();
// Open the temporary document & copy form responses into template copy response sheet
var copyt = SpreadsheetApp.openById (tcopyId);
var copyts = copyt.getSheetByName('Resp');
// Transfers Data from Form Responses to Temporary file
copyts.getRange('A3').setValue(name);
//Sends copy of template in an email as an excel file
var url = "https://docs.google.com/feeds/download/spreadsheets/Export?key=" + copyt.getId();
var subject = 'Aggregaton Output for' + name;
var body = url
MailApp.sendEmail(email, subject, body);
// Deletes temp file
DriveApp.getFileById(tcopyId).setTrashed(true);
}
Question 1:
The error you get is due to a wrong syntax, values (All small, not Values)
var email = e.values[2];
var name = e.values[3];
Question 2:
When the question is skipped the value of the response is blank. So if an email is left blank e.values[2] would still refer to the email field in your form, but will have no value in it.
If you have edit later option activated on the form, the edited responses will only be present in the e.values array. So if they update their email ID only, e.values[2] = "updated Email ID" and e.value[0-1,3-end] = Empty/blank.
To figure out if the submission is new entry or edited entry you can use e.range to figure out where the responses are going to be added in the "form Response" sheet. And you can mirror that range in your "resp" sheet to keep it updated the same way as form response sheet.

Javascript store session value even after click back button

I need to know how I can show the input data of a form after a user presses the back button.
Here is the working jsfiddle
I created a simple asp page.
If I click next after entering some input value, it will go to next page, and again I click back, it doesn't show entered input values.
$("#form").submit(function() {
var inputs=$('.input');
for(i=0;i<inputs.length;i++)validateEmail(inputs[i]);
if($('.invalid').length!==0)return false;
});
I need to show those values. How can I achieve this? Thanks in advance!
Note: In my jsfiddle didn't include next page action.
You have several methods, althought I'm not a big fan of cookies because they are not so smart to manage.
You can either use:
The localStorage API, if you want your values being saved until the user clears the cache, like this:
var myValues = [];
for(i=0;i<inputs.length;i++) {
validateEmail(inputs[i]);
myValues.push(inputs[i].value);
}
// Save them fot later use
localStorage.myValues = JSON.stringify(myValues);
// After clicking the button you can retrieve them back
var oldValues = JSON.parse(localStorage.myValues);
The sessionStorage API, which lives for the current session of the browser (i.e. when the user closes the browser it gets deleted):
var myValues = [];
for(i=0;i<inputs.length;i++) {
validateEmail(inputs[i]);
myValues.push(inputs[i].value);
}
// Save them fot later use
sessionStorage.myValues = JSON.stringify(myValues);
// After clicking the button you can retrieve them back
var oldValues = JSON.parse(sessionStorage.myValues);
NOTE: I'm using JSON.parse and JSON.stringify because both storage objects (local and session) can only store data in string format.

How to make a web app retain information?

Ok... so a user enters info into the journal field and hits submit, and a function gets called on the information they have submitted, which I call changeTextComment(). That function calls another function and so on as the info is formatted and placed in the cue, as in a Facebook commentary.
I need this information saved so it can be recalled later, in local storage, making the app not refresh every time I restart it. So...
<script>
function appCache() {
// Check browser support
// this is experimental for local storage...
more here: http://www.w3schools.com/html/html5_webstorage.asp
if (typeof(Storage) != "undefined") {
localStorage.setItem(para);
// Retrieve
document.getElementById("journal").innerHTML = localStorage.getItem(para);
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support Web Storage...";
};
</script>
So looking at the appCache function it seems like it might work except I need to identify what the key variable is that will be stored and then retrieved.
I think this is the variable userInput, as any time the user hits the 'add to journal' button this variable is used to store the user input and then put into the changeTextComment() function.
I am wondering if this is the simplest way to deal with this whole thing... I do not yet understand databases, but wondering if that would be easier to learn.
In this case, I would add the function Appcache() to the function changeText() such that it caches the variable and then how would I set it up to then feed the value of the variable's cached info into changeText() upon launch of the app?
Every submission to the journal will have a unique value.
Heres the ChangeTextComment() Function... still sorting out how to use classes in css to simplify these functions:
function changeTextComment(){
// to be modified from the above to change the location of the dump
var userInput = document.getElementById('userInput').value;
// get the input from the user
var panel = document.createElement("div"); // create a parent divider for everything
// assignment of attributes
panel.setAttribute("class","panel panel-default panel-body");
panel.setAttribute("id","panelBody");
var para = document.createElement("P");
var t = document.createTextNode(userInput);
para.appendChild(t);
// add comment area
var c = document.createElement("INPUT");
c.setAttribute("type", "text");
c.setAttribute("id", "comment");
c.setAttribute("placeholder", "comment here");
c.setAttribute("class", "form-control input-lg");
// add comment button attempt -- success <> now try to put it in the textarea
var d = document.createElement("INPUT");
d.setAttribute("type","button");
d.setAttribute("class","btn btn-info active pull-right");
d.setAttribute("onclick","commentThis()");
d.setAttribute("value","Add Comment");
panel.appendChild(para); // this is where a comments piece would go
// place the item
var destination = document.getElementById("journal")
//destination.insertBefore(Commentaryarea, destination.firstChild);
//destination.insertBefore(panel, destination.firstChild);
destination.insertBefore(panel, destination.firstChild);
panel.appendChild(c);
panel.appendChild(d);
document.getElementById("userInput").value = "";
document.getElementById("userInput").focus();}
</script>
<script>
function setText(a){
document.getElementById("userInput").value = a
document.getElementById("userInput").focus();
}
</script>
When you say "stored locally", do you mean stored in the visitors browser, i.e. with only themselves being able to see it and retrieve it? Or do you want other users to be able to see the data, and/or the commenter to be able to see it if they log in later from another location?
If it's the latter then you need to store it on the server side, and you're going to need a database for that. If all you need is to store journal entries then mongoDB is easy to set up with javascript, but if you have a lot of relations (journal entries being associated with users, comments on entries being associated with users and entries, ect) then perhaps an SQL database would suit you better.

Categories