AutoForward labeled emails once a month using Google Scripts - email

I'm a complete noob.
I'd like to forward labelled emails on the 1st of every month to another email address. Using Gmail's filters, the label "forwardthis" get applied to my invoices and statements I received during the month. On the 1st of every month, I'd like Google scrips to forward all new emails with the "forwardthis" label to another email address, including attachments.
The script I found doesn't work as I hoped. Not sure what the interval options below are for, but I need to run my script only once a month, on the 1st.
function autoForward() {
var label = 'forwardthis';
var recipient = 'elvis#gmail.com';
var interval = 5; // if the script runs every 5 minutes; change otherwise
var date = new Date();
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;
var threads = GmailApp.search('label:' + label + ' after:' + timeFrom);
for (var i = 0; i < threads.length; i++) {
threads[i].getMessages()[0].forward(recipient); // only the 1st message
}
}
Hope my question is clear. I'll greatly appreciate some guidance, thanks.

Sends labeled emails created after the last time the script was run
Evidently the script was meant to run every 5 minutes so rather that keeping tracked of emailed labels already sent the script just takes the labels created after the current time minus the interval or in other words the labeled emails received since the last time the script was run.
Since you want to run the script on the first of every month you will have to figure how to get the time for the 1st of last month and put that value into the timefrom variable.
function autoForward() {
var label = 'forwardthis';
var recipient = 'elvis#gmail.com';
var interval = 5; // if the script runs every 5 minutes; change otherwise
var date = new Date();
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;//This subtracts five minutes from the current time
var threads = GmailApp.search('label:' + label + ' after:' + timeFrom);//Emails with the correct label and created since 5 minutes ago are sent
for (var i = 0; i < threads.length; i++) {
threads[i].getMessages()[0].forward(recipient); // only the 1st message
}
}
Noob or not, you can figure this out. Most of what you need is right here. Or possibly here.
You can use triggers to run your script once a month. You can read about triggers here.

Related

Counting cloudless days using Google Earth Enigne - Sentinel-5p

I perform some analysis on Sentinel-5P data using Google Earth Engine. I would like to know how many cludless days are during month for each pixel. I am using the code below and it works. The problem is that Sentinel-5P images are captured several times a day so my result is number of cloudless images instead of cloudless days.
//Poland's border
var polska = ee.FeatureCollection('users/patrykgrzybowski1991/POL_adm1');
//upload collection
var collection_january_19 = ee.ImageCollection('COPERNICUS/S5P/NRTI/L3_NO2')//('COPERNICUS/S5P/NRTI/L3_NO2')
//filters
.filterBounds(ee.FeatureCollection('users/patrykgrzybowski1991/POL_adm0'))
.filterDate('2019-01-01', '2019-02-01')
.map(function(img){return ee.Image(img.select('tropospheric_NO2_column_number_density')).updateMask(img.select('cloud_fraction').lt(0.4))})
//cludless images - count
var count_january_19 = collection_january_19.count();
Number of cloudless days is slightly ill-defined when there are multiple images, but if you mean "none of the images on a day had clouds in them", then you probably need to composite the images by day before counting:
var start = ee.Date('2019-01-01')
var daysAsList = ee.List.sequence(0, 31).map(function(n) {
n = ee.Number(n)
var begin = start.advance(n, 'day')
var end = begin.advance(1, 'day')
return collection_january_19.filterDate(begin, end).mosaic()
})
var days = ee.ImageCollection.fromImages(daysAsList).count()

search/match a date on Google Sheets using Google script and copy a source range to a destination range OFFSET from that date

I'm struggling with some simple copy/paste scripting in Google Sheets. I'm a complete scripting newbie. Advanced at Excel/Sheets.
I have a "Today" data entry sheet. I have a "Program" sheet that stores data in a column under a Date header. I want a macro/script that will:
1. cut data from a range on Today
2. search the date row in "Program" to Match whatever date is on the "Today" data entry page.
3. Finally paste data to a range on Program xx rows down from that identified date in step 2
Essentially I want to INDEX/MATCH in reverse. Rather than return a cell in INDEX/MATCH, I want to copy to a cell.
Sheet is here: https://docs.google.com/spreadsheets/d/19r6IbgdYCPOZjNr92MA0vAKQ_R2Fx5JohuC0DE8ekPo/edit?usp=sharing
Below is as far as I've gotten. I can cut/paste from a specific cell to another cell. But need to figure out "var destination" somehow.
function copydata2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getRange('Today!b11:g11');
source.copyTo(ss.getRange('Program!d11:i11'), {contentsOnly: true});
source.clear();
};
Try this:
function test2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var today = ss.getSheetByName("Today");
var program = ss.getSheetByName("Program");
var date = today.getRange("B4").getValue();
var targetDates = program.getRange("D4:KL4").getValues();
var column;
// Get the target column.
for (var i = 0; i < targetDates[0].length; i++){
if (targetDates[0][i].toString() == date.toString()){
column = i + 4; // To account for the first cols.
Logger.log("found the date " + targetDates[0][i] + " at column " + column + " with i = " + i);
break;
}
else continue;
}
// Move the values.
today.getRange("B11:G11").moveTo(program.getRange(11, column)); // Sq w/ Belt
today.getRange("J11:O11").moveTo(program.getRange(16, column)); // 2ct Paused Squat
today.getRange("R11:W11").moveTo(program.getRange(21, column)); // Pin Squat
today.getRange("Z11:AE11").moveTo(program.getRange(26, column)); // 3-0-3 Tempo Squat
today.getRange("AH11:AM11").moveTo(program.getRange(31, column)); // Sq no Belt
today.getRange("B19:H19").moveTo(program.getRange(37, column)); // 1 Ct Paused Bench Press
today.getRange("J19:O19").moveTo(program.getRange(42, column)); // Overhead Press w/ Belt
today.getRange("R19:W19").moveTo(program.getRange(47, column)); // Close Grip Bench Press
today.getRange("Z19:AE19").moveTo(program.getRange(52, column)); // 2 Ct Paused Bench Press
today.getRange("AH19:AM19").moveTo(program.getRange(57, column)); // 3 Ct Paused Bench Press
today.getRange("AP19:AU19").moveTo(program.getRange(62, column)); // Pin Bench Press
today.getRange("B27:H27").moveTo(program.getRange(68, column)); // Deadlift w/ Belt
today.getRange("J27:O27").moveTo(program.getRange(73, column)); // Rack Pull - mid shin
today.getRange("R27:W27").moveTo(program.getRange(78, column)); // 2 ct Paused Deadlift
today.getRange("AH27:AM27").moveTo(program.getRange(83, column)); // Pendlay Rows
}
A couple things, I used moveTo() (documentation here), which cuts/pastes the values to the target, the only issue is that it overwrites the style format. When working with Sheets it's always better to use getValues() and setValues() to avoid making multiple calls to the API. In this case, however, since your data was organized in such a particular way it was better to work like this for complexity's sake. Please let me know if you have any doubts. I tested it on a copy of the sheet you provided and it worked as intended.

Script is taking 11 - 20 seconds to lookup up an item in an 18,000 row data set

I have two Google sheets workbooks.
One is the "master" source of lookup data with a key based on manufacturer item #, which could be anything from 1234 to A-01/234-Name_1. This sheet, referenced via SpreadsheetApp.openByUrl, has 18,000 rows and 13 columns. The key column has been converted to plain text and the sheet is sorted by this column.
The second is the "template" where people enter item #s that they need to look up against the master, typically 20 - 1500 items at a time.
The script is in the template. It is very slow and routinely times out after 30 minutes. It was written by someone else and I am new to App Script, but I think I've managed to understand what the script is doing and where the bottleneck is occurring.
It does a bunch of stuff, but this is the meat of the lookup:
var numrows = master.getDataRange().getNumRows();
var masterdata = master.getDataRange().getValues();
var itemnumberlist = template.getDataRange().getValues();
var retreiveddata = [];
// iterate through the manf item number list to find all matches in the
// master and return those matches to another sheet
for (i = 1; i < template.getDataRange().getValues().length; i++) {
for (j = 0; j < numrows; j++) {
if (masterdata[j][1].toString() === itemnumberlist[i][1].toString()) {
retreiveddata.push(data[j]);
anothersheet.appendRow(data[j]);
}
}
}
I used Logger.log() to determine that each time through the i loop is taking 11 - 19 seconds, which just seems insane.
I've been doing some google searching and I've tried a couple of different things...
First I tried moving the writing of found data out of the for loop so the script would be doing all of its reading first and then writing in one big chunk, but I couldn't get it exactly right. My two attempts are below.
var mycounter = 0;
for (i = 0; i < template.getDataRange().getValues().length; i++) {
for (j = 0; j < numrows; j++) {
if (masterdata[j][0].toString() === itemnumberlist[i][0].toString()) {
retreiveddata.push(masterdata[j]);
mycounter = mycounter + 1;
}
}
}
// Attempt 1
// var myrange = retreiveddata.length;
// for(k = 0; k < myrange; k++) {
// anothersheet.appendRow(retreiveddata.pop([k]);
// }
//Attempt 2
var myotherrange = anothersheet.getRange(2,1,myothercounter, 13)
myotherrange.setValues(retreiveddata);
I can't remember for sure, because this was on Friday, but I think both attempts resulted in the script trying to write the entire master file into "anothersheet".
So I temporarily set this aside and decided to try something else. I was trying to recreate the issue in a couple of sample spreadsheets, but I was unable to do so. The same script is getting through my 15,000 row sample "master" file in less than 1 second per lookup. The only thing I can think of is that I used a random number as my key instead of a weird text string.
That led me to think that maybe I could use a hash algorithm on both the master data and the values to be looked up, but this is presenting a whole other set of issues.
I borrowed these functions from another forum post:
function GetMD5Hash(value) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5,
value);
var txtHash = '';
for (j = 0; j <rawHash.length; j++) {
var hashVal = rawHash[j];
if (hashVal < 0)
hashVal += 256;
if (hashVal.toString(16).length == 1)
txtHash += "0";
txtHash += hashVal.toString(16);
Utilities.sleep(100);
}
return txtHash;
}
function RangeGetMD5Hash(input) {
if (input.map) { // Test whether input is an array.
return input.map(GetMD5Hash); // Recurse over array if so.
Utilities.sleep(100);
} else {
return GetMD5Hash(input)
}
}
It literally took me all day to get the hash value for all 18,000 item #s in my master spreadsheet. Neither GetMD5Hash nor RangeGetMD5Hash will return a value consistently. I can only do a few rows at a time. Sometimes I get "Loading..." indefinitely. Sometimes I get "#Name" with a message about GetMD5Hash being undefined (despite the fact that it worked on the previous row). And sometimes I get "#Error" with a message about an internal error.
This method actually reduces the lookup time of each item to 2 - 3 seconds (much better, but not great). However, I can't get the hash function to consistently work on the input data.
At this point I'm so frustrated and behind on my other work that I thought I'd reach out to the smart people on these forums and hope for some sort of miracle response.
To summarize, I'm looking for suggestions on these three items:
What am I doing wrong in my attempt to move the write out of the for loop?
Is there a way to get my hash value faster or utilize a different method to accomplish the same goal?
What else can I try to help speed up the script?
Any suggestions you can offer would be greatly appreciated!
-Mandy
It sounds like you hit on the right approach with attempting to move the appendRow() call out of the loop. Anytime you are reading or writing to a spreadsheet you can expect the individual call to take 1 to 2 seconds, so this will eat up a lot of time when you get matches. Storing the matches in an array and writing them all at once is the way to go.
Another thing I notice is that your script calls getValues() in the actual for loop condition statement. The condition statement is executed each time on each iteration of the loop, so this is potentially wasting a lot of time even when you don't have matches.
A final tweak that may be helpful depending on your desired behaviour. You can stop the inner for loop after it finds the first match, which, if you only care about the first match or know there will only be one match, will save you a lot of iterations. To do this, put "break" immediately after the retreiveddata.push(masterdata[j]); line.
To fix the getValues issue, Change:
for (i = 1; i < template.getDataRange().getValues().length; i++) {
To:
for (i = 1; i < itemnumberlist.length; i++) {
And that fix along with the appendRow issue, and including the break call:
for (i = 1; i < itemnumberlist.length; i++) {
for (j = 0; j < numrows; j++) {
if (masterdata[j][0].toString() === itemnumberlist[i][0].toString()) {
retreiveddata.push(masterdata[j]);
break; //stop searching after first match, move on to next item
}
}
}
//make sure you have data to write before trying to write it.
if(retreiveddata.length > 0){
var myotherrange = anothersheet.getRange(2,1,retreiveddata.length, retreiveddata[0].length);
myotherrange.setValues(retreiveddata);
}
If you are re-using the same sheet for "anothersheet" on each execution, you may also want to call anothersheet.clear() to erase any existing data before you write your fresh results.
I would pass on the hashing approach altogether, comparing strings is comparing strings, so whether they are hashes or actual part numbers I wouldn't expect a significant difference.

How to automatically generate sequent numbers when using a form

Ahab stated in 2010: the complex looking number based on the Timestamp has one important property, the number can not change when rows are deleted or inserted.
As long as the submitted data is not changed by inserting deleting rows the simple formula =ArrayFormula(ROW(A2:A) - 1) may be the easiest one to use.
For other situations there is no nice reliable solution. :(
Now we live in 2015. Maybe times have changed?
I need a reliable way to number entries using a form.
Maybe a script can do the trick? A script that can add 1 to each entry?
That certain entry has to keep that number even when rows are deleted or inserted.
I created this simple spreadsheet in which I added 1,2, and 3 manually,please have a look:
https://docs.google.com/spreadsheets/d/1H9EXns8-7m9oLbCrTyIZhLKXk6TGxzWlO9pOvQSODYs/edit?usp=sharing
The script has to find the maximum of the former entries, which is 3, and then add 1 automatically.
Who can help me with this?
Grtz, Bij
Maybe a script can do the trick? A script that can add 1 to each
entry?
Yes, that would be what you need to resort to. I took the liberty of entering this in your example ss:
function onEdit(e) {
var watchColumns = [1, 2]; //when text is entered in any of these columns, auto-numbering will be triggered
var autoColumn = 3;
var headerRows = 1;
var watchSheet = "Form";
var range = e.range;
var sheet = range.getSheet();
if (e.value !== undefined && sheet.getName() == watchSheet) {
if (watchColumns.indexOf(range.getColumn()) > -1) {
var row = range.getRow();
if (row > headerRows) {
var autoCell = sheet.getRange(row, autoColumn);
if (!autoCell.getValue()) {
var data = sheet.getDataRange().getValues();
var temp = 1;
for (var i = headerRows, length = data.length; i < length; i++)
if (data[i][autoColumn - 1] > temp)
temp = data[i][autoColumn - 1];
autoCell.setValue(temp + 1);
}
}
}
}
}
For me the best way is to create a query in a second sheet pulling everything from form responses in to second column and so on. then use the first column for numbering.
In your second sheet B1 you would use:
=QUERY(Form!1:1004)
In your second sheet A2 you would use:
=ARRAYFORMULA(if(B2:B="",,Row(B2:B)-1))
I made a second sheet in your example spreadsheet, have a look at it.

How to add more than one function in user-extension.js - Selenium IDE

I am very new to Selenium IDE or javascript and was using google to learn it and do some testing of my website application. In my application I need to input user name and date of activity as part of the testcase.
I used random string generator function I saw online and created a user-extension.js to be called in S-IDE for the user name and was using a storeEval command to generate a date 5 days from today's date to give in the date of activity field. But today being 26th of Apr, when I run the script it is generating a date of 31-Apr which is invalid date and the test is failing.
I again found some code snippets online on how to check if the date generated is valid and then pass valid date back. But when I append this to the user-extension.js (Selenium.prototype.doValidDay) that I am having, this function is not recognized by S-IDE more over my random string function is also not recognized. (Meaning I am not able to find the command when I type on the command line of S-IDE)
Can someone please help me understand how to append multiple commands in a userextension js? I even created 2 js and selected both of them in options tab. Still not working..
Please help...
Try This for getting future date.
Selenium.prototype.doTypeTodaysDate = function(locator){
var dates = new Date();
dates.setDate(dates.getDate() + 5);
var day = dates.getDate();
if (day < 10){
day = '0' + day;
}
month = dates.getMonth() + 1;
if (month < 10){
month = '0' + month;
}
var year = dates.getFullYear();
var prettyDay = day + '/' + month + '/' + year;
this.doType(locator, prettyDay);
}
Copy the above code into note pad and save as date.js
after that add the date.js file into selenium ide
Options->Selenium core Extensions->browse the file
Think it will work for you.
Thank you