Google apps script running slow - google-apps-script

I have a homemade script for saving attachments to Drive from Gmail. I'm looping through the attachments and Logging the time when each attachment is saved. I've noticed some weird discrepancies between some saves. As the following image shows. Sometimes there is a minute and a half between loops. Each file is a pdf and they are all roughly the same size, e.g. 100 kb. each. It's not unusual that this script processes 50 attachments from a single email.
My for loop is as follows:
for (var k in attachments) {
var attachment = attachments[k];
var isDefinedType = checkIfDefinedType_(attachment);
if (!isDefinedType) continue;
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
if (parentFolder.addFile(file)) {
root.removeFile(file);
writeToSheet(file, message, msgId, k, totalattachments)
}
}
Below are the relevant functions.
function checkIfDefinedType_(attachment) {
var fileName = attachment.getName();
var temp = fileName.split('.');
var fileExtension = temp[temp.length - 1].toLowerCase();
if (fileTypesToExtract.indexOf(fileExtension) !== -1) return true;
else return false;}
function writeToSheet(file, message, msgId, k, totalattachments) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var fileId = file.getId();
var fileUrl = file.getUrl();
var formattedDate = Utilities.formatDate(new Date(), "GMT", "dd-MM-yyyy HH:mm:ss");
var emailSubject = message.getSubject();
var emailFrom = message.getFrom();
var sheet = ss.getSheetByName('Log');
sheet.appendRow([msgId, formattedDate, emailFrom, emailSubject, k, totalattachments, file, fileId, fileUrl]);}
Is there something wrong with my code?

Well turns out, saving a file to the root, then copying it to another directory and then delete the original file takes longer than just put it in the correct directory to begin with.
I changed this:
var file = DriveApp.createFile(attachmentBlob);
if (parentFolder.addFile(file)) {
root.removeFile(file);
}
to this:
var file = parentFolder.createFile(attachmentBlob);
Although I'm pretty sure this method wasn't always possible. Maybe I'm mistaken.

Related

Adding Replace Existing File to GmailToDrive App Script - Follow up Question

This is an additional question off of the related post: Adding Replace Existing File to GmailToDrive App Script answered by #Tanaike.
I'm trying to use the code from the previous post with the Edit function with an xlsx file. Everything seems to be working fine where it grabs the file and saves it and it even updated the modified date in the google drive folder. However, the content of the excel file does not change from the original. I've enabled the Drive API. Is this compatible with xlsx file types? Is there something additional needed to actually update the content in the file and not just the metadata?
// GLOBALS
//Array of file extension which you would like to extract to Drive
var fileTypesToExtract = ['xlsx'];
//Name of the folder in google drive i which files will be put
var folderName = 'YourfolderName;
//Name of the label which will be applied after processing the mail message
var labelName = 'Saved2';
function GmailToDrive(){
//build query to search emails
var query = '';
//filename:jpg OR filename:tif OR filename:gif OR fileName:png OR filename:bmp OR filename:svg'; //'after:'+formattedDate+
for(var i in fileTypesToExtract){
query += (query === '' ?('filename:'+fileTypesToExtract[i]) : (' OR filename:'+fileTypesToExtract[i]));
}
query = 'in:inbox label:MyCustomLabel has:attachment ' + query;
var threads = GmailApp.search(query);
var label = getGmailLabel_(labelName);
var parentFolder;
if(threads.length > 0){
parentFolder = getFolder_(folderName);
}
var root = DriveApp.getRootFolder();
for(var i in threads){
var mesgs = threads[i].getMessages();
for(var j in mesgs){
//get attachments
var attachments = mesgs[j].getAttachments();
for(var k in attachments){
var attachment = attachments[k];
var isDefinedType = checkIfDefinedType_(attachment);
if(!isDefinedType) continue;
var attachmentBlob = attachment.copyBlob();
var existingFile = DriveApp.getFilesByName(attachment.getName());
if (existingFile.hasNext()) {
var file = existingFile.next();
Drive.Files.update({}, file.getId(), attachmentBlob);
} else { // Added
var file = DriveApp.createFile(attachmentBlob); // Added
parentFolder.addFile(file); // Added
root.removeFile(file); // Added
}
}
}
threads[i].addLabel(label);
}
}
//This function will get the parent folder in Google drive
function getFolder_(folderName){
var folder;
var fi = DriveApp.getFoldersByName(folderName);
if(fi.hasNext()){
folder = fi.next();
}
else{
folder = DriveApp.createFolder(folderName);
}
return folder;
}
//getDate n days back
// n must be integer
function getDateNDaysBack_(n){
n = parseInt(8);
var date = new Date();
date.setDate(date.getDate() - n);
return Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy/MM/dd');
}
function getGmailLabel_(name){
var label = GmailApp.getUserLabelByName(name);
if(!label){
label = GmailApp.createLabel(name);
}
return label;
}
//this function will check for filextension type.
// and return boolean
function checkIfDefinedType_(attachment){
var fileName = attachment.getName();
var temp = fileName.split('.');
var fileExtension = temp[temp.length-1].toLowerCase();
if(fileTypesToExtract.indexOf(fileExtension) !== -1) return true;
else return false;
}
Because .xlsx is the Excel format, Apps Script can't modify its content because it's not a Google product, it's like trying to modify a pdf using Apps Script, but don't worry, you could convert the .xlsx file into the native Google Spreadsheet format and then handle it like a normal Google Spreadsheet. This function will do the conversion for you:
// this Function will convert your .xlsx files into native Google Spreadsheet format
function convertExceltoGoogleSpreadsheet(parentFolder) {
var files = parentFolder.getFiles();
while(files.hasNext()){
var file = files.next();
// If the file contains the .xlsx extention in its name
// then it will return an array with length 2
var filename = file.getName().split(/.xlsx?/);
if(filename.length > 1){
// build info to create the new file with Google Spreadsheet format
var resource = {
title: filename[0],
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{id: parentFolder.getId()}],
};
// Return the response after creating the file
return Drive.Files.insert(resource, file.getBlob());
}
}
}
Then, use that function in this part of your code:
...
var attachmentBlob = attachment.copyBlob();
var existingFile = DriveApp.getFilesByName(attachment.getName());
if (existingFile.hasNext()) {
var file = existingFile.next();
Drive.Files.update({}, file.getId(), attachmentBlob);
} else {
var file = DriveApp.createFile(attachmentBlob)
parentFolder.addFile(file);
root.removeFile(file);
}
// Let's change the format and insert a "Hello world" message
// ----START----
var spreadSheetResponse = convertExceltoGoogleSpreadsheet(parentFolder);
var spreadSheetId = spreadSheetResponse.getId()
var sheet = SpreadsheetApp.openById(spreadSheetId).getSheets()[0];
sheet.getRange(1, 1).setValue("Hello world");
// ----END----
...
Notice
In my code I left the .xlsx file in the Drive, if you want you could delete it after converting it iinto the native Google Spreadsheet format
Docs
This is what I used to help you:
SpreadsheetApp
Drive - Class File

How to loop data and print each iteration to PDF in a defined Google Drive folder

I'm a novice at GAS, so please bear with me.
I'd like to create a script for a Google Sheet that will loop through a series of values, pause at each value, and print a specific tab from the sheet to a defined location in Google Drive (creating a new folder with a date within the parent folder). Below is what I have so far, which achieves looping and printing, but I can't figure out how to get it to save the PDF files to a specific folder. Grateful for any help! Thank you.
Link to dummy spreadsheet with script here, and target Google Drive folder here.
function loop() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var validation_sheet = ss.getSheetByName('Validation');
var lastRow = validation_sheet.getLastRow();
var inputs = ss.getSheetByName('Validation').getRange('A2:A'+lastRow).getValues();
var sheet2 = ss.getSheetByName('Loadout');
var tab = ss.getSheetByName('Loadout');
var formattedDate = Utilities.formatDate(new Date(), "GMT+5", "yyyy-MM-dd hh:mm");
//Create Folder for PDFs
var fld = DriveApp.createFolder(formattedDate);
fld.addFile(DriveApp.getFileById(ss.getId()));
for (var i = 0; i < inputs.length; i++) {
sheet2.getRange('A1').setValue(inputs[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(5000);
//Print PDFs
var name = tab.getRange(1, 1).getValue();
fld.createFile(ss.getAs('application/pdf')).setName(name);
}
DriveApp.getRootFolder().removeFile(DriveApp.getFileById(ss.getId()));
}
You want to create PDF files to "target Google Drive folder". https://drive.google.com/drive/u/1/folders/1QmjXLyssyCGU16ApLQ4Anj_kir0uuQS8
If my understanding is correct, how about this modification?
Modification points:
Retrieve the target folder using DriveApp.getFolderById().
When PDF file is created, it creates the file to the target folder.
Modified script:
function loop() {
var targetFolderId = "1QmjXLyssyCGU16ApLQ4Anj_kir0uuQS8"; // Added
var targetFolder = DriveApp.getFolderById(targetFolderId); // Added
var ss = SpreadsheetApp.getActiveSpreadsheet();
var validation_sheet = ss.getSheetByName('Validation');
var lastRow = validation_sheet.getLastRow();
var inputs = ss.getSheetByName('Validation').getRange('A2:A'+lastRow).getValues();
var sheet2 = ss.getSheetByName('Loadout');
var tab = ss.getSheetByName('Loadout');
// var formattedDate = Utilities.formatDate(new Date(), "GMT+5", "yyyy-MM-dd hh:mm"); // Removed
//Create Folder for PDFs
// var fld = DriveApp.createFolder(formattedDate); // Removed
// fld.addFile(DriveApp.getFileById(ss.getId())); // Removed
for (var i = 0; i < inputs.length; i++) {
sheet2.getRange('A1').setValue(inputs[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(5000);
//Print PDFs
var name = tab.getRange(1, 1).getValue();
targetFolder.createFile(ss.getAs('application/pdf')).setName(name); // Modified
}
// DriveApp.getRootFolder().removeFile(DriveApp.getFileById(ss.getId())); // Removed
}
Note:
In your script, the active spreadsheet is also moved to a created new folder. About this, how do you want to do?
References:
getFolderById(id)
If I misunderstand your question, please tell me. I would like to modify it.
Edit:
When you run the script, at first, you want to create new folder in the target folder. Then, you want to create PDF files in the created folder.
About the active spreadsheet, do nothing.
I understood like above. If my understanding is correct, how about this modification?
Modified script:
function loop() {
var targetFolderId = "1QmjXLyssyCGU16ApLQ4Anj_kir0uuQS8"; // Added
var targetFolder = DriveApp.getFolderById(targetFolderId); // Added
var ss = SpreadsheetApp.getActiveSpreadsheet();
var validation_sheet = ss.getSheetByName('Validation');
var lastRow = validation_sheet.getLastRow();
var inputs = ss.getSheetByName('Validation').getRange('A2:A'+lastRow).getValues();
var sheet2 = ss.getSheetByName('Loadout');
var tab = ss.getSheetByName('Loadout');
var formattedDate = Utilities.formatDate(new Date(), "GMT+5", "yyyy-MM-dd hh:mm");
//Create Folder for PDFs
var fld = targetFolder.createFolder(formattedDate); // Modified
// fld.addFile(DriveApp.getFileById(ss.getId())); // Removed
for (var i = 0; i < inputs.length; i++) {
sheet2.getRange('A1').setValue(inputs[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(5000);
//Print PDFs
var name = tab.getRange(1, 1).getValue();
fld.createFile(ss.getAs('application/pdf')).setName(name); // Modified
}
// DriveApp.getRootFolder().removeFile(DriveApp.getFileById(ss.getId())); // Removed
}

Using google script to import .csv files into an existing google sheet. Formatting issues.

Is there a way to not duplicate the headers when the new .csv files are imported from my one drive folder into the existing google sheet? I want the .csv files to be added to the existing sheet in sequential order without adding the headers...example - like row 2 and 19 show. Also, sequential to make the dates go in order in column A. Another question I had was, do you know what happened in line 10? I have deleted and re entered the new data in and every time that happens. This is my script I have now. This is a shareable link to the sheet and what it looks like. https://docs.google.com/spreadsheets/d/1f9HEwikMxm5sJzzRh_-etBxXzL0NpK47i9LtoZVCv_0/edit?usp=sharing This is the script I have right now.
function appendingCSV() {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var sht=ss.getActiveSheet();
var drng = sht.getDataRange();
var lastRow = drng.getLastRow();
var data = loadFiles();
var dataA =Utilities.parseCsv(data);
if(dataA.length>0)
{
var rng = sht.getRange(lastRow + 1, 1, dataA.length, dataA[0].length);
rng.setValues(dataA);
}
else
{
SpreadsheetApp.getUi().alert('No Data Returned from LoadFiles');
}
}
function loadFiles(folderID)
{
var folderID = (typeof(folderID) !== 'undefined')? folderID :
'0B8m9xkDP_TJxUUlueHhXOWJMbjg';
var fldr = DriveApp.getFolderById(folderID);
var files = fldr.getFiles();
var s='';
var re = /^.*\.csv$/i;
while (files.hasNext())
{
var file = files.next();
var filename = file.getName();
if(filename.match(re))
{
s += file.getBlob().getDataAsString() + '\n';
file.setName(filename.slice(0,-3) + 'old');
}
}
return s;
}
function createTimeDrivenTriggers() {
// Trigger every Friday at 09:00.
ScriptApp.newTrigger('myFunction')
.timeBased()
.onWeekDay(ScriptApp.WeekDay.FRIDAY)
.atHour(9)
.create();
}
In your loadFiles() script. Try changing it to something like this.
function loadFiles(folderID)
{
//var folderID = (typeof(folderID) !== 'undefined')? folderID : 'Your_folder_id';
var folderID = (typeof(folderID) !== 'undefined')? folderID : 'Your_folder_id';
var fldr = DriveApp.getFolderById(folderID);
var files = fldr.getFiles();
var s='';
var re = /^.*\.csv$/i;
while (files.hasNext())
{
var file = files.next();
var filename = file.getName();
if(filename.match(re))
{
s+=file.getBlob().getDataAsString().split('\n').splice(0,1).join('\n') + '\n';
//s += file.getBlob().getDataAsString() + '\n';
file.setName(filename.slice(0,-3) + 'old');
}
}
return s;
}
You may have to play with this a little. I'm not sure if the last '\n' is needed or not and I'm not that great at chaining so many operations. But you need to remove the headers from each file. You could write a local script that you give to your techs that strips off the headers at the origin and in that case then go back to the way it is now.

How to write google script to automatically call data from a google folder drive that contains a CSV file into one Google Sheets

I am trying to write a google script that will allow me to go into my google drive folder called "MeadJohsnon" and pull 'Temperature Calibration.csv' to google sheets. I have never used google script before. Currently I have the "Save Email and Attachment" Add-ons. This add-on is pulling .cvs files that my team is sending me from the field. They use "TapForms" and then send the form via email to my gmail. So, I got the email sending that attachment to my google drive but I need help with the script, so Drive will automatically get those .cvs files and put the information into one google sheet. I say ONE google sheet because although I have a team sending in forms, all the forms have the same information on them.
This is what I have done so far. The fourth line gives me a
function loadingCSV() {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var sht=ss.getActiveSheet();
sht.clearContents();
var data = loadFile();
var dataA =Utilities.parseCsv(data);
var rng = sht.getRange(1, 1, dataA.length, dataA[0].length);
rng.setValues(dataA);
}
I would just like feedback on how to fix my error or what I could do instead. As stated this is my first time using google script, my specialty is ASP.net lol not script. Thank you.
function loadingCSV() {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var sht=ss.getActiveSheet();
sht.clearContents();
var data = loadFile();
var dataA =Utilities.parseCsv(data);
var rng = sht.getRange(1, 1, dataA.length, dataA[0].length);
rng.setValues(dataA);
}
function loadFile(filename,folderID)
{
var filename = (typeof(filename) !== 'undefined')? filename : 'Temperature Calibration.csv';
var folderID = (typeof(folderID) !== 'undefined')? folderID : '0B8m9xkDP_TJxUUlueHhXOWJMbjg';
var fldr = DriveApp.getFolderById(folderID);
var file = fldr.getFilesByName(filename);
var s = '';
while(file.hasNext())
{
var fi = file.next();
var target = fi.getName();
if(target == filename)
{
s = fi.getBlob().getDataAsString();
}
}
return s;
}
Okay this will append the files to the active spreadsheet you'll probably have to open the spreadsheet by id and use getSheetByName to get the sheet you want because this spreadsheet probably won't be active all the time when the trigger is running. I assume the the files all end in .csv. I rename them to .old after reading the data so that the program won't read them multiple times.
function appendingCSV() {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var sht=ss.getActiveSheet();
var drng = sht.getDataRange();
var lastRow = drng.getLastRow();
var data = loadFiles();
var dataA =Utilities.parseCsv(data);
if(dataA.length>0)
{
var rng = sht.getRange(lastRow + 1, 1, dataA.length, dataA[0].length);
rng.setValues(dataA);
}
else
{
SpreadsheetApp.getUi().alert('No Data Returned from LoadFiles');
}
}
function loadFiles(folderID)
{
var folderID = (typeof(folderID) !== 'undefined')? folderID : '0B8m9xkDP_TJxUUlueHhXOWJMbjg';
var fldr = DriveApp.getFolderById(folderID);
var files = fldr.getFiles();
var s='';
var re = /^.*\.csv$/i;
while (files.hasNext())
{
var file = files.next();
var filename = file.getName();
if(filename.match(re))
{
s += file.getBlob().getDataAsString() + '\n';
file.setName(filename.slice(0,-3) + 'old');
}
}
return s;
}

How to permanently delete Mails within scripts via MailApp?

I have found the following script in this forum and works really smoothly (takes files, saves files in the right folder, etc.). Namely I receive a txt. file every morning with relative quantities which I would like to import and save to my Gdrive (to later use in other scripts). The file is named with todays date which I have used the Utilities.formatDate function to obtain.
Unfortunately every time I run the script I get all files from even from previous days which would spam my folder full in a couple of days. Therefore my question is whether one can delete the emails after the script that my google search query will not find the previous emails the following day?
Any help with this conundrum appreciated!!
P.S: Email comes from the same recipient, with an identical subject "Stock_Load", however the attachment is named "date"_Todays Date.
// GLOBALS
//Array of file extension which you would like to extract to Drive
var fileTypesToExtract = ['txt'];
//Name of the folder in google drive i which files will be put
var folderName = 'GmailToDrive';
//Name of the label which will be applied after processing the mail message
var labelName = 'GmailToDrive';
function GmailToDrive(){
//build query to search emails
var query = 'filename:stock_'+Utilities.formatDate(new Date(), "GMT", "yyyyMMdd").toString()+'.gpg.txt';
//filename:jpg OR filename:tif OR filename:gif OR fileName:png OR filename:bmp OR filename:svg'; //'after:'+formattedDate+
for(var i in fileTypesToExtract){
query += (query == '' ?('filename:'+fileTypesToExtract[i]) : (' OR filename:'+fileTypesToExtract[i]));
}
query = 'in:inbox has:nouserlabels is:unread' + query;
var threads = GmailApp.search(query);
var label = getGmailLabel_(labelName);
var Folder = DriveApp.getFolderById("XXX");
for(var i in threads){
var mesgs = threads[i].getMessages();
for(var j in mesgs){
//get attachments
var attachments = mesgs[j].getAttachments();
for(var k in attachments){
var attachment = attachments[k];
var isImageType = checkIfImage_(attachment);
if(!isImageType) continue;
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
Folder.addFile(file);
}
}
threads[i].addLabel(label);
}
};
//This function will get the parent folder in Google drive
function getFolder_(folderName){
var folder;
try {folder = DriveApp.getFolder(folderName)}
catch(e){ folder = DriveApp.createFolder(folderName);}
return folder;
}
//getDate n days back
// n must be integer
function getDateNDaysBack_(n){
n = parseInt(n);
var today = new Date();
var dateNDaysBack = new Date(today.valueOf() - n*24*60*60*1000);
return dateNDaysBack;
}
function getGmailLabel_(name){
var label = GmailApp.getUserLabelByName(name);
if(label == null){
label = GmailApp.createLabel(name);
}
return label;
}
//this function will check for filextension type.
// and return boolean
function checkIfImage_(attachment){
var fileName = attachment.getName();
var temp = fileName.split('.');
var fileExtension = temp[temp.length-1].toLowerCase();
if(fileTypesToExtract.indexOf(fileExtension) != -1) return true;
else return false;
}