Filter gmails using timestamp - google-apps-script

Am new dabbling with Google Apps Script; would like to ask if I'm in the right direction, and how to I manipulate time within the script.
I'm struggling in trying to maniuplate time values in Google App Script, basically I am able to pull the timestamp of each email sent, but I only want to paste into the spreadsheet email information that were recent, e.g. within 30minutes from script run time. This is to avoid pulling duplicate information.
Not sure if there is a currentTime() function here, or I have to create a new Date() object and do some calculations from there. Tried a few variations and nothing seemed to work proper.
Would appreciate any help in getting towards the right direction in doing this thank you!
function getDetails(){
var DEST_URL = "SHEET_URL"; //redacted for sensitivity
var DEST_SHEETNAME = "Test";
var destss = SpreadsheetApp.openByUrl(DEST_URL);
var destSheet = destss.getSheetByName(DEST_SHEETNAME);
var threads = GmailApp.search("FILTERS"); //filter settings redacted for sensitivity
for(var i = 0; i < threads.length; i++){
var messages=threads[i].getMessages();
for(var j =0; j < 1; j++){ //only take first message in thread
var message = messages[j];
var subject = message.getSubject() ;
var sentTimeStamp = message.getDate();
if(sentTimeStamp is within last 30minutes as of script run time){ //this is where i need help
var delimitString = subject.split("is sent");
var detailName = delimitString[0];
var lastRow = destSheet.getLastRow();
destSheet.getRange(lastRow + 1,1).setValue(detailName);
destSheet.getRange(lastRow + 1,2),setValue(sentTimeStamp);
}
}
}
}

You can convert timeStamp into ms seconds and then compare to the value of "30 s ago"
Sample:
var sentTimeStamp = message.getDate();
var now = new Date();
var ThirtyMinutesAgo = now-30*60*1000;
if(sentTimeStamp.getTime() < ThirtyMinutesAgo){
...
}
References:
newDate()
getTime()

Another idea would be to query for emails that you received the last 30 minutes.
Explanation:
You can get the emails that you received the last 30 minutes ago as a query in the GmailApp.search function. See this link to see what filters you can use.
This will get the last emails with keyword "FILTERS" that you received the last 30 minutes.
var ThirtyMinutesAgo = new Date();
ThirtyMinutesAgo.setMinutes(ThirtyMinutesAgo.getMinutes() - 30);
const queryString = `"FILTERS" newer:${Math.round(ThirtyMinutesAgo.getTime()/1000)}`
const threads = GmailApp.search(queryString); // threads the last 30 minutes
This approach is more efficient for two reasons:
You have less data (threads) to iterate over with the for loop.
You don't need to apply and if statement on every thread.
Solution:
function getDetails(){
var DEST_URL = "SHEET_URL"; //redacted for sensitivity
var DEST_SHEETNAME = "Test";
var destss = SpreadsheetApp.openByUrl(DEST_URL);
var destSheet = destss.getSheetByName(DEST_SHEETNAME);
// var threads = GmailApp.search("FILTERS"); //filter settings redacted for sensitivity
// new code
var ThirtyMinutesAgo = new Date();
ThirtyMinutesAgo.setMinutes(ThirtyMinutesAgo.getMinutes() - 30);
const queryString = `"FILTERS" newer:${Math.round(ThirtyMinutesAgo.getTime()/1000)}`
const threads = GmailApp.search(queryString); // threads the last 30 minutes
//
for(var i = 0; i < threads.length; i++){
var messages=threads[i].getMessages();
for(var j =0; j < 1; j++){ //only take first message in thread
var message = messages[j];
var subject = message.getSubject() ;
var sentTimeStamp = message.getDate();
var delimitString = subject.split("is sent");
var detailName = delimitString[0];
var lastRow = destSheet.getLastRow();
destSheet.getRange(lastRow + 1,1).setValue(detailName);
destSheet.getRange(lastRow + 1,2),setValue(sentTimeStamp);
}
}
}
}

Related

Google Sheets script hangs on getRange

POST UPDATE:
Hi, at the end the problem was seemingly not related to the getvalue() itself but to the source, which was a range imported by an IMPORTRANGE formula. This formula was bugging. I do not really know why but I assume it was somehow related to the size of the spreadsheet. I have not been able to check this point. I tried to download the file without success. Finally, I have build a new spreadsheet. Things now seem to be working alright now. But still I’d like to now what happened to understand how to avoid it next time.
Anyway, thank you for your help.
ORIGINAL POST:
I experiencing problems with the getRange function in GAS. It's quite a simple code which was working normally until something (I do not know what) went wrong and then started to fail. I am just trying to gather a date value from a cell to use it afterwards to create an email.
I am trying to debug the code step by step by means of Logger and vars but I am struggling to figure out what happens when I try to getValue from the corresponding monocell range.
This below is the code I am trying currently:
// Retreive email receipts from Form submitted choice
function EmailUpdatefromForm()
{
var spreadsheet = SpreadsheetApp.openById("1ImOXXXXXxSDz8AqjMWtuSPtg7xMejqPpHQ9vwDnY");
var form = FormApp.getActiveForm();
var formResponses = form.getResponses();
var formlength = formResponses.length;
Logger.log(formlength);
var formResponse = formResponses[formResponses.length-1];
var itemResponses = formResponse.getItemResponses();
var itemlength = itemResponses.length;
Logger.log(itemlength);
for (var j = 0; j < itemResponses.length; j++) {
var itemResponse = itemResponses[j];
var emailto = itemResponse.getResponse();
Logger.log(itemResponse+" - "+emailto);
}
var email = (emailto? emailto[0] : "xxxxxxxxxxs#xxxxxxx.com");
var sheet = spreadsheet.getSheetByName("report");
var log1 = spreadsheet.getId(); //--> THIS CHECKPOINT WORKS AND GETS THE PROPER SS ID
var log2 = sheet.getSheetId(); //--> THIS CHECKPOINT WORKS AND GETS THE PROPER SHEET ID
var log3 = sheet.getRange(2, 1) //--> THIS CHECKPOINT WORKS ALTHOUGH I CANNOT SEE IF IT IS ACTUALLY POINTING TO THE PROPER CELL
var log4 = sheet.getRange(2, 1).getValues(); //--> THIS CHECKPOINT GETS STUCK\\
var updatedate = sheet.getRange(2,1).getValue(); //--> THIS IS WHAT I REALLY WANT TO DO
var PDFdate = Utilities.formatDate(updatedate, "GMT+1", "yyyyMMdd")
}
I have found several posts mentioning issues with getRange but I have not found any response really clarifying what's happening here and how to turn it around.
Thank you in advance for your support!
function EmailUpdatefromForm() {
var spreadsheet = SpreadsheetApp.openById("1ImOXXXXXxSDz8AqjMWtuSPtg7xMejqPpHQ9vwDnY");
var form = FormApp.getActiveForm();
var formResponses = form.getResponses();
var formlength = formResponses.length;
Logger.log(formlength);
var formResponse = formResponses[formResponses.length-1];
var itemResponses = formResponse.getItemResponses();
var itemlength = itemResponses.length;
Logger.log(itemlength);
for (var j = 0; j < itemResponses.length; j++) {
var itemResponse = itemResponses[j];
var emailto = itemResponse.getResponse();
Logger.log(itemResponse+" - "+emailto);
}
var email = (emailto? emailto[0] : "xxxxxxxxxxs#xxxxxxx.com");
var sheet = spreadsheet.getSheetByName("report");
var log1 = spreadsheet.getId();
var log2 = sheet.getSheetId();
var log3 = sheet.getRange(2, 1);
var log4 = log3.getValue();
var updatedate = log4;
var PDFdate = Utilities.formatDate(updatedate, "GMT+1", "yyyyMMdd")
}

Logging Google mail in Google sheet, Google Apps Script

HELP! I’m using a script I basically cribbed from Tom Woodward at Bionice Teaching to record email messages in a spreadsheet.
http://bionicteaching.com/auto-logging-email-via-google-script/
I need to add a column that collects any labels that have been attached to the messages. I need to get this done for my work, but I'm brand new to Google Apps Script and really need someone to hold my hand... Essentially doing it for me, then teaching me what it was you did. I really appreciate any help you can give me in any case. Thanks
Here is what I’m using:
function myFunction() {
//this is just the stuff that recognizes what spreadsheet you're in
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var sheet = ss.getSheetByName("data"); //gets the right sheet
//this chunk gets the date info
var today = new Date();
var dd = today.getDate()-1;
var mm = today.getMonth()+1; //January is 0 DO NOT FORGET THIS
var yyyy = today.getFullYear();
var yesterday = yyyy + '/' + mm + '/' + dd;
//****************************************************
//searches your GMail for emails written after yesterday
var query = "after:" + yesterday;
var threads = GmailApp.search(query);
Logger.log('threads len ' + threads.length);
Logger.log(query);
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
Logger.log(messages);
for (var m = 0; m < messages.length; m++) {
var supportStats = [];
//here's where you decide what parts of the email you want
var from = messages[m].getFrom(); //from field
Logger.log(from);
var time = messages[m].getDate();//date field
Logger.log(time);
var subject = messages[m].getSubject();//subject field
Logger.log(subject);
var body = messages[m].getPlainBody();//body field
Logger.log(body);
var mId = messages[m].getId();//id field to create the link later
var mYear = time.getFullYear();
var mMonth = time.getMonth()+1;
var mDay = time.getDate();
var messageDate = mYear + '/' + mMonth + '/' + mDay;
Logger.log('msg date ' + messageDate);
//decides what found emails match yesterday's date and push them to an array to write to the spreadsheet
if (messageDate === yesterday) {
supportStats.push(from);
supportStats.push(time);
supportStats.push(subject);
supportStats.push(body);
supportStats.push('https://mail.google.com/mail/u/0/#inbox/'+mId); //build the URL to the email
SpreadsheetApp.getActiveSheet().appendRow(supportStats); //writes to the spreadsheet
}
}
}
}
Here is the results I'm getting... Perfect!
Except I'd like one more column that adds the labels that are on each message. How do I do that?
spreatsheet results of Google Apps script mail->sheet
You can use this:
var labels = threads[i].getLabels();
GmailThread::getLabels()
GmailThread has labels, not GmailMessage. It returns an array of labels. Maybe use:
var labelsString = "";
var labelArray = []
for each (var label in labels)
{
labelArray.push(label.getName());
}
if (labelArray.length > 0)
{
labelsString = labelArray.join(',');
}
to insert into the row of the spreadsheet.
GmailLabel::getName()

Google Script: print school schedule data for each teacher

This is my first question here. I have searched the site to the best of my knowledge, but haven't found any other examples of my question.
Here is the Google Sheets file
https://docs.google.com/spreadsheets/d/1HxyhoxuPK8H8_vhLg0ZZ-THyOn1cn9nPYRyls8y47iM/edit?usp=sharing
I have 2 sheets in the same Google Sheets document.
The first, "schema" contains a base-school schedule for a teacher, with different classes in different blocks. This needs to be replicated, so that all teachers has this exact setup - so that all unique users have the same 50 lines of schedule data - only with their allocated classes.
The second sheet contains information about the users. Each line contains a UNI-login username and their designated class 1a-1, 4a-1 and 8a-1 for user uni12345 for example. 1a-1 needs to replace 1, in "uni12345"'s schedule data.
I would like all these data (a lot of lines) combined into one sheet, fx. called "combined" - but you get your pick on the name :-)
I have made a Combined Example sheet, that presents how I would like the output for user1+2 in the list.
If the question already is partly answered elsewhere, I'll be happy to look at that also!
Edit:
Since my original question I have made it work - only now I'm hitting the 6min script exectution time limit. Any way around that, eg. optimization?
function merge() {
var CurrentDate = new Date() ;
var CurrentDateString1 = Utilities.formatDate(CurrentDate, "GMT", "MM-dd-yyyy HH:mm:ss") ;
var ss=SpreadsheetApp.getActive();
// var mergeSht=ss.getSheetByName(CurrentDateString1);
var users=ss.getSheetByName('users');
var schema=ss.getSheetByName('schema');
var mergeSht = ss.insertSheet();
mergeSht.setName(CurrentDateString1);
var usersValues = users.getDataRange().getValues();
var schemaValues = schema.getDataRange().getValues();
var counter = 1;
for(var n=1; n < usersValues.length ; n++){
var usersValue = usersValues[n];
var uniName = usersValue[5];
var levelInd = usersValue[2];
var levelMellem = usersValue[3];
var levelUdsk = usersValue[4];
// Logger.log(usersValues[n][5])
for(var i=1; i < schemaValues.length ; i++){
var schemaValue = schemaValues[i];
if (schemaValue != null && schemaValue.length > 0) {
var level = schemaValue[3];
var subject = schemaValue[4];
var room = schemaValue[5];
var day = schemaValue[6];
var position = schemaValue[7];
var levelAfd = getlevel(level,levelInd, levelMellem, levelUdsk);
Logger.log(levelAfd);
// print
var row=[];
row.push(counter++,'','unilogin:'+ uniName, levelAfd, subject, room, day, position);
mergeSht.appendRow(row);
}
}
}
}
function getlevel(level, levelInd, levelMellem, levelUdsk){
switch (level)
{
case 1:
return levelInd;
case 4:
return levelMellem;
case 7:
return levelUdsk;
}
}
Here's something else you could try:
This code creates a new array which avoids having to push each row one by one. Instead, at the end in loads the entire mergeSht all at one time.
function merge()
{
var CurrentDate = new Date() ;
var CurrentDateString1 = Utilities.formatDate(CurrentDate, "GMT", "MM-dd-yyyy HH:mm:ss") ;
var ss=SpreadsheetApp.getActive();
// var mergeSht=ss.getSheetByName(CurrentDateString1);
var users=ss.getSheetByName('users');
var schema=ss.getSheetByName('schema');
var mergeSht = ss.insertSheet();
mergeSht.setName(CurrentDateString1);
var usersValues = users.getDataRange().getValues();
var schemaValues = schema.getDataRange().getValues();
var counter = 1;
var mergeA=[];//Small change
mergeA.push(['H1','H2','H3','H4','H5','H6','H7','H8']); //First row is headers
for(var n=1; n < usersValues.length ; n++)
{
var usersValue = usersValues[n];
var uniName = usersValue[5];
var levelInd = usersValue[2];
var levelMellem = usersValue[3];
var levelUdsk = usersValue[4];
// Logger.log(usersValues[n][5])
for(var i=1; i < schemaValues.length ; i++)
{
var schemaValue = schemaValues[i];
if (schemaValue != null && schemaValue.length > 0)
{
var level = schemaValue[3];
var subject = schemaValue[4];
var room = schemaValue[5];
var day = schemaValue[6];
var position = schemaValue[7];
var levelAfd='';
switch(level)
{
case 1:
levelAfd=levelInd;
break;
case 4:
levelAfd=levelMellem;
break;
case 7:
levelAfd=levelUdsk;
break;
default:
levelAfd='';
break;
}
if(levelAfd)
{
mergeA.push([counter++,'','unilogin:'+ uniName, levelAfd, subject, room, day, position]);
}
}
}
}
mergeSht.getRange(1,1,mergeA.length,mergeA[0].length).setValues(mergeA);
}

Split Array Function on Email PlainBody() MSG using Google Script

I use the following code below to grab emails, get the plainbody(), date and messageto bring them into Google sheet in COLUMN A.
Then in the spreadsheet I copy down the formula in COLUMN C
=ARRAYFORMULA(IFERROR(SPLIT(A3,"|")))
(Which I tested this works as well - not sure if one is preferred)
=SPLIT($A2,"|")
I'd like to combine these functions and have attempted the following
function getEmails() {
var ss = SpreadsheetApp.getActiveSheet();
var threads = GmailApp.search("is:starred in:TanWebOppCRM");
for (var i=0; i<threads.length; i++) {
var messages = threads[i].getMessages();
for (var j=0; j<messages.length; j++) {
var msg = messages[j].getPlainBody();
var dat = messages[j].getDate();
var msgParse = '=SPLIT($A2,"|")';
/*var msgParse = '=ARRAYFORMULA(IFERROR(SPLIT($A2,"|")))';*/
ss.appendRow([msg, dat, msgParse]);
}
threads[i].moveToTrash();
}
}
This DOES "WORK". For example - I know I just sent myself a test email that can be extracted. I know the next row that WILL be appended is Row 30 - and change the code
var msgParse = '=SPLIT($A2,"|")';
to
var msgParse = '=SPLIT($A30,"|")';
Then the extraction works, and you see the split happen.
If I was to import 2 new rows - obviously
$A30 /* stays A30 - so I tried-
var msgParse = '=SPLIT($A[j],"|")';
Just to see if the [j] dynamically changed - even though I know j is for message loop
The question...
how do I get the following line to increment to the append row value
so that each loop changes the row reference to the appended row - so if I was starting from row 30
var msgParse = '=SPLIT($A30,"|")';
var msgParse = '=SPLIT($A31,"|")';
var msgParse = '=SPLIT($A32,"|")';
Is what the incremental msgParse value data would be.
You can construct the formula via string concatenation.
If you always just append you can just get the last row and add 1 (because the new row will be one row below) and use that as the row reference:
var msgParse = '=SPLIT($A' + (ss.getLastRow() + 1) + ', "|")';
Since I'm new to Script - and I couldn't find this answer - I wanted to add my own answer.
#Robin definitely pointed me in the right direction - I didn't realize I can use string concatenation - however the solution provided did not increment and once that string concatenation was written - it remain for the rest of the FOR loop
The final piece was adding a counter - I could probably eliminate the first row = variable and there is probably a way to make this more efficient But it took in 20 sample emails from gmail and parsed them into spreadsheet in maybe 6 or 7 seconds.
function getEmails() {
var ss = SpreadsheetApp.getActiveSheet();
var threads = GmailApp.search("is:starred in:TannerWebOppCRM");
var row = ss.getLastRow();
for (var i=0; i<threads.length; i++) {
var messages = threads[i].getMessages();
var newrow = row + 1;
for (var j=0; j<messages.length; j++) {
var msg = messages[j].getPlainBody();
var dat = messages[j].getDate();
var msgParse ='=SPLIT($A'+ (newrow) + ', "|")';
ss.appendRow([msg, dat, msgParse]);
newrow = newrow + 1;
}
threads[i].moveToTrash();
}
}
To complete this post I wanted to include the update and addition to the answer I accepted because that technically answered my question - just didn't put it into context how to make it work in my script.

Handling time duration in Google Sheets Api

I'm making a basic script that fetches time durations from external sheets, and sums them. What I have so far is:
function getHorasCasoUso() {
var libros = {
"key1" : "externalSheetURL1",
"key2" : "externalSheetURL2",
...
};
var horas_por_caso_uso = {};
for (var key in libros) {
var libro = SpreadsheetApp.openByUrl(libros[key]);
var sheets = libro.getSheets();
for (var i = 0; i < sheets.length; i++) {
var sheet = sheets[i];
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var j = 5; j < numRows; j++) {
var row = values[j];
var caso_uso = row[6];
var horas = row[4]; //The cell format is 'Duration'
if (!caso_uso)
continue;
if (!!horas_por_caso_uso[caso_uso])
horas_por_caso_uso[caso_uso] += horas;
else
horas_por_caso_uso[caso_uso] = horas;
}
}
}
var ss = SpreadsheetApp.getActiveSheet();
for (var key in horas_por_caso_uso) {
ss.appendRow([key, horas_por_caso_uso[key]]);
}
}
The problem is that the data stored in 'horas' is a string. I want to get the time duration in that cell. How can I do that?
Your issue seems quite similar to the one in this post but at a larger scale...
You should convert row[4] value to minutes (or seconds if you need this accuracy) before adding that value to the total counter.
If the cells are formatted as duration (as you say it is) it should work without changes.(see code at the end of this post)
If not, ie if these values are returned as strings then a simple string manipulation will do the job for you like this :
example : testString = 12:34
function toMinutes(value){
var minutes = Number(value.split(':')[0].replace(' ',''))*60+Number(value.split(':')[1].replace(' ',''));
return minutes;
}
(code working as a function) will return 754 (60*12+34)
usage in your code : var horas = toMinutes(row[4]);
function toMinutes(value){
var duration = new Date(value);
var min = duration.getMinutes()+60*duration.getHours();
return min;
}
You can eventually improve this function with a few error trapping features to handle cases where cell is empty of malformed... Since I don't know what the data look like I prefer let you do it yourself.