The object is to collect gmail data from incoming and outgoing messages, from within all labels, and log it the information to various spreadsheets, respective to the label they belong to.
Essentially - the label will determine which spreadsheet its corresponding gmail data belongs in. I currently have it all setting into a single spreadsheet. This method will soon become unmanageable.
for (var l = 0; l < labels.length; l++) {// ** Runs a for-loop over "labels array".
var label = labels[l].getName();//Gets "this" label name.
var labelThreads = GmailApp.getUserLabelByName(label).getThreads(start, end);//Gets threads in "this" label. (*Set Limits Here*)
var labelMessages = GmailApp.getMessagesForThreads(labelThreads);//Gets array with each email from "this" thread.
for (var t = 0; t <labelThreads.length; t++){// ** Runs a for-loop over threads in a label.
if (labelMessages[t] == undefined){}// If it's empty, skip.
else {// If it's not empty.
emailBody.push([" "]);
emailFrom.push([labelMessages[t][0].getFrom()]);
emailTo.push([labelMessages[t][0].getTo()]);
emailDate.push([Utilities.formatDate(labelMessages[t][0].getDate(), "GMT", "MMM d, EEE, yyyy - HH:mm")]);
emailSubject.push([labelMessages[t][0].getSubject()]);
emailLabel.push([labels[l].getName()]);
}
}
// ** THEN, LOG THE FILLED DATA ARRAYS TO ROWS **
//getSheetValues(startRow, startColumn, numRows, numColumns)
mySheet.getRange(2,2,emailLabel.length,1).setValues(emailLabel);
mySheet.getRange(2,4,emailFrom.length,1).setValues(emailFrom);
mySheet.getRange(2,3,emailTo.length,1).setValues(emailTo);
mySheet.getRange(2,1,emailDate.length,1).setValues(emailDate);
mySheet.getRange(2,5,emailSubject.length,1).setValues(emailSubject);
mySheet.getRange(2,6,emailBody.length,1).setValues(emailBody);
}
}
While I was able to programatically create spreadsheets with the same names as all my labels, I don't quite know how to select and fill them with the correct email data.
I understand that my code currently works because the same number of email data is captured in each array, so all the rows of data "line-up" (this information is provided at (start,end).
full code here: http://pastie.org/9768890
Update- Clarified Question: The specific question is- how can I separate the information stored in the arrays- emailFrom, emailTo, emailDate, emailSubject, emailLabel, into separate sheets - by label. A spreadsheet will have a certain email label name as a title, and I want all email pertaining to that label to be entered into that spreadsheet.
Here's one way to open a specific spreadsheet document (not necessarily the active document):
var ss = SpreadsheetApp.openById("id");
where "id" is a string representing the document ID. If the URL to your spreadsheet is "https://docs.google.com/spreadsheets/d/abc123/edit#gid=0" then the ID is "abc123".
From there, you can access individual sheets like this:
var sheet = ss.getSheetByName("name");
where "name" is a string representing the sheet name.
Once you have your sheet variable (or whatever you want to name it), you can insert data just like you already did: use getRange() to get the range where you want the data, and setValues() to actually insert the data.
Related
I don't know anything about Google Scripts. All the scripts mentioned here under including the formulas have been researched on internet and improvised gradually. I need help of experts like you to achieve my outcomes. I am a beginner, hence please write your answer which I can understand. It would be great if you improve my script / formulas with brief explanation in simple language.
I have two sheets:
1) Form Response Sheet (linked to form)
2) DataBnak Sheet (importing form response data via query: =query('Form Responses 3'!$A:$BH,"",1)
I am using ArrayFormula in "DataBank" Sheet to create a unique text report based on values pulled "Form Responses". This report needs to be emailed to each respondent upon form submit. the report is pulled in CH column thru (ArrayFormula), and once the mail is sent, I am marking "1" in CM column so to ensure duplicate mails are not sent every time the script runs.
=ArrayFormula(IF(ROW($B:$B)=1,"Breif Report",IF(ISBLANK($B:$B),"",if(NOT(ISBLANK($CM:$CM)),"",iferror(vlookup(BU:BU&BV:BV&BW:BW&BX:BX,BriefProfile!$E:$F,2,0),"")))))
Formula Explanation: where Cell in B column is not blank, and where cell in CM column is not blank (mail not sent), then bring the pre-written text based on the look-up value within columns (BU,BV,BW,BX).
What works correct:
1) The ArrayFormula works perfect (it pulls correct pre-written text)
2) Mail Script Works perfect (it sends to mails to those who have not been sent mail earlier)
What does not work:
1) The text report picked for a respondent is same for all respondents. When I remove ArrayFormula and ValuePaste the text report instead of calling it thru the formula mentioned above, then mail script picks up the correct unique report for each respondent and sends mail.
My mail script is mentioned below:
function sendEmails() { // Sends non-duplicate emails with data from the current spreadsheet.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DataBank");
var Avals = sheet.getRange("A1:A").getValues(); // helps getting last fillled row^
var Alast = Avals.filter(String).length; // helps getting last fillled row^
var startRow = 2; // First row of data to process
var numRows = Alast-1; // Number of rows to process - last filled row^
var dataRange = sheet.getRange(startRow, 1, numRows, 91); // Fetch the range of cells A2:B3 //.getrange(row,column,numRows,numColumns) numColumns should equal to max column number where data process is required.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[1]; // second column, actual column minus one
var message = row[85]; // 85th column, actual column minus one
var emailSent = row[90]; // 90th column, actual column minus one
if (emailSent !== EMAIL_SENT) { // Prevents sending duplicates
var subject = row[84] //'Sending emails from a Spreadsheet';
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 91).setValue(EMAIL_SENT);
Utilities.sleep(120000); // keeps the script waiting untill the sheet gets updated values from "ArrayFormula"
}
SpreadsheetApp.flush(); // Make sure the cell is updated right away in case the script is interrupted
}
}
Can you review my mail script and help me improve it so that it picks up correct unique report?
I just solved it. declaring var data = dataRange.getValues(); within the for loop solved my problem. I found the solution just now Thank you all!.
by declaring the variable before the for loop, I was actually storing the static data and the same was used in each iterations. When I declared the variable within for loop, the data started changing at each iteration.
I was setting up an gooleappscript to automatically send mail if certain condition meets for a range o value in spreadsheet.I need to read values of multiple cells (A1 to A10) and check whether its less than 3 , if so then send a mail to mentioned mail id. I have done the script but its only reading value form the first cell and sends mail accordingly. My purpose is to read each selected cell values and then compare it . Any idea to correct it.
Google sheet
Below is the code am using.
function sendMail() {
if (SpreadsheetApp.openById('1bmAFWR7YsfMlgxExLKY4**********').getRange("A2:A10").getValue()<3) ;
MailApp.sendEmail("s*****#gmail.com", "*Subject*******", "Dear Sreenath, Your **** is getting expired in two days !");
}
i have tried getRange("A2:A10").getValue()<3 but its reading and comparing the value from A2 only.ie in this case only value 10 is read and compared.i need to read all values from A2 to A10 and check whether its less than 3. and if any one of it is less than 3 then need to trigger the send mail command.
Any help highly appreciated.
You'll need to fix a few things:
getValue() gets just one value. You want all the values in the range. So use getValues(). Ref doc here.
The values you receive are an array. You'll need to loop through the array and check each value.
Note that this
"Returns a two-dimensional array of values, indexed by row, then by
column"
You may also want to specify which sheet to get the values from.
So I would try something like this:
function sendMail() {
var ss = SpreadsheetApp.openById('1bmAFWR7YsfMlgxExLKY4**********'); // The spreadsheet
var sheet = ss.getSheetByName("The_Name_Of_Your_Sheet_Goes_Here"); // The sheet that has the data
var data = sheet.getRange("A2:A10").getValues(); // The values from the sheet
for (var i = 0; i < data.length; i++) { // Loop throught the values
if ( data[i][0] < 3 ) { // The array is a 2-d array. See ref doc in this post
MailApp.sendEmail("s*****#gmail.com", "*Subject*******", "Dear Sreenath, Your **** is getting expired in two days !"); // Send email
}
}
}
I have a large data file that I want to be separated/filtered out into separate tabs on a new sheet. They need to be filtered by a certain column containing "BGT" within the string.
I am very new to using Google Apps Script so this is a work in progress. I am able to pull the data from one sheet into another, convert it to an array and assign the column I want to sort by. I just can't properly set up the filter.
function CreativeReport() {
var ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = ss.getSheetByName(SHEET_NAME);
var folder = getDriveFolderFromPath("Daily Creative Audit");
var fileId = folder.getFiles().next().getId();
var RawMM = SpreadsheetApp.openById(fileId);
var ArrayRawData = RawMM.getRange(2,1,RawMM.getLastRow()-1,5).getValues();
var ConceptName = RawMM.getRange('A:A').getValues();
var BGTFilter = ConceptName.includes("BGT");
var BGTFilteredData = ArrayRawData.filter(function(item){ return item [6] === BGTFilter;});
Logger.log(BGTFilteredData);
The column I want to sort buy contains cells formatted like this
2019_BGT_Bigsale_300x50
2019_SWT_Bigsale_300x50
2019_AI_Bigsale_300x50
2019_SWO_Bigsale_300x50
2019_DCO_Bigsale_300x50.
The goal is to have the script filter this raw data, and sort it into separate sheets depending on the cells containing:
BGT
SWO
SWT
AI
DCO
The code I how SHOULD log out just the concepts containing "BGT" in the string but I get
TypeError: Cannot find function includes in object ,concept_name,
You have values at "Sheet1" in the source Spreadsheet.
You want to separate the values by checking whether the column "A" has the values of BGT, SWO, SWT, AI, DCO.
You want to put the separated values to the sheet names of BGT, SWO, SWT, AI, DCO in the destination Spreadsheet.
I could understand like above. If my understanding is correct, how about this modification? Unfortunately, the modified script was largely changed from your original script. I apologize for this. I think that there are several answers for your situation. So please think of this as just one of them.
The flow of the modified script is as follows.
Retrieve values from the source sheet.
Create an object for retrieving values for each destination sheet.
The object is like {"BGT": [values], "SWO": [values],,,}.
Put values for the destination sheets.
Modified script:
Before you run the script, please set srcSpreadsheetId, dstSpreadsheetId and srcSheetName.
function CreativeReport() {
// Variables: Please set them for your situation.
var srcSpreadsheetId = "###"; // Please set the source Spreadsheet ID. In your sample, the Spreadsheet name is "Sample Raw Data".
var dstSpreadsheetId = "###"; // Please set the destination Spreadsheet ID. In your sample, the Spreadsheet name is "Sorted Data".
var srcSheetName = "Sheet1"; // Please modify this. In this sample, it's the same with your sample Spreadsheet.
var dstSheetNames = ["BGT", "SWO", "DCO", "AI", "SWT"];
// Open Spreadsheets
var srcSS = SpreadsheetApp.openById(srcSpreadsheetId);
var dstSS = SpreadsheetApp.openById(dstSpreadsheetId);
// Retrieve values from the source sheet.
var srcSheet = srcSS.getSheetByName(srcSheetName);
var srcValues = srcSheet.getRange(2, 1, srcSheet.getLastRow() - 1, 5).getValues();
// Create an object for retrieving values for each destination sheet.
var object = srcValues.reduce(function(obj, e) {
dstSheetNames.forEach(function(f) {
if (e[0].indexOf(f) > -1) obj[f] = obj[f] ? obj[f].concat([e]) : [e];
});
return obj;
}, {});
// Put values for the destination sheets.
dstSheetNames.forEach(function(e) {
var sheet = dstSS.getSheetByName(e);
sheet.getRange(sheet.getLastRow() + 1, 1, object[e].length, object[e][0].length).setValues(object[e]);
});
}
Note:
As mentioned at my comment, the values retrieved by getValues() is 2 dimensional array. So if Array.includes() can be used at Google Apps Script, in your script, ConceptName.includes("BGT") is always false. And, unfortunately, Array.includes() cannot be used at Google Apps Script. Ref
This modified script was prepared using the sample Spreadsheets of "Sample Raw Data" and "Sorted Data" you shared. So if your actual Spreadsheet is different from these samples, please modify above script.
When the values of source Spreadsheet and the number of destination sheets are much increased, I recommend to use Sheets API for putting the values for each destination sheet.
References:
getValues()
Basic JavaScript features
includes()
indexOf()
reduce()
forEach()
I am trying to create a small invoicing organization system using google sheets/drive. I have one sheet I call "tasks", where I plan to control everything from. Some of my columns include, "Client", "Project", "Requirements", "Details", "subcontractor"... As I acquire new tasks/clients, i'd find and append information respective of the task ("Project", "Requirements") to other sheets or, if none exist, create the folders, sheets, and append the respective necessary information from the "tasks" sheet to the new sheets. Some of the sheets will be sent to subcontractors, dependent on whether or not their tasks were updated or new ones were assigned to them in the original "tasks" sheet.
Within the sheets I send to subcontractors, there will be fields for them to fill out (rate, eta..), once filled, I will send that info to a third sheet to apply some margins, extra fees, and then send the info back to the original "tasks" sheet where it will fill appropriate cells.. Once all of the necessary information in a row is filled, it will be prepared and organized into an invoice for the client specified in the "client" column...
Anyways, i've been trying to learn javascript to implement all of this. As I plan to create folders, sheets, and append information based on the values entered in the rows and columns of the "tasks" sheet... I've placed a for loop in an onEdit function that does the following:
function onEdit(e) {
var ss = e.range.getSheet().getParent();
var sheet = e.range.getSheet();
var row = e.range.getRow();
var columns = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//assign titles as 'keys' to array
var titles = []
//assign values of edited row to array
var values = []
//create an object to associate the title to the new edited values
var task = {}
for(var i in columns){
titles.push(sheet.getRange(1, columns[i]).getValue()); //push titles
values.push(sheet.getRange(row, columns[i]).getValue()); //push values of updated row
task[titles[i]] = values[i]; //add the values to their property names in task object
}
This works, and I can reference task["Client"], but i'd like to put this loop in a function so that I can use it again. I suppose I could do without it, but array "columns" only represents the columns I will be inputting on the "first round" --- when im sending information out...I will be inputting new information to columns 10-15, then 16-20, as the tasks progress.. and i'd like to run the for loop for those columns without having to create separate loops. To do this i've created the GetInfo function below:
function GetInfo(row,column){
for(var i in column){
titles.push(sheet.getRange(1, column[i]).getValue()); //push titles
values.push(sheet.getRange(row, column[i]).getValue()); //push values of updated row
this.task[titles[i]] = values[i]; //add the values to their task
}
}
What I am trying to accomplish is similar to what is outlined here. However, the "for(var..in..") is not mentioned in the examples and I think im missing something. In attempt to use the function for the first array of columns ive done this:
var list = new GetInfo(row,columns);
i'd like to reference the task as follows
list.task["client"]
Or var.task["name"], but the above doesn't work. When I toast list.task["Client] or try to append it to another cell, nothing happens - its blank. What am I doing wrong? How do I accomplish this correctly? What should I do?
Any help or guidance would be greatly appreciated. Please.
(other toasts are working, and the respective cell is not blank, without the function the for var in works)
I feel like I'm going about this in all the wrong way. I'm trying to automate some of my workload here. I'm cleaning up spreadsheets with 4 columns (A-E), 2000+ rows. Column B contains website URLs, column D contains the URL's business name, generated from another source.
Sometimes the tool doesn't grab the name correctly or the name is missing, so it populates the missing entries in column D with "------" (6 hyphens). I've been trying to make a function that takes an input cell, checks if the contents of the cell are "------", and if it is the function changes the contents of the input cell to the contents of the cell two columns to the left (which is generally a website url). This is what I've come up with.
function replaceMissing(input) {
var sheet = SpreadsheetApp.getActiveSheet();
//sets active range to the input cell
var cell = sheet.getRange('"' + input + '"');
//gets cell to fill input cell
var urlCell = sheet.getRange(cell.getRow(), cell.getColumn() - 2);
//gets contents of input cell as String
var data = cell.getValue();
//gets contents of urlCell as String
var data2 = cell.getValue();
//checks if input cell should be replaced
if (data === "------") {
//set current cell's value to the value of the cell 2 columns to the left
cell.setValue(data2);
}
}
When I attempt to use my function in my sheet, the cell is returning the error
Error Range not found (line 4).
I'm assuming, based on similar questions people have asked, that this is how you use the A1 notation of the function with an argument. However, that doesn't seem to be the case, so I'm stuck. I also don't think my solution is very good period.
1) It's somewhat ambiguous in GAS documentation, but custom functions have quite a few limitations. They are better suited for scenarios where you need to perform a simple calculation and return a string or a number type value to the cell. While custom functions can call some GAS services, this practice is strongly discouraged by Google.
If you check the docs for the list of supported services, you'll notice that they support only some 'get' methods for Spreadsheet service, but not 'set' methods https://developers.google.com/apps-script/guides/sheets/functions
That means you can't call cell.setValue() in the context of a custom function. It makes sense if you think about it - your spreadsheet can contain 1000s of rows, each with its own custom function making multiple calls to the server. In JavaScript, every function call creates its own execution context, so things could get ugly very quickly.
2) For better performance, use batch operations and don't alternate between read / write actions. Instead, read all the data you need for processing into variables and leave the spreadsheet alone. After processing your data, perform a single write action to update values in the target range. There's no need to go cell by cell when you can get the entire range using GAS.
Google Apps Script - best practices
https://developers.google.com/apps-script/guides/support/best-practices
Below is a quick code example that runs onOpen and onEdit. If you need more flexibility in terms of when to run the script, look into dynamically-created triggers https://developers.google.com/apps-script/reference/script/script-app
Because your spreadsheets have lots of rows, you may hit the execution quota anyway - by using triggers you can work around the limitation.
Finally, if a cell containing '----' is a rare occurrence, it might be better to create another array variable with new values and row numbers to update than updating the entire range.
Personally, I think the single range update action would still be quicker, but you could try both approaches and see which one works best.
function onOpen(){
test();
}
function onEdit() {
test();
}
function test() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('yourSheetName');
//range to replace values in
var range = sheet.getRange(2, 4, sheet.getLastRow() - 1, 1);
//range to get new values from
var lookupRange = range.offset(0, -2);
//2d array of values from the target range
var values = range.getValues();
//2d array of values from the source range
var lookupValues = lookupRange.getValues();
//looping through the values array and checking if array element meets our condition
for (var i=0; i < values.length; i++) {
values[i][0] = (values[i][0] == '------') ? lookupValues[i][0] : values[i][0];
}
// one method call to update the range
range.setValues(values);
}