I'm close, but my logic just isn't quite working. I am able to run the code below within Google Sheets Scripts to create a calendar event for each row in the sheet. I am trying to get it to only create an event when a new row is entered and to only read the spreadsheet until it comes to the first column/row being empty/null. enter image description here
Here is the associated code:
function createCalendarEvent() {
var sheet = SpreadsheetApp.getActiveSheet();
var calendar = CalendarApp.getCalendarById('dentaldigitaldesign.bkp#gmail.com');
var startRow = 100; // First row of data to process - 100 exempts my header row and adds to the case number count.
var numRows = sheet.getLastRow(); // Number of rows to process
var numColumns = sheet.getLastColumn();
var dataRange = sheet.getRange(startRow, 1, numRows-1, numColumns);
var data = dataRange.getValues();
var complete = "TRUE";
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var caseID = row[0]; //Case ID Number
var patient = row[4]; //Patient Name
var doctor = row[5]; //Doctor
var contact = row[14]; //Doctor contact info
var tooth = row[15]; //Tooth number
var shade = row[10]; //Tooth Shade
var notes = row[8]; //Notes
var callNotes = row[7]; //Call notes if there are any
var timeStamp = new Date (row[2]); //Retrieve the timestamp field
// year as 4 digits (YYYY)
var year = timeStamp.getFullYear();
// month as 2 digits (MM)
var month = ("0" + (timeStamp.getMonth() + 1)).slice(-2);
// date as 2 digits (DD)
var day = ("0" + timeStamp.getDate()).slice(-2);
var dueDate = new Date(month + day, + year); //Due date
var rDate = new Date(row[2]-1); //Remind date
var caseComplete = row[3]; //Case marked as complete
if (caseComplete !== complete && caseID !== null) {
var currentCell = sheet.getRange(startRow + i, numColumns);
calendar.createEvent(patient, rDate, rDate, {
description: doctor + '\r' + patient + '\r' + contact + '\r' + dueDate + '\r' + tooth + '\r' + shade + '\r' + notes + '\r' + callNotes
});
currentCell.setValue(complete);
}
}
}
When these events are created, they are all created for 8:59am. Ideally, I could do a check on the calendare if an event is set for that time, a new event is added immediately after. (perhaps in 15min or 30min slots). For now it works well for me to get the reminders on cases that are due, but eventually an invite to the doctor for them to know the due date might work well, too.
I can also use help in formatting the description field as it is not pushing the return value and everything is on one line.
And finally, the script continues to run on numerous fields beyond the scope of the desired rows, ultimately ending up with the script failing because too many event attempts are created in too short a time. (all the attempts with fields that are empty do not result in any events being created, but it is a resouce issue....and who knows maybe Google eventually blocks me?)
I appreciate any help that can be offered.
Here is the link to the Google Sheet. No data on it is sensitive as it is only test data: https://docs.google.com/spreadsheets/d/1M9qYzSl1PnRv-GehHEYvpiag15UI_GjnanA0wgA4xmg/edit?usp=sharing
There are several issues with your code, causing this expression to always return true, so that the script tries to create an event for each row:
if (caseComplete !== complete && caseID !== null) {
Empty string !== null:
You want to check whether the cell in column A is empty (that is, whether its value is an empty string), and to do that you are comparing its value to null. null is not the same as an empty string, so the following expression will always be true (a cell value never returns null):
caseID !== null
You should be comparing this to an empty string. For example, like this:
caseID !== ""
So the if statement should be:
if (caseComplete !== complete && caseID !== "") {
Wrong complete column:
Once an event has been created, you want to set the corresponding cell in D to TRUE. You're not doing that, since you specify the last column in the sheet (numColumns) when setting the currentCell:
var currentCell = sheet.getRange(startRow + i, numColumns);
Because of this, the value in D never changes to TRUE, so caseComplete !== complete is always true.
You should specify the correct column index for D instead, which is 4. It should be like this:
var currentCell = sheet.getRange(startRow + i, 4);
Edit: Also, the retrieved value for cell with checked checkboxes is the boolean true, and you're trying to compare it with the string TRUE. Because of this, this condition is not working correctly. Please change:
var complete = "TRUE";
To
var complete = true;
Wrong number of rows in source range:
You want to get the value from row 100 till the last one in the sheet. In this case, the total number of rows in the range should be the last row (numRows) minus the first row in the range (startRow) plus one (numRows - startRow + 1). The dataRange should be defined like this instead:
var dataRange = sheet.getRange(startRow, 1, numRows - startRow + 1, numColumns);
Reference:
Class Range
null
Related
I have a sheet that is dynamically updating with leads from my marketing campaign. Currently, it has over 9K rows and will continue to grow.
I need to check values for Col F for leads that came in today; however, when I run a loop, it takes too long since it has 9k+ rows.
I want to run a loop for rows that have today's date only. Is there a way to have my script efficiently look for today's date first (without having to loop through 9k rows), and then start my loop?
Below is what I had originally. The code would start from the top of the sheet and look at Col F values and then email me if a cell had only "az-" instead of a full location value. Content below has been modified for privacy
function Google_Check_Lead_Values() {
var title = "Google | Leads"
var app = SpreadsheetApp;
var Def_url = ("website")
///EDIT ^ URL
var ss = app.openByUrl(Def_url).getSheetByName("Google | Leads");
var last_row = ss.getLastRow();
for (i = 1;i <=last_row; i++) {
var location = ss.getRange(i,6).getValue();
var location_len = location.length;
if (location_len <= 3){
MailApp.sendEmail({
to: "email1",
//cc: "email2",
subject: "Discrepancy found on " + title,
htmlBody:"Hi Team, <br>This is an automated notifcation to let you know that an issue was found on " + title + ". Please take a look at the sheet linked below: <br><br> email"
});//end of email
}//end of if
}//end of for
}//end of function
Try it like this:
function Google_Check_Lead_Values() {
const title = "Google | Leads"
const ss = SpreadsheetApp.openByUrl("url")
const sh = SpreadsheetApp.openByUrl(Def_url).getSheetByName(title);
const dt = new Date();
const dtv = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()).valueOf();
const vs = sh.getDataRange().getValues();
vs.forEach(r => {
let loc = r[5].toString();
let d = new Date(r[0]);
let dv = new Date(d.getFullYear(),d.getMonth(),d.getDate()).valueOf();
if( dv == dtv && loc.length < 4) {
MailApp.sendEmail({ to: "email1", subject: "Discrepancy found on " + title, htmlBody: "Hi Team, <br>This is an automated notifcation to let you know that an issue was found on " + title + ". Please take a look at the sheet linked below: <br><br> email", })
}
});
}
I believe your goal is as follows.
You want to reduce the process cost of the search of value from a large sheet.
You want to achieve this using Google Apps Script.
In your situation, by searching the values from the columns "A" and "F", you want to send emails.
In order to search the values from a sheet, there are several methods. Using for loop, TextFinder and query language. In my test, when the query language is used, the process cost was the lowest of these 3 patterns. So, in this answer, I would like to propose to search the values using use the query language. Ref
Sample script:
function Google_Check_Lead_Values() {
var title = "Google | Leads"
var Def_url = "###"; // Please use your Spreadsheet URL.
var ss = SpreadsheetApp.openByUrl(Def_url);
const spreadsheetId = ss.getId();
const today = new Date();
const tommorow = new Date();
tommorow.setDate(tommorow.getDate() + 1);
const vToday = Utilities.formatDate(today, Session.getScriptTimeZone(), "yyyy-MM-dd");
const vTommorow = Utilities.formatDate(tommorow, Session.getScriptTimeZone(), "yyyy-MM-dd");
const sheetName = "Google | Leads";
const query = `SELECT * WHERE A >= DATE '${vToday}' AND A < date '${vTommorow}' AND E = 'az-'`;
const url = `https://docs.google.com/spreadsheets/d/${spreadsheetId}/gviz/tq?sheet=${sheetName}&tqx=out:csv&tq=${query}`;
const res = UrlFetchApp.fetch(encodeURI(url), { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } });
const [, ...values] = Utilities.parseCsv(res.getContentText());
if (values.length == 0) return;
values.forEach(_ => {
MailApp.sendEmail({
to: "email1",
//cc: "email2",
subject: "Discrepancy found on " + title,
htmlBody: "Hi Team, <br>This is an automated notifcation to let you know that an issue was found on " + title + ". Please take a look at the sheet linked below: <br><br> email"
});
});
}
When this script is run, the today date values are searched from the column "A", and also it searches whether the cell value of column "F" is the value of az-. When the values are found, the emaiil is sent. In this case, from your showing script, for example, when 2 rows are found, 2 emails are sent.
If you know the Spreadsheet ID, you can also directly use it to const spreadsheetId = ss.getId(); like const spreadsheetId = "###SpreadsheetID###";.
In this sample script, about the search of the column "F", it seaches whether the value is az-. But, if you want to search the length of cell value, please modify as follows.
From
const query = `SELECT * WHERE A >= DATE '${vToday}' AND A < date '${vTommorow}' AND E = 'az-'`;
To
const query = `SELECT * WHERE A >= DATE '${vToday}' AND A < date '${vTommorow}' AND E MATCHES '^.{0,3}$'`;
References:
Query Language Reference (Version 0.7)
Related thread.
https://stackoverflow.com/a/56663884
What I'm looking for:
I am working on a spreadsheet that contains transaction information for multiple people. Some people have only 1 transaction (1 row of information) while others may have multiple transactions (rows of information). So far, the code I am working on is able send an email containing one transaction per email. However, I am having trouble figuring out how to group multiple transactions into one email. One of the column fields is "Email address" and ideally I could use that to group multiple rows. Pretty much a mail merge that can be grouped by a column in the spread sheet or by the identification of similar values.
What I've tried/what I'm thinking:
I already have the code sort by email address so that the transactions for each person are consecutive on the sheet. I am thinking that I need to loop with an if statement saying something along the lines "if the next value in the 'email address' field is the same as the previous one, add a new line in the body of the email." From what I have researched I might need to do a loop within a loop for the email body but I am not completely sure how to go about that. In the code I have, I have already somewhat formatted the email the way it should be.
Disclaimer:
I'm pretty new to coding so any help is appreciated. I am unsure how to loop in the body of the message and eventually exit once the script hits a different person. I'd like to do it all in GAS.
This is what I've got so far:
var SENT = "Y";
function sendEmails() {
//This section specifies the sheet and some definitions we are working with
var sheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var lastrow = sheet.getLastRow();
var startrow = 4; // First row of data to process
var numrows = lastrow; // Number of rows to process
sheet.sort(14, true); //Sorts the sheet by email address
//This section formats the columns so they appear correctly in the html email below
var column = sheet.getRange("K4:L");
column.setNumberFormat("MM/dd/yyyy"); // Simple date format
var column = sheet.getRange("M:M");
column.setNumberFormat("#"); // Simple plain text format
var column = sheet.getRange("K4:L");
column.setNumberFormat("#"); // Simple plain text format
var column = sheet.getRange("E:E");
column.setNumberFormat("$0.00"); // Simple currency format
//This section specifies the actual data we will be working with
var datarange = sheet.getRange(startrow, 1, lastrow, 15)// Fetch the range of cells
var data = datarange.getValues();// Fetch values for each row in the Range.
//Defining column data
for (var i = 0; i < data.length; ++i) {
var col = data[i];
var firstname = col[2]; // Column starting at 0 from left to right
var expamount = col[4]; // Column starting at 0 from left to right
var subdate = col[10]; // Column starting at 0 from left to right
var trandate = col[11]; // Column starting at 0 from left to right
var reportname = col[12]; // Column starting at 0 from left to right
var emailaddress = col[13]; // Column starting at 0 from left to right
var emailsent = col[14]; //Column starting at 0 from left to right
var subject = "MinistryCard Expenses Over 90 Days"; // Subject for the email to be sent
var emailintro = // Introduction part of the email
'Hi ' + firstname + ', <br /><br />' +
'This is the introduction to the email.' +
'The related expenses are below: <br /><br/ >'
var emailtrans = //THIS IS WHERE I WOULD LIKE THE MAGIC TO HAPPEN... I THINK
'<strong>Report Name: </strong>' + reportname + ' <br /><strong>Transaction Date: </strong>' + trandate + ' <br /><strong>Transaction Amount: </strong>' + expamount +
' <br /><strong>Approval Date: </strong>' + subdate + '<br /><br />'
var emailend = // The endd of the email
'We apologize for any inconvenience this may cause. ' +
'Also, please contact us if you believe you are recieving this email in error. Thank you. <br /><br /><br />' +
'Enjoy your day, <br /><br />'
//The section below retrieves alias email address to send as
var me = Session.getActiveUser().getEmail();// Log the aliases for this Gmail account and send an email as the first one.
var aliases = GmailApp.getAliases(); // Gets the alias ministrycard#cru.org from account
Logger.log(aliases); // Logs the alias
// The section below sends the actual emails
if (emailaddress != "" && emailsent != SENT){
if (aliases.length > 0){ // Prevents sending duplicates
GmailApp.sendEmail(emailaddress, subject , emailintro + emailtrans + emailend, {'from': aliases[0],'replyto': 'ministrycard#cru.org', htmlBody : emailintro + emailtrans + emailend});
sheet.getRange(startrow + i, 15).setValue(SENT);
SpreadsheetApp.flush(); // Make sure the cell is updated right away in case the script is interrupted
}} else { }}}
There are multiple solutions. Here's mine:
declare a nextEmail variable before the main for loop begins.
var nextEmail = '';
at the beginning of the loop, set nextEmail to the email in the next row:
if (i < data.length - 1) {
nextEmail = data[i+1][13]//looks at next row, 13th column
} else {//catch if i is the maximum row
nextEmail = ''
}
add another conditional that checks that the next email address is not the same before sending the email
if (emailaddress != "" && emailsent != SENT && nextEmail != emailaddress){
You didn't seem to specify what parts of the message you want to change each time and what parts you want to stay the same, but I'm assuming you want to change only emailtrans.
now, instead of declaing a new variable emailtrans each time it loops around, declare emailtrans before the loop as an empty string...
var emailtrans = ''
...and instead append the big string to emailtrans
emailtrans +=
'<strong>Report Name: </strong>' + reportname + ' <br /><strong>Transaction Date: </strong>' + trandate + ' <br /><strong>Transaction Amount: </strong>' + expamount +
' <br /><strong>Approval Date: </strong>' + subdate + '<br /><br />';
and then set emailtrans to an empty string right after you send the email, inside of the if statement.
emailtrans = ''
There are a few things you didn't ask about but I still think should be addressed. You probably would have caught a lot of this if only you tested whatever you've got, even if it's incomplete, to make sure it did what you want before asking for help.
SENT variable should probably be declared within the function (good practice)
numrows is only equal to lastrow if startrow is zero. Set numrows to lastrow - startrow and use numrows instead of lastrow when you declare datarange.
You sort the whole sheet based on column M, but your script only collect data from row 4 onward. Depending on your labels in rows 1-3, the sorting might place some emails in rows 1-3 and your labels in row 4 and onward. Instead, I'm guessing you only want to sort the range containing the emails, so do a .sort() on that range instead of the whole sheet.
Putting in semicolons where you define emailintro, emailtrans, and emailend in multiple lines is probably a good idea.
I could be wrong, but it seems the html tags aren't formatted right. <br /><br /> wouldn't insert a break ... they would just end two breaks?
Declaring a variable inside a for loop is bad practice. Declare it outside the loop beforehand, and set it (thing = value), not declare it (var thing = value), inside the loop.
Hope this helps.
My script gets data from a CSV file and then exports it to a Google Sheet. the CSV file consists of a 2D array and has 9000 + rows. My current script runs fine but give an error of
Incorrect Range Width, was 1 but should be 5
This error only occurs if I process the whole CSV file, whereas if I separate the file into chunks it processes fine without any errors using the same code. Therefore the error is with the amount of rows. I use a loop feature it times out.
I was wondering if there is a way to run the code to process 2000 rows quit and then start again without any interruptions. I have been stuck on this error for several weeks and really need help. Thank you
Here is my code:
Function getCSV() {
var fSource = DriveApp.getFolderById('0B2lVvlNIDosoajRRMUwySVBPNVE'); //reports_folder_id = id of folder where csv reports are saved
var date= Utilities.formatDate(new Date(), "GMT", "dd-MM-yy");
var fi = fSource.getFilesByName('L661_BOM-CAD_07-01-16.csv');
// latest report file
var ss = SpreadsheetApp.openById('1V8YG8lyNZiTllEPHENcnabYRLDPCK6mHGUyAyNhW0Is').getSheet s()[0]; // data_sheet_id = id of spreadsheet that holds the data to be updated with new report data Sheet will be opened server side.
ss.getName() == "Sheet1"
if ( fi.hasNext()) { // proceed if "report.csv" file exists in the reports folder
var file = fi.next();
//file.setName('L661_BOM-CAD_'+ date +'(EXPORTED)'+'.csv');
var csv = file.getBlob().getDataAsString();
var csvData = CSVToArray(csv);
Logger.log('csvData[0].length: ' + csvData[0].length + ' csvData.length:' + csvData.length);
var lastrow = ss.getLastRow();
ss.getRange(lastrow + 1,1,csvData.length,csvData[0].length).setValues((csvData));
}
//adds the last modified date to the first row
if( ss.getName() == "Sheet1" ) { //checks that we're on the correct sheet
var r= ss.getRange('A1');
if( r.getColumn() == 1 ) { //checks the column
var nextCell = r.offset(0, 5);
if( nextCell.getValue() === '' ) //is empty?
var date = new Date();
var date= Utilities.formatDate(new Date(), "GMT", "dd-MM-yy");
nextCell.setValue(date); //enters the date in F1 in dd/mm/yyyy format
};
};
};
function CSVToArray( strData, strDelimiter ){
strDelimiter = (strDelimiter || ';');
var objPattern = new RegExp(
(
// Delimiters.
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
// Quoted fields.
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
// Standard fields.
"([^\"\\" + strDelimiter + "\\r\\n]*))"
),
"gi"
);
var arrData = [[]];
var arrMatches = null;
while (arrMatches = objPattern.exec( strData )){
// Get the delimiter that was found.
var strMatchedDelimiter = arrMatches[ 1 ];
// Check to see if the given delimiter has a length
// (is not the start of string) and if it matches
// field delimiter. If id does not, then we know
// that this delimiter is a row delimiter.
if (
strMatchedDelimiter.length &&
strMatchedDelimiter !== strDelimiter
){
// Since we have reached a new row of data,
// add an empty row to our data array.
arrData.push( [] );
}
var strMatchedValue;
// Now that we have our delimiter out of the way,
// let's check to see which kind of value we
// captured (quoted or unquoted).
if (arrMatches[ 2 ]){
// We found a quoted value. When we capture
// this value, unescape any double quotes.
strMatchedValue = arrMatches[ 2 ].replace(
new RegExp( "\"\"", "g" ),
"\""
);
} else {
// We found a non-quoted value.
strMatchedValue = arrMatches[ 3 ];
}
// Now that we have our value string, let's add
// it to the data array.
arrData[ arrData.length - 1 ].push( strMatchedValue );
Logger.log('arrData[0].length: ' + arrData[0].length);
}
// Return the parsed data.
return( arrData );
}
I believe your delimiter is a semicolon.
Here's how to import and write to the currently open sheet
function getcsv(){
// get the data from drive
var csvString = DriveApp
.getFileById('0B92ExLh4POiZTHFDUThmaHE2d2s1LVpvbWhyNjJaOE1MejBZ')
.getBlob()
.getDataAsString();
// convert to array of arrays
var csvData = Utilities.parseCsv(csvString, ';');
// write to sheet
SpreadsheetApp
.getActiveSheet()
.getRange(1,1,csvData.length, csvData[0].length)
.setValues(csvData);
}
I can'f find the loop that goes over the rows in your code, but essentially you could do something like that (you need a var rowcount = 0 the value of which you increase with every row):
if (rowcount == 2000) {
console.log("found row 2000");
}
Then set a breakpoint on the console.log().
Firefox developer tools
Setting breakpoints in Firefox
I'm new to using the DocumentApp in Google Apps, so looking for some help!
I'm trying to create an FAQ (in a Google Doc) automatically from a spreadsheet. If certain conditions are met in the spreadsheet row, I want the script to find the category of the question in the document and insert a new question and response underneath it (pushing anything already there back).
So I'd have a document that looks like this:
https://docs.google.com/document/d/1fjb3RO6hUY6n7x0bu6WvtcleMWRNC8VQ9U82hXiGqcY/edit?usp=sharing
And a spreadsheet that looks like this:
https://docs.google.com/spreadsheets/d/1fb3ceqP6142_C7QQ1PfkWtVNswOyzOxkWCofvauf4Ps/edit?usp=sharing
And this is the code I'm trying to use. I'm getting a ton of errors- mainly because I don't have a good grasp on what the different elements involved are. Can anyone point me in the right direction?
function insertnewquestion() {
//(works fine)Get active document and related spreadsheet
var doc = DocumentApp.getActiveDocument().getBody();
var ss = SpreadsheetApp.openById("xxxxxxx");
var sheet = ss.getSheetByName("xxxxx");
//(works fine)Go through the various rows in the spreadsheet up to the last row
for (var row = 2; row <= sheet.getLastRow(); ++row) {
var status = sheet.getRange(row,4).getValue();
var question = sheet.getRange(row,1).getValue();
var response = sheet.getRange(row,2).getValue();
var category = sheet.getRange(row,3).getValue();
var date = sheet.getRange(row,5).getValue();
//(works fine)find rows with blank status and a response filled in
if (status !== "Entered" && response !== "") {
//(errors! this is where i need help) find the pertinent header
var categoryposition = body.findText(category); //looking for the category header- new text should be added after this, the idea is that what was already under this header will move down
var questionheader = categoryposition.appendText(question); //trying here to add in question text after where the category was found
questionheader.setHeading(DocumentApp.ParagraphHeading.HEADING3); //set question text as heading 3 (so it shows up on table of contents)
questionheader.appendText("\r"+"\r"+response+"\r\r"); //add line breaks and then the response text in normal font, then more line breaks to put space between new stuff and old stuff
//(works fine)Mark in the spreadsheet that it was entered in FAQ and the date so that it isn't double entered next time the script runs
sheet.getRange(row,4).setValue("Entered");
var currentTime = new Date()
var month = currentTime.getMonth() + 1
var day = currentTime.getDate()
var year = currentTime.getFullYear()
var date = (month + "/" + day + "/" + year)
sheet.getRange(row,5).setValue(date);
}
else {continue}
}
//(need help!) Refresh table of contents to include new questions
//no idea how to do this!
}
In the code you are referring to body.findText(category); where it should be doc.findText(category);. Also for the line:
var categoryposition = body.findText(category);
it returns the RangeElement — a search result indicating the position of the search text, or null if there is no match
Before adding any lines of code you have to check for null value in categoryposition.
The text class has a method to insert text in particular offset value to particular String value gives as shown here.
Hope that is helpful.
I currently receive emails from a specific source with multiple attachments specifically csv attachments. What I need is to retrieve the data from the csv attachment. I've looked into creating a Google App Script which I hear can get the job done based on my research but perhaps there is a better way if so please advice.
I wrote a pseudo code of what I would like the script to do:
Determine who sent the email. If its the source that I need then follow to step 2.
Look at the subject of the email if its the subject that I need then proceed to step 3.
If step 1 and 2 are good then the next step is to retrieve the data from one of the csv attachments(based on the name) this is because there could be more than one attachment in the email.
Open the attachment copy the data and paste it in either a google spreadsheet or excel spreadsheet which is created dynamically OR save the attachment to my google drive in a specific folder but either one could work. The trick here is to loop through all the emails in my inbox in past month and achieve the above task.
Thanks everyone for your help and I hope I was clear in my specifications.
Links I found to be helpful to me but not quite exactly what I need.
Create time-based Gmail filters with Google Apps Script
Trigger Google Apps Script by email
After researching and working along with the google apps script documentation I was able achieve my goal for my task at hand. Please see my code below with comments and hopefully this can help.
Thanks,
function RetrieveAttachment() {
// variables being used i, j, k, n, m, a, d, x
var threads = GmailApp.search('*SubjectName*') //search gmail with the given query(partial name using * as a wildcard to find anything in the current subject name).
var msgs = GmailApp.getMessagesForThreads(threads); //retrieve all messages in the specified threads.
//var sheet = SpreadsheetApp.create('test_filename', 2, 8); //creates a new spreadsheet in case I need to create it on a separate file.
//you can get the id from your own google spreadsheet in your browser bar.
var sheet = SpreadsheetApp.openById('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx').getSheetByName('your sheet name');
sheet.clearContents(); //clears all the data in the specified tab, the code below will recreate the dataset once again.
for (var i = 0; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var emailDate = msgs[i][j].getDate();
var attachments = msgs[i][j].getAttachments();
for (var k = 0; k < attachments.length; k++) {
/*search for the attachment by name, stringLen returns the start position number of the word 'filename' ignoring any previous characters, counting starts at 0.
e.g. "test_filename", will output the number 6 "test_" will ends at 5 and 6 will start at "f" for filename. Than we use substring to get the actual name out of
attachment name then we use the stringLen variable as a starting position and also as an end position plus the number of characters in word I'm searching for
to get the attachment name, 8 is used because this is how many letters are in the string. Finally we create the stringValue variable and compare to see which
attachments meet the criteria that we are looking for and return only that attachment.*/
var attachmentName = attachments[k].getName();
var stringLen = attachmentName.search('filename');
var stringValue = attachmentName.substring(stringLen,stringLen + 8);
if (stringValue == 'filename') {
var attachmentData = attachments[k].getDataAsString();
var attachmentClean = attachmentData.split('"').join(',');
var attachmentCleanA = attachmentClean.split(',');
/*input headings into the spreadsheet. This is depending on how many columns or fields the attachment file has. The numbers after the "attachmentCleanA" is the
position number of the field you want is located i.e [1][4][7][10]. The reason it skips the numbers is because the getDataAsString() method returned an array with
multiple separations that had to be parsed by quotations. So [2][3] had an empty string value.*/
sheet.getRange('A1').setValue(attachmentCleanA[1]); //Field One
sheet.getRange('B1').setValue(attachmentCleanA[4]); //Field Two
sheet.getRange('C1').setValue(attachmentCleanA[7]); //Field Three
sheet.getRange('D1').setValue(attachmentCleanA[10]); //Field Four
//Extra fields if you want to add.
sheet.getRange('E1').setValue('Email Date'); //Email Date
sheet.getRange('F1').setValue('Email Month'); //Email Month
sheet.getRange('G1').setValue('Email Year'); //Email Year
sheet.getRange('H1').setValue('Source Name'); //Attachment Name
var n = LastRow(sheet); //calls the LastRow function to get the next empty cell.
var m = attachmentCleanA.length + n;
/*attachmentCleanA.length alone is not useful as a limit in the loop because the n variable ends up being bigger than the actual attachmentCleanA.length.
To fix this I added the "attachmentCleanA" + "n" variable so that the n variable will always be less than the attachmentCleanA.length expression.*/
var range = sheet.getRange('A1:H30000'); //this has to match the number of columns in the above sheet.getRange().setValue methods.
var d = 11;
/*now we loop through each string in the array and place it in each individual row and column. The first string position you want may vary depending
on the file you have. The file I have has the first item and is positioned in the 12th position of the array. The reason variable d shows 11 is because
it will be added before the actual extraction of the value "d++" */
RowLoop:
for (var x = n; x < m; x++) {
for (var a = 1; a < 5; a++) {
var cell = range.getCell(x, a);
d++;
//the reason of the if function is so that when I run into an empty string in the array I can simply ignore it and continue to the next string.
if (attachmentCleanA[d] !== "" && attachmentCleanA[d] !== undefined) {
cell.setValue(attachmentCleanA[d]);
}
else if (attachmentCleanA[d] == "") {
/*the a-- is used so that when I find and empty string in the array I don't want to skip to the next column but continue to stay there until I find
a none empty string.*/
a--;
}
}
/*email date - the reason of the if function is because in my situation it was producing more values at the end of the loop. So I made it stop if in
column A doesn't have a value*/
var setDate = range.getCell(x, 5);
if (range.getCell(x, 1).getValue() !== "") {
setDate.setValue(emailDate);
}
else if (range.getCell(x, 1).getValue() == "") {
break RowLoop;
}
//source name
var attachmentLen = attachmentName.search('filename');
var attachmentValue = attachmentName.substring(0, attachmentLen-1);
var setAttachmentName = range.getCell(x, 8);
setAttachmentName.setValue(attachmentValue);
//email year
var setYear = range.getCell(x, 7);
setYear.setValue(emailDate.getFullYear());
//email month
var setMonth = range.getCell(x, 6);
var monthName = MonthFunc(emailDate.getMonth());
setMonth.setValue(monthName);
}
}
}
}
}
}
function LastRow(sheetName) {
//retrieve the last row position after each attachment data file has been put into the spreadsheet
var column = sheetName.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] !== "" ) {
ct++;
}
return (ct)+1; // add 1 to get the row which is empty
}
function MonthFunc(inputMonth) {
//this function returns the short name of the month.
var monthNumber = inputMonth
switch (monthNumber) {
case 0:
return "Jan";
break;
case 1:
return "Feb";
break;
case 2:
return "Mar";
break;
case 3:
return "Apr";
break;
case 4:
return "May";
break;
case 5:
return "Jun";
break;
case 6:
return "Jul";
break;
case 7:
return "Aug";
break;
case 8:
return "Sep";
break;
case 9:
return "Oct";
break;
case 10:
return "Nov";
break;
case 11:
return "Dec";
break;
}
}