I am currently trying to automatically archive responses from a Google form. I have a script which runs every time the form is submitted and does some processing of the last submit.
What I want to do is on the first submit of the month, create a separate spreadsheet with just last months entries.
I am doing this by getting the date from last month, creating a new spreadsheet with last months name and year as the name of the file.
What I now need to do is select a range based on dates. So in column A is a timestamp (e.g. 31/12/2014 22:21:31) - I would want to select all rows between for example 1/12/2014 00:00:00 and 31/12/2014 23:59:59.
Using this example I know its possible to copy a range but it's finding the correct range I need help with:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var target = SpreadsheetApp.openById("0Aqv8.....");
var source_sheet = ss.getSheetByName("Form Responses");
var target_sheet = target.getSheetByName("Sheet1");
var source_range = source_sheet.getRange("A2:A");
var target_range = target_sheet.getRange("A2:A");
var values = source_range.getValues();
target_range.setValues(values);
I am using the following to get the start time, and end time of the month:
var x = new Date();
var d = new Date();
// Start Time:
x.setDate(1);
x.setHours(0);
x.setMinutes(0);
x.setSeconds(0);
x.setMonth(x.getMonth()-1);
// Finish Time:
d.setDate(0);
d.setHours(23);
d.setMinutes(59);
d.setSeconds(59);
Ok so this probably isn't the best answer, but hey it seems to work for me:
var x = new Date();
var d = new Date();
// Start Time:
x.setDate(1);
x.setHours(0);
x.setMinutes(0);
x.setSeconds(0);
x.setMonth(x.getMonth()-1);
// Finish Time:
d.setDate(0);
d.setHours(23);
d.setMinutes(59);
d.setSeconds(59);
// Create a blank spreadsheet:
var root = DocsList.getRootFolder()
var newFileId = SpreadsheetApp.create(archiveName).getId();
var newFile = DocsList.getFileById(newFileId);
newFile.addToFolder(destFolder);
newFile.removeFromFolder(root);
// Get the spreadsheets:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var target = SpreadsheetApp.openById(newFileId);
var sheet = ss.getSheetByName("Form Responses");
var target_sheet = target.getSheetByName("Sheet1");
// Record the range:
var firstRow = 1;
var lastRow = 1;
var width = sheet.getDataRange().getWidth();
// Get all the current data:
var numRows = sheet.getDataRange().getNumRows();
var data = sheet.getDataRange().getValues();
// Get the first entry:
for(var i=0; i<numRows; i++){
var tmp = new Date(data[i][0]);
if (tmp > x) {
firstRow = i+1;
Logger.log(i + " - is the first row");
i = numRows;
}
}
// Get the last entry:
for(var i=0; i<numRows; i++){
var tmp = new Date(data[i][0]);
if (tmp > d) {
lastRow = i;
Logger.log(i + " - is the last row");
i = numRows;
}
}
// Copy the title:
var source_range = sheet.getRange(1,1,1,width);
var target_range = target_sheet.getRange(1,1,1,width);
var values = source_range.getValues();
var formats = source_range.getNumberFormats();
target_range.setValues(values);
target_range.setNumberFormats(formats);
target_sheet.setFrozenRows(1);
// Copy the last months values:
var source_range = sheet.getRange(firstRow,1,lastRow-firstRow+1,width);
var target_range = target_sheet.getRange(2,1,lastRow-firstRow+1,width);
var values = source_range.getValues();
target_range.setValues(values);
Related
I have Google form which is linked to Form Response Sheet. I want to Move data from "Discrepancy Report - Outlet" to "Vaishali Nagar - DTR" Sheet on each day. The date is to be used from "Discrepancy Report - Outlet" sheet from Colum J.
The condition is that if I run the script today it should move data of Previous Day. For Ex. If I run script Today i.e. 31/10/2020 then It should only pick data of 30/10/2020. This process goes on each day. For running this script I will use Time Trigger.
I am using script which is mentioned below the Question. The problem I am facing it runs on the basis of today's date.
Link of Spreadsheet is:
https://docs.google.com/spreadsheets/d/1MhCdwFscPqskeeM2Hza-t2VXoVkI6sq3XRCNybMm4NM/edit?usp=sharing
function copyrange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Discrepancy Report - Outlet'); //source sheet
var testrange = sheet.getRange('J:J');
var testvalue = (testrange.setNumberFormat("#").getValues());
Logger.log(testvalue);
var ds = ss.getSheetByName('Vaishali Nagar - DTR'); //destination sheet
var data = [];
var j =[];
var today = new Date();
var yesterday = new Date(today); yesterday.setDate(yesterday.getDate()-1);
var today = Utilities.formatDate(new Date(), 'GMT-0', 'dd/MM/yyyy')
//Condition to check in J:J, if true, copy the same row to data array
for (i=0;i<testvalue.length;i++) {
if (testvalue[i] == today) {
data.push.apply(data,sheet.getRange(i+1,1,1,9).getValues());
//Copy matched ROW numbers to B
j.push(i);
}
}
//Copy data array to destination sheet
var start_row=ds.getRange('B7:B').getValues().filter(String).length +6; //calculate max row
ds.getRange(start_row+1,1,data.length,data[0].length).setValues(data);
}
Explanation:
The main issue is that your current code compares the dates with the date of today instead of yesterday.
However, your code can be further optimized. Instead of using a for loop to check if an element matches the date of today, you can use filter to get the data only for yesterday and then remove the lst column because you don't need it to be in the destination sheet:
yest_data = data.filter(r=>r[9]==yesterday).map(v=>v.slice(0,-1));
It is a good practice to check if the data has a length higher than 0, otherwise the script will drop an error when setting the values.
Solution:
function copyrange() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Discrepancy Report - Outlet'); //source sheet
const ds = ss.getSheetByName('Vaishali Nagar - DTR'); //destination sheet
const data = sheet.getRange('A2:J'+ sheet.getLastRow()).getDisplayValues();
var yesterday = new Date();
yesterday.setDate(yesterday.getDate()-1);
var yesterday = Utilities.formatDate(yesterday, ss.getSpreadsheetTimeZone(), 'dd/MM/yyyy');
yest_data = data.filter(r=>r[9]==yesterday).map(v=>v.slice(0,-1));
const start_row=ds.getRange('B7:B').getValues().filter(String).length +6; //calculate max row
if (yest_data.length>0){
ds.getRange(start_row+1,1,yest_data.length,yest_data[0].length).setValues(yest_data);
};
}
When checking date equality, you should use the date value or epoch time. Try this instead, which allows you to pass a specific date as your today value.
function test() {
var now = new Date('October 28, 2020');
var today = new Date(now.setHours(0, 0, 0, 0));
copyYesterday(today);
}
/**
* Copy yesterday's responses from Sheet1 to Sheet2.
* #param {Date} [today] - Optionally include a value to use as "today".
*/
function copyYesterday(today) {
if (today == null) { today = new Date(new Date().setHours(0, 0, 0, 0)); }
var yesterday = (new Date(today)).setDate(today.getDate() - 1);
var ss = SpreadsheetApp.getActive();
var sourceSheet = ss.getSheetByName('Sheet1');
var sourceData = sourceSheet.getDataRange().getValues();
var responses = [];
for (var rowIndex = 0; rowIndex < sourceData.length; rowIndex++) {
var row = sourceData[rowIndex];
var responseDate = row[0];
if (responseDate.valueOf() == yesterday) {
responses.push(row);
}
}
if (responses.length > 0) {
var destination = ss.getSheetByName('Sheet2');
destination.getRange(destination.getLastRow()+1, 1, responses.length, responses[0].length).setValues(responses);
}
}
function copyrange() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Discrepancy Report - Outlet'); //source sheet
var rg=sh.getRange(1,10,sh.getLastRow(),1);
var vA= rg.setNumberFormat("#").getValues();
var ds = ss.getSheetByName('Vaishali Nagar - DTR'); //destination sheet
var data=[];
var j=[];
var today = new Date().valueOf();
var yesterday = new Date(today.getFullYear(),today.getMonth(),today.getDate()-1).valueOf();
for (var i=0;i<vA.length;i++) {
var dt=new Date(vA[i][0]);
var dtv=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()).valueOf();
if (dtv==today) {
data.push(sheet.getRange(i+1,1,1,9).getValues());
}
}
ds.getRange(getColumnHeight(ds,2,ss)+1,1,data.length,data[0].length).setValues(data);
}
function getColumnHeight(col,sh,ss){
var ss=ss||SpreadsheetApp.getActive();
var sh=sh||ss.getActiveSheet();
var col=col||sh.getActiveCell().getColumn();
const rcA=sh.getRange(1,col,sh.getLastRow(),1).getValues().reverse()
let s=0;
for(let i=0;i<rcA.length;i++) {
if(rcA[i][0].toString().length==0) {
s++;
}else{
break;
}
}
return rcA.length-s;
}
I assume it has something to do with timezone, but I cannot figure out how to add a day. I would like to only display the month. The issue is at the bottom of the code:
function compull() {
var linkssheet = SpreadsheetApp.openById("1hxkrSKhoUyveyK7dLr-xPpYxhZVvlKJt3S5L6rpMS7w").getSheetByName("Links");
var comsource = SpreadsheetApp.openById("1egqeIX6Lf2As2tsT0U8yhV41fg7as3dOLnwGryU9GVs");
var adjustmentssource = comsource.getSheetByName("Adjustments").getRange("A:I").getValues().filter(function(item){ return item[0] != ""; });
var compullsource = comsource.getSheetByName("Calculator").getRange("A:Q").getValues().filter(function(item){ return item[0] != ""; });
//var length = 4;
var length = 1 + getLastRowSpecial(linkssheet.getRange("E:E").getValues());
var comlength = 1 + getLastRowSpecial(compullsource);
var adjlength = 1 + getLastRowSpecial(adjustmentssource);
for (i=2; i<length; i++) {
var writelocation = SpreadsheetApp.openByUrl(linkssheet.getRange(i,5).getValue());
var compull = writelocation.getSheetByName("Commissions");
var id = linkssheet.getRange(i,1).getDisplayValue();
var id2 = linkssheet.getRange(i,2).getDisplayValue();
var rowlength = comlength;
var columnlength = compullsource[0].length;
dataarray = [];
dataarray.push(compullsource[0].slice());
for (j=1;j<compullsource.length;j++){
if(compullsource[j][1] == id){
dataarray.push(compullsource[j].slice());
}
}
for (k=0;k<dataarray.length;k++){
dataarray[k].splice(1,2);
}
compull.getRange(4,1,dataarray.length,dataarray[0].length).clearContent();
compull.getRange(4,1,dataarray.length,dataarray[0].length).setValues(dataarray);
var rowlength = adjlength;
var columnlength = adjustmentssource[0].length;
dataarray2 = [];
dataarray2.push(adjustmentssource[0].slice());
for (j=1;j<adjustmentssource.length;j++){
if(adjustmentssource[j][1] == id2){
dataarray2.push(adjustmentssource[j].slice());
}
}
for (k=0;k<dataarray2.length;k++){
dataarray2[k].splice(0,2);
}
compull.getRange(4,17,dataarray2.length,dataarray2[0].length).clearContent();
compull.getRange(4,17,dataarray2.length,dataarray2[0].length).setValues(dataarray2);
SpreadsheetApp.flush();
//change date in adjustments in Column Q
var date = compull.getRange("Q5:Q")
var values = date.getValues();
values[0][0] = Utilities.formatDate(date, "GMT-8:00", "MM");
You should know that the spreadsheet settings and the script project have their own timezone, so if you have problems with dates one of the first things that you should check is that each of them is using the correct timezone.
This part of the code is wrong
var date = compull.getRange("Q5:Q")
var values = date.getValues();
values[0][0] = Utilities.formatDate(date, "GMT-8:00", "MM");
The problem is that the first argument of formatDate should be a Date object but date had assigned a Range object.
Resources
Working with Dates and Times
I have a rota (a fixed order of rotation (as of persons or duties)) that I've already had help with this week. It's up & running as is, but for simpler reading I'd like to transpose it.
You can see the transposed sheet as I'd like it here
The current script is for the pre-transposed table.
It would search Column 0 for the date. If it was was 7 days away it would retrieve the name from Column 1 & match it with e-mail address in separate sheet etc.
What I'd like to do is instead have the Date in Row 0 & then subsequent names in Row 1 etc etc
I've tried various things. I've stepped through the code & can see what it's doing, & I've done some reading through about 2 dimensional arrays, but I can't seem to find a way of getting the code to work down through columns, instead of across the rows.
Here's the code:
function sendEmails() {
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss1.getSheetByName("Rota")
ss1.setActiveSheet(sh1);
var rotalink = "https://docs.google.com/spreadsheets/d/1LgzUWSAGA2kbpar8r5nosU1bSHF7nrtvtUiHS3nB_e8";
var sheet = SpreadsheetApp.getActiveSheet();
// Fetch the range
var dataRange = sheet.getRange("B3:G50")
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var today=new Date();
var timecell = new Date(row[0]);
var timediff = new Date();
var one_day=1000*60*60*24;
var daystogo = Math.ceil((timecell.getTime()-today.getTime())/(one_day));
if (daystogo==7) {//only e-mail people with one week to go. To change that alter the "7" to the number of days you want
var subject = "Rota reminder!";
var emailAddress = [];
var message;
message = "Hello \n\n"+
"You are down to help at Youth Café this week. \n\n" +
"Please see the below rota for your role \n\n" +
"If you have any questions or problems let us know at thameyouthcafe#gmail.com \n\n" +
"Remember, you can check the rota anytime by clicking on the link below: \n\n"+
rotalink
for (var x = 1; x < 5; x++) { // 5 because emails are till col4
// var emailAddress = []; // Start by collecting the non-blank emails in an array
if (getEmailFromName(row[x]) != "") {
emailAddress.push(getEmailFromName(row[x]))
}
}
emailAddress = emailAddress.join(); // Join the array to get a comma separated string
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
and here's the getEmailFromName function that matches with SKey (which I presume comes from the "i" variable in the first function?
function getEmailFromName(sKey) {
// to use this function, don’t put anything in the first column (A) or row (1).
// Put the name (i.e. the key, or what we’re looking for) in column B.
// Put what we want to return in column C.
var columnToSearch = 1; //column B
// Set the active sheet to our email lookup
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss1.getSheetByName("EmailContactList")
ss1.setActiveSheet(sh1);
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var line = -1;
for( var i = 0; i < data.length; i++ ) {
if( data[i][columnToSearch] == sKey ) {
line = i;
break;
}
}
if( line != -1 ) {
//do what you want with the data on "line"
return data[line][2]; //value on column C of the matched line
}
else {
return "";
// if criteria is not found
}
}
Try it this way:
function sendEmails() {
var ss1 = SpreadsheetApp.getActive();
var sh1 = ss1.getSheetByName("Rota")
ss1.setActiveSheet(sh1);
var rotalink = "https://docs.google.com/spreadsheets/d/1LgzUWSAGA2kbpar8r5nosU1bSHF7nrtvtUiHS3nB_e8";
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange("B3:G50")
var data = dataRange.getValues();
for (var i=0;i<dataRange.length;i++) {
var row = data[i];
var today=new Date();
var timecell = new Date(row[0]);
var timediff = new Date();
var one_day=1000*60*60*24;
var daystogo = Math.ceil((timecell.getTime()-today.getTime())/(one_day));
if (daystogo==7) {
var subject = "Rota reminder!";
var emailAddress = [];
var message = Utilities.formatString('Hello\n\nYou are down to help at Youth Café this week.\n\n Please see the below rota for your role \n\nIf you have any questions or problems let us know at thameyouthcafe#gmail.com \n\nRemember, you can check the rota anytime by clicking on the link below: \n\n%s',rotalink);
for (var x=1;x<5;x++) {
if(data[i][x]) {
emailAddress.push(data[i][x]);
}
}
MailApp.sendEmail(emailAddress.join(), subject, message);
}
}
}
Managed to solve it - thank you for your contributions. Turned out it was incredibly simple.
Just had to change this line:
var timecell = new Date(data[0])
to this:
var timecell = new Date(data[0][i])
so it iterates through the first row of each column.
How can I change the background color for only the rows that were (true) as per function checkDate(row) on the originating sheet "Pasco"? Is this possible?
A little bit about the script:
A date range is inputted through function getDateRange(), all rows in sheet "Pasco" is checked for if they meet that date range through function checkDate(row). If it does meet the date range (true), function filterRows() essentially filters the rows from "Pasco" sheet, and moves them over to another sheet "Copy of Pasco".
Another way of asking my question, how can I get a range of all the rows that were "true" in sheet "Pasco". If "Pasco" wasn't sorted by date, this could mean multiple ranges, right? Once I have a range I'd be able to change background easy.
If you are to test the script, please create two sheets, 'Pasco' and 'Copy of Pasco'. In 'Pasco' Starting from row 2, place some dates down column I (column 8). To see the filtering in action. 'Copy of Pasco' will be deleted/created on each run.
Thank you for your time =)
var globalStartDate;
var globalEndDate;
function getDateRange(){
var startui = SpreadsheetApp.getUi();
var startprompt = startui.prompt('Start Date', 'Enter a date in m/d/y format', startui.ButtonSet.OK_CANCEL);
var startdate = new Date(startprompt.getResponseText());
var startdatemilliseconds = startdate.getTime();
Logger.log(startdate);
Logger.log(startdatemilliseconds);
globalStartDate = startdatemilliseconds;
var endui = SpreadsheetApp.getUi();
var endprompt = endui.prompt('End Date', 'Enter a date in m/d/y format', endui.ButtonSet.OK_CANCEL);
var enddate = new Date(endprompt.getResponseText());
var enddatemilliseconds = enddate.getTime();
Logger.log(enddate);
Logger.log(enddatemilliseconds);
globalEndDate = enddatemilliseconds;
}
function checkDate(row) {
Logger.log(row[8].getTime() <= globalEndDate && row[8].getTime() >= globalStartDate);
return (row[8].getTime() <= globalEndDate && row[8].getTime() >= globalStartDate); // Check column H
}
function filterRows() {
var Spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = Spreadsheet.getSheetByName('Pasco');
var sheetdelete = Spreadsheet.getSheetByName('Copy of Pasco');
Spreadsheet.deleteSheet(sheetdelete);
Spreadsheet.setActiveSheet(sheet1);
Spreadsheet.duplicateActiveSheet();
var headers = 1; // # rows to skip
var sheet2 = Spreadsheet.getSheetByName('Copy of Pasco');
var range = sheet1.getDataRange();
var data = range.getValues();
var headerData = data.splice(0,headers); // Skip header rows
getDateRange();
var filteredData = data.filter( checkDate );
var outputData = headerData.concat(filteredData); // Put headers back
Logger.log(filteredData)
sheet2.clearContents(); // Clear content, keep format
// Save filtered values
sheet2.getRange(1, 1, outputData.length, outputData[0].length).setValues(outputData);
}
Sorry I don't have time to read through your code and give you a complete answer but you could just add a loop to go through the sheet and set the background colour of each row with 'true'.
In my script below I assume 'true' is in column A.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var data = sheet.getRange(1, 1, sheet.getLastRow()).getValues();
var lastCol = sheet.getMaxColumns();
for (var i = 0; i < data.length; i ++){
if(data[i][0] == true){
sheet.getRange(i + 1, 1, 1, lastCol).setBackground('Yellow');
}
}
}
EDIT
Insert this code after you call getDateRange() in the filter rows function.
var lastCol = sheet1.getMaxColumns();
for(var i = headers; i < data.length ; i++){
if(data[i][8].getTime() <= globalEndDate && data[i][8].getTime() >= globalStartDate){
sheet1.getRange(i, 1, 1, lastCol).setBackground('Yellow');
}
}
Your filter rows function should now look like this:
function filterRows() {
var Spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = Spreadsheet.getSheetByName('Pasco');
var sheetdelete = Spreadsheet.getSheetByName('Copy of Pasco');
Spreadsheet.deleteSheet(sheetdelete);
Spreadsheet.setActiveSheet(sheet1);
Spreadsheet.duplicateActiveSheet();
var headers = 1; // # rows to skip
var sheet2 = Spreadsheet.getSheetByName('Copy of Pasco');
var range = sheet1.getDataRange();
var data = range.getValues();
var headerData = data.splice(0,headers); // Skip header rows
getDateRange();
var lastCol = sheet1.getMaxColumns();
for(var i = headers; i < data.length ; i++){
if(data[i][8].getTime() <= globalEndDate && data[i][8].getTime() >= globalStartDate){
sheet1.getRange(i + headers, 1, 1, lastCol).setBackground('Yellow');
}
}
var filteredData = data.filter( checkDate );
var outputData = headerData.concat(filteredData); // Put headers back
Logger.log(filteredData)
sheet2.clearContents(); // Clear content, keep format
// Save filtered values
sheet2.getRange(1, 1, outputData.length, outputData[0].length).setValues(outputData);
}
I have recently began a project to allow communication between separate groups of people to confirm appointments for meetings. The problem is I can't seem to be able to send the response from a form to the google calendar ID that I put in the function.
Could somebody explain what is going wrong with the code or lead me in the right direction to solve this issue?
Here is the code:
//Function records responses and sends them to a google calendar.
function onFormSubmit(e) {
var form = FormApp.getActiveForm(); // opens active form
var usarResponses = form.getResponses(); // collects responses
//Loop creates an array for responses
for(var i = 0; i < usarResponses.length; i++){
var usarResponse = usarResponses[i];
}
//responses are put into variables.
var name = usarResponses[0];
var appointmentName = usarResponses[1];
var appointmentDate = usarResponses[2];
var uDescription = usarResponses[3];
var cal = CalendarApp.getCalendarById('kq14it8fl42i560gl6ueeo4qu8#group.calendar.google.com').createEvent(appointmentName, new Date(appointmentDate), new Date('8:00:00 UTC') , {description: uDescription});
}
This is a tricky one. Here is what worked for me:
function createEvent() {
var form = FormApp.getActiveForm();
var cal = CalendarApp.getDefaultCalendar();
var responses = form.getResponses();
var len = responses.length;
var last = len – 1;
var items = responses[last].getItemResponses();
var email = responses[last].getRespondentEmail();
var name = items[0].getResponse();
var bring = items[1].getResponse();
var date = items[2].getResponse();
Logger.log(date);
var replace = date.replace(/-/g,”/”);
Logger.log(replace);
var start = new Date(replace);
Logger.log(‘start ‘+start);
//Logger.log(newStart.getHours());
var endHours = 2+0+start.getHours();//2 hour event
//Logger.log(start.getDay());
var day = start.getDate();
var minutes = start.getMinutes();
var year = start.getFullYear();
var month = start.getMonth();
var hours = start.getHours();
var d = new Date(year, month, day, endHours, minutes);
Logger.log(d);
var event = cal.createEvent(‘Class Party ‘+name+’ brings ‘+bring, start, d)
.addGuest(email)
.setDescription(name+’ you will be bringing ‘+bring+’ to the party.’);
GmailApp.sendEmail(email, name+’ a Google Calendar invite has been created for you’, name+’ You filled out the Google Form for the date of ‘+start+’. Check your Google Calendar to confirm that you received the invite.\n’);
}