I currently have a GoogleSheet, with 2 GoogleApp Scripts that work more or less how I want them too, but I could use a little help in refining them to get them working perfectly.
I have 1 script that scans through my email, looks for a mail's subject and then pulls it to my Google Drive if it matches.
Script 1: Pull from Gmail to GDrive
function importData() {
var fSource = DriveApp.getFolderById('0B3h9TQiHV_rjR04xTlctb2s2Qms'); // reports_folder_id = id of folder where csv reports are saved
var fi = fSource.getFilesByName('CR Logs this week.csv'); // latest report file
var ss = SpreadsheetApp.openById('1OuEXVjZzPwfdcEW8eIOtNAeS4HJeTn_rw8w5o5Ja-Fo'); // data_sheet_id = id of spreadsheet that holds the data to be updated with new report data
if ( fi.hasNext() ) { // proceed if "CR Logs this week.csv" file exists in the reports folder
var file = fi.next();
var csv = file.getBlob().getDataAsString();
var csvData = CSVToArray(csv); // see below for CSVToArray function
var newsheet = ss.insertSheet('NEWDATA'); // create a 'NEWDATA' sheet to store imported data
// loop through csv data array and insert (append) as rows into 'NEWDATA' sheet
for ( var i=0, lenCsv=csvData.length; i<lenCsv; i++ ) {
newsheet.getRange(i+1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
}
/*
** report data is now in 'NEWDATA' sheet in the spreadsheet - process it as needed,
** then delete 'NEWDATA' sheet using ss.deleteSheet(newsheet)
*/
// rename the CR Logs this week.csv file so it is not processed on next scheduled run
file.setName("CR Logs this week.csv-"+(new Date().toString())+".csv");
}
};
// http://www.bennadel.com/blog/1504-Ask-Ben-Parsing-CSV-Strings-With-Javascript-Exec-Regular-Expression-Command.htm
// This will parse a delimited string into an array of
// arrays. The default delimiter is the comma, but this
// can be overriden in the second argument.
function CSVToArray( strData, strDelimiter ) {
// Check to see if the delimiter is defined. If not,
// then default to COMMA.
strDelimiter = (strDelimiter || ",");
// Create a regular expression to parse the CSV values.
var objPattern = new RegExp(
(
// Delimiters.
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
// Quoted fields.
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
// Standard fields.
"([^\"\\" + strDelimiter + "\\r\\n]*))"
),
"gi"
);
// Create an array to hold our data. Give the array
// a default empty first row.
var arrData = [[]];
// Create an array to hold our individual pattern
// matching groups.
var arrMatches = null;
// Keep looping over the regular expression matches
// until we can no longer find a match.
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( [] );
}
// 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.
var strMatchedValue = arrMatches[ 2 ].replace(
new RegExp( "\"\"", "g" ),
"\""
);
} else {
// We found a non-quoted value.
var strMatchedValue = arrMatches[ 3 ];
}
// Now that we have our value string, let's add
// it to the data array.
arrData[ arrData.length - 1 ].push( strMatchedValue );
}
// Return the parsed data.
return( arrData );
};
The second script then looks through my Google Drive for a matching file name, then imports it to the Google Sheet.
Script 2: Import from GDrive to GSheet
// GLOBALS
//File extension
var fileTypesToExtract = ['csv'];
//Name of the GDrive folder it will be placed
var folderName = 'Reports';
//Name of the label which will be applied after processing the mail message
var labelName = 'ReportToDrive';
function GmailToDrive(){
//query to search mails
var query = 'CR Logs this week';
//filename:csv; //'after:'+formattedDate+
for(var i in fileTypesToExtract){
query += (query == '' ?('filename:'+fileTypesToExtract[i]) : (' OR filename:'+fileTypesToExtract[i]));
}
query = 'in:inbox has:nouserlabels ' + query;
var threads = GmailApp.search(query);
var label = getGmailLabel_(labelName);
var parentFolder;
if(threads.length > 0){
parentFolder = getFolder_(folderName);
}
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 isFileType = checkIfCSV_(attachment);
if(!isFileType) continue;
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
parentFolder.addFile(file);
}
}
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(n);
var today = new Date();
var dateNDaysBack = new Date(today.valueOf() - n*2);
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 checkIfCSV_(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;
}
The Goal
I have a piece of software that produces reports in a CSV format, these reports are emailed to me every night at midnight. What I would like to happen is this:
Script 1 pulls CSV from email and imports it to GDrive
Script 2 to import the data to the Google Sheet
Repeat the above on the next night, replacing (and discarding) the previous imported data
Currently, it pulls from my GMail into my GDrive ok. But I can't get it to replace the old data / delete the old data and import the new data.
Would be grand if someone with more experience could help me out!
So, I managed to figure this out by myself in the end.
//function CopyData() {
var source = SpreadsheetApp.openById('xxx');
var sourcesheet = source.getSheetByName('NEWDATA');
var target = SpreadsheetApp.openById('xxx')
var targetsheet = target.getSheetByName('Data');
var targetrange = targetsheet.getRange(2, 1, sourcesheet.getLastRow(), sourcesheet.getLastColumn());
var rangeValues = sourcesheet.getRange(2, 1, sourcesheet.getLastRow(), sourcesheet.getLastColumn()).getValues();
targetrange.setValues(rangeValues);
ss.deleteSheet(newsheet)
This copies the data from NEWDATA, the sheet the Script 2 above creates, and then duplicates it to the sheet Data, replacing whatever is already there.It then deletes the NEWDATA sheet, so that the script can run on the next night.
Related
I've pieced together this code from various google searches that will pull an e-mail's CSV attachment if the e-mail has a specific label.
function importCSVFromGmail() {
//gets first(latest) message with set label
var threads = GmailApp.getUserLabelByName('Dashboard Updates').getThreads(0,1);
var message = threads[0].getMessages()[0];
var attachment = message.getAttachments()[0];
// Is the attachment a CSV file
if (attachment.getContentType() === "text/csv") {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("Monthly_Detail_Instantis");
//parses content of csv to array
var dataString = attachment.getDataAsString();
var escapedString = dataString.replace(/(?=["'])(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]\r\n(?:\\[\s\S][^'\\]\r\n)*')/g, '\\r\\n');
var csvData = Utilities.parseCsv(escapedString);
// Remember to clear the content of the sheet before importing new data
sh.clearContents().clearFormats();
//pastes array to sheet
sh.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
//marks the Gmail message as read and unstars it (Filter sets a star)
message.markRead();
message.unstar();
}
The script runs fine, however I am running into issues with cells that had values with commas or quotes. For example, if a cell has the following:
1,000,000
or
Google "Apps" Script
It will return to following, respectively.
\r\n
\r\n\r\n\r\n
I'm certain it has to do with the Regex used, however I am not certain how to adjust for the above. Any help with this would be greatly appreciated.
I was able to use the original code (used in question) and replace the escaping and Utilities.parseCsv with code from this link. This will properly import CSVs even if cells include quotes and commas:
https://productforums.google.com/forum/#!topic/docs/nhXjrl8JIek
Here is my final code:
function importCSVFromGmail() {
//gets first(latest) message with set label
var threads = GmailApp.getUserLabelByName('Dashboard Updates').getThreads(0,1);
var message = threads[0].getMessages()[0];
var attachment = message.getAttachments()[0];
// Is the attachment a CSV file
if (attachment.getContentType() === "text/csv") {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("Monthly_Detail_Instantis");
//parses content of csv to array
var dataString = attachment.getDataAsString();
var csvData = CSVToArray(dataString);
// Remember to clear the content of the sheet before importing new data
sh.clearContents().clearFormats();
//pastes array to sheet
var lastRowValue = sh.getLastRow();
for (var i = 0; i < csvData.length; i++) {
sh.getRange(i+lastRowValue+1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
}
}
//marks the Gmail message as read and unstars it (Filter sets a star)
message.markRead();
message.unstar();
}
//The code formats the code so it can be entered into the Google Script
function CSVToArray( strData, strDelimiter ){
// Check to see if the delimiter is defined. If not,
// then default to comma.
strDelimiter = (strDelimiter || ",");
// Create a regular expression to parse the CSV values.
var objPattern = new RegExp(
(
// Delimiters.
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
// Quoted fields.
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
// Standard fields.
"([^\"\\" + strDelimiter + "\\r\\n]*))"
),
"gi"
);
// Create an array to hold our data. Give the array
// a default empty first row.
var arrData = [[]];
// Create an array to hold our individual pattern
// matching groups.
var arrMatches = null;
// Keep looping over the regular expression matches
// until we can no longer find a match.
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( [] );
}
// 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.
var strMatchedValue = arrMatches[ 2 ].replace(
new RegExp( "\"\"", "g" ),
"\""
);
} else {
// We found a non-quoted value.
var strMatchedValue = arrMatches[ 3 ];
}
// Now that we have our value string, let's add
// it to the data array.
arrData[ arrData.length - 1 ].push( strMatchedValue );
}
// Return the parsed data.
return( arrData );
}
I want to import 3 CSV attachments from a single GMAIL email.
Attachment names are as follows:
- Sales.csv
- Labor.csv
- ServerPerformance.csv
I believe it can be done some how using the following:
var title = attachment.getName();
if (attachment.getContentType() === "text/csv" && title === "Sales.csv") {
but could use some help with proper execution.
Below is the script I'm currently using to import based off of GMAIL Labels and CSV attachment name but it is only importing the first attachment (Sales.csv), so I'd like to have 1 label "South Loop" with 3 separate attachments to be imported from a single email to google sheets.
function SLSalesImportFromGmail() {
//gets first(latest) message with set label
var threads = GmailApp.getUserLabelByName('South Loop').getThreads(0,1);
if (threads && threads.length>0) {
var message = threads[0].getMessages()[0];
var attachment = message.getAttachments()[0];
var title = attachment.getName();
// Is the attachment a CSV file
attachment.setContentTypeFromExtension();
if (attachment.getContentType() === "text/csv" && title === "Sales.csv") {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("South Loop Sales");
//parses content of csv to array
var dataString = attachment.getDataAsString();
var csvData = CSVToArray(dataString);
// Remember to clear the content of the sheet before importing new data
sh.clearContents().clearFormats();
//pastes array to sheet
var lastRowValue = sh.getLastRow();
for (var i = 0; i < csvData.length; i++) {
sh.getRange(i+lastRowValue+1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
}
if( message.getSubject().indexOf('END OF DAY SALES DETAILS') !== -1) {
SLlogTodaysSales();
}
if (attachment.getContentType() === "text/csv" && title === "Labor.csv") {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("South Loop Labor");
//parses content of csv to array
var dataString = attachment.getDataAsString();
var csvData = CSVToArray(dataString);
var range = sh.getRange("A:K");
// Remember to clear the content of the sheet before importing new data
range.clear();
//pastes array to sheet
var lastRowValue = sh.getLastRow();
for (var i = 0; i < csvData.length; i++) {
sh.getRange(i+1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
}
if( message.getSubject().indexOf('END OF WEEK LABOR DETAILS') !== -1) {
SLlogWeeksLabor();
}
if (attachment.getContentType() === "text/csv" && title === "ServerPerformance.csv") {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("South Loop Server Report");
//parses content of csv to array
var dataString = attachment.getDataAsString();
var csvData = CSVToArray(dataString);
var range = sh.getRange("A:M");
// Remember to clear the content of the sheet before importing new data
range.clear();
//pastes array to sheet
var lastRowValue = sh.getLastRow();
for (var i = 0; i < csvData.length; i++) {
sh.getRange(i+1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
}
if( message.getSubject().indexOf('END OF DAY') !== -1) {
SLlogTodaysServers()
}
}
}
}
//marks the Gmail message as read, unstars it and deletes it using Gmail API (Filter sets a star)
message.markRead();
message.unstar();
Gmail.Users.Messages.remove("me", message.getId()); // Added
}
}
//The code formats the code so it can be entered into the Google Script
function CSVToArray( strData, strDelimiter ){
// Check to see if the delimiter is defined. If not,
// then default to comma.
strDelimiter = (strDelimiter || ",");
// Create a regular expression to parse the CSV values.
var objPattern = new RegExp(
(
// Delimiters.
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
// Quoted fields.
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
// Standard fields.
"([^\"\\" + strDelimiter + "\\r\\n]*))"
),
"gi"
);
// Create an array to hold our data. Give the array
// a default empty first row.
var arrData = [[]];
// Create an array to hold our individual pattern
// matching groups.
var arrMatches = null;
// Keep looping over the regular expression matches
// until we can no longer find a match.
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( [] );
}
// 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.
var strMatchedValue = arrMatches[ 2 ].replace(
new RegExp( "\"\"", "g" ),
"\""
);
} else {
// We found a non-quoted value.
var strMatchedValue = arrMatches[ 3 ];
}
// Now that we have our value string, let's add
// it to the data array.
arrData[ arrData.length - 1 ].push( strMatchedValue );
}
// Return the parsed data.
return( arrData );
var label = GmailApp.getUserLabelByName("South Loop");
label.deleteLabel();
}
function SLlogTodaysSales() {
var todaysSales = SpreadsheetApp.getActive().getRange('South Loop Sales Log!SLSalesImport');
var logSheet = todaysSales.getSheet();
logSheet.appendRow(
todaysSales.getValues()
.reduce(function(a, b) { return a.concat(b); }) // flatten the 2D array to 1D
);
}
function SLlogWeeksLabor() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('South Loop Labor Log');
var rg=sh.getRange('SLLaborImport');
var vA=rg.getValues();
sh.getRange(sh.getLastRow()+1,1,vA.length,vA[0].length).setValues(vA);
}
function SLlogTodaysServers() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('South Loop Server Log');
var rg=sh.getRange('SLServerReport');
var vA=rg.getValues();
sh.getRange(sh.getLastRow()+1,1,vA.length,vA[0].length).setValues(vA);
}
The issue you have is that you're only looking at the first attachment because of this statement var attachment = message.getAttachments()[0];. Instead, just drop the [0] and loop through all of your attachments.
In an effort to make this clearer, I got rid of a bunch of your code for this example. Please review my notes after, as well.
function SLSalesImportFromGmail() {
var ss = SpreadsheetApp.getActive(); // Get the spreadsheet file once
//gets first(latest) message with set label
var threads = GmailApp.getUserLabelByName('South Loop').getThreads(0,1);
if (threads && threads.length > 0) {
var message = threads[0].getMessages()[0];
// Get all of the attachments and loop through them.
var attachments = message.getAttachments();
for (var i = 0; i < attachments.length; i++) {
var attachment = attachments[i];
var title = attachment.getName();
// Is the attachment a CSV file
attachment.setContentTypeFromExtension();
var table = Utilities.parseCsv(attachment.getDataAsString());
if (attachment.getContentType() === "text/csv") {
// Update the specified sheets
// Clears the sheet of values & formatting and inserts the new table
// using the Apps Script built-in CSV parser.
switch (title) {
case "Sales.csv":
ss.getSheetByName("South Loop Sales").getRange("A:M").clear();
ss.getSheetByName("South Loop Sales").getRange(1, 1, table.length, table[0].length).setValues(table);
break;
case "Labor.csv":
ss.getSheetByName("South Loop Labor").getRange("A:M").clear();
ss.getSheetByName("South Loop Labor").getRange(1, 1, table.length, table[0].length).setValues(table);
break;
case "ServerPerformance.csv":
ss.getSheetByName("South Loop Servers").getRange("A:M").clear();
ss.getSheetByName("South Loop Servers").getRange(1, 1, table.length, table[0].length).setValues(table);
break;
}
}
}
if (message.getSubject().indexOf('END OF DAY') !== -1) {
SLlogTodaysSales();
SLlogTodaysServers();
}
if (message.getSubject().indexOf('END OF WEEK') !== -1) {
SLlogTodaysSales();
SLlogTodaysServers();
SLlogWeeksLabor();
}
// Marks the Gmail message as read, unstars it and deletes it using Gmail API (Filter sets a star)
message.markRead();
message.unstar();
Gmail.Users.Messages.remove("me", message.getId()); // Added
}
}
Notes:
You're using a custom CSVToArray() function to convert the CSV file into an array, but Google already provides a method for this with Utilities.parseCsv(). It should suffice in most cases.
There is no need to call both clearContents() and clearFormats(), because clear() does both in a single call.
I'm aware that you have additional logic (e.g. "END OF DAY" emails & slightly different rules for Labor & ServerPerformance). As mentioned before, I removed all of that to demonstrate how to loop through the attachments. You can use an if or switch statement to include that logic in your script, instead of necessarily using the updateSheet() closure that I wrote.
So looking at your code I don't see you iterating through the attachments.
You only are getting the first attachment and then analyzing that without caring for the others.
var attachment = message.getAttachments()[0];
So instead of doing this, take all the attachments with the getAttachments() and make a for loop to analyze every one of them.
var attachments = message.getAttachments();
for(var i=0; i < attachments.length; i++){
var attachment = attachments[i];
//....
// The rest of your code needs to go inside this for loop
}
I've pieced together this code from various google searches that will pull an e-mail's CSV attachment if the e-mail has a specific label.
function importCSVFromGmail() {
//gets first(latest) message with set label
var threads = GmailApp.getUserLabelByName('Dashboard Updates').getThreads(0,1);
var message = threads[0].getMessages()[0];
var attachment = message.getAttachments()[0];
// Is the attachment a CSV file
if (attachment.getContentType() === "text/csv") {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("Monthly_Detail_Instantis");
//parses content of csv to array
var dataString = attachment.getDataAsString();
var escapedString = dataString.replace(/(?=["'])(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]\r\n(?:\\[\s\S][^'\\]\r\n)*')/g, '\\r\\n');
var csvData = Utilities.parseCsv(escapedString);
// Remember to clear the content of the sheet before importing new data
sh.clearContents().clearFormats();
//pastes array to sheet
sh.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
//marks the Gmail message as read and unstars it (Filter sets a star)
message.markRead();
message.unstar();
}
The script runs fine, however I am running into issues with cells that had values with commas or quotes. For example, if a cell has the following:
1,000,000
or
Google "Apps" Script
It will return to following, respectively.
\r\n
\r\n\r\n\r\n
I'm certain it has to do with the Regex used, however I am not certain how to adjust for the above. Any help with this would be greatly appreciated.
I was able to use the original code (used in question) and replace the escaping and Utilities.parseCsv with code from this link. This will properly import CSVs even if cells include quotes and commas:
https://productforums.google.com/forum/#!topic/docs/nhXjrl8JIek
Here is my final code:
function importCSVFromGmail() {
//gets first(latest) message with set label
var threads = GmailApp.getUserLabelByName('Dashboard Updates').getThreads(0,1);
var message = threads[0].getMessages()[0];
var attachment = message.getAttachments()[0];
// Is the attachment a CSV file
if (attachment.getContentType() === "text/csv") {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("Monthly_Detail_Instantis");
//parses content of csv to array
var dataString = attachment.getDataAsString();
var csvData = CSVToArray(dataString);
// Remember to clear the content of the sheet before importing new data
sh.clearContents().clearFormats();
//pastes array to sheet
var lastRowValue = sh.getLastRow();
for (var i = 0; i < csvData.length; i++) {
sh.getRange(i+lastRowValue+1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
}
}
//marks the Gmail message as read and unstars it (Filter sets a star)
message.markRead();
message.unstar();
}
//The code formats the code so it can be entered into the Google Script
function CSVToArray( strData, strDelimiter ){
// Check to see if the delimiter is defined. If not,
// then default to comma.
strDelimiter = (strDelimiter || ",");
// Create a regular expression to parse the CSV values.
var objPattern = new RegExp(
(
// Delimiters.
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
// Quoted fields.
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
// Standard fields.
"([^\"\\" + strDelimiter + "\\r\\n]*))"
),
"gi"
);
// Create an array to hold our data. Give the array
// a default empty first row.
var arrData = [[]];
// Create an array to hold our individual pattern
// matching groups.
var arrMatches = null;
// Keep looping over the regular expression matches
// until we can no longer find a match.
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( [] );
}
// 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.
var strMatchedValue = arrMatches[ 2 ].replace(
new RegExp( "\"\"", "g" ),
"\""
);
} else {
// We found a non-quoted value.
var strMatchedValue = arrMatches[ 3 ];
}
// Now that we have our value string, let's add
// it to the data array.
arrData[ arrData.length - 1 ].push( strMatchedValue );
}
// Return the parsed data.
return( arrData );
}
First, this code does exactly what I need it to do with one issue, if there is too much data for it to move, Google times out and will end the process. In full honestly/transparency, I'm a total script-code noob, so I might be missing something really simple. From what I can tell, this codes stacks data row by row, and if there are too many rows, this is where my breakdown comes in. Is there a why to just select all data, and paste all data? Thanks for your help!
function importDataCDOP() {
var fSource = DriveApp.getFolderById('FILE PATH');
var fi = fSource.getFilesByName('xxxx.csv');
var ss = SpreadsheetApp.openById('FILE PATH');
if ( fi.hasNext() ) {
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var first = ss.getSheetByName('NEWDATA');
var file = fi.next();
var csv = file.getBlob().getDataAsString();
var csvData = CSVToArray(csv);
var newsheet = ss.getSheetByName('NEWDATA');
var destSheet = ss.getSheetByName('NEWDATA');
var lastRow = destSheet.getLastRow();
destSheet.insertRowAfter(lastRow);
for ( var i=0, lenCsv=csvData.length; i<lenCsv; i++ ) {
destSheet.getRange(lastRow + i + 1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
}
file.setName("OP report-"+(new Date().toString())+".csv");
}
};
function CSVToArray( strData, strDelimiter ) {
strDelimiter = (strDelimiter || ",");
var objPattern = new RegExp(
(
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
"([^\"\\" + strDelimiter + "\\r\\n]*))"
),
"gi"
);
var arrData = [[]];
var arrMatches = null;
while (arrMatches = objPattern.exec( strData )){
var strMatchedDelimiter = arrMatches[ 1 ];
if (
strMatchedDelimiter.length &&
(strMatchedDelimiter != strDelimiter)
){
arrData.push( [] );
}
if (arrMatches[ 2 ]){
var strMatchedValue = arrMatches[ 2 ].replace(
new RegExp( "\"\"", "g" ),
"\""
);
} else {
var strMatchedValue = arrMatches[ 3 ];
}
arrData[ arrData.length - 1 ].push( strMatchedValue );
}
return( arrData );
};
From your question and comment, I could understand that you want to import the csv data from a file of "xxxx.csv" to the sheet of "NEWDATA" on the spreadsheet of "FILE PATH", and when the large csv data is imported, the elapsed time is over the time limitation of Google Apps Script. I understood that this was your issue. For this situation, how about this modification? Although I'm not sure whether this modification can directly solve your issue, please try to run this.
Modification points :
Retrieve a file object using getFilesByName('xxxx.csv').next(), because the csv file is only one.
var first = ss.getSheetByName('NEWDATA'); and var newsheet = ss.getSheetByName('NEWDATA'); are not used in your script.
Removed them.
In your script, the 2 dimensional array of csv data is imported by every element.
I thought that this may be the main cause of your issue.
In the modified script, import the 2 dimensional array at once.
Modified script :
I modified only importDataCDOP().
function importDataCDOP() {
var fSource = DriveApp.getFolderById('FILE PATH');
var file = fSource.getFilesByName('xxxx.csv').next();
var ss = SpreadsheetApp.openById('FILE PATH');
var csv = file.getBlob().getDataAsString();
var csvData = CSVToArray(csv).filter(String);
var destSheet = ss.getSheetByName('NEWDATA');
destSheet.getRange(destSheet.getLastRow() + 1, 1, csvData.length, csvData[0].length).setValues(csvData);
file.setName("OP report-"+(new Date().toString())+".csv");
};
Note :
I didn't modify CSVToArray().
filter(String) remove the elements with only the line break.
If this modified script was over the limitation of execution time, I would like to propose to use Sheets API.
If I misunderstand your question and the elapsed time of modified script was over the limitation, I'm sorry.
I'm using Google Apps Script to import data from a CSV file where the datas are in a single column. I'm following this tutorial to read datas from CSV file but since the datas are in single column, comma delimiter is not working and the code is hanging whenever I run the function.
Here is my code:
function importCSV(getfile) {
getfile = "Copy of FR1_1.csv";
var getFolder = DriveApp.getFolderById(fId);
var fi = getFolder.getFilesByName(getfile);
if (fi.hasNext()) {
var ssNew = SpreadsheetApp.getActiveSpreadsheet();
var newSheet = ssNew.getSheetByName("Sheet1");
var file = fi.next();
var csv = file.getBlob().getDataAsString();
var csvData = CSVToArray(csv); // see below for CSVToArray function
// loop through csv data array and insert (append) as rows into the sheet
for (var i = 0; i < csvData.length; i++) {
newSheet.getRange(newSheet.getLastRow()+1, 1, csvData.length).setValues(new Array(csvData[i][0]));
}
Browser.msgBox("CSV imported successfully!");
}
}
function CSVToArray(strData, strDelimiter) {
// Check to see if the delimiter is defined. If not,
// then default to COMMA.
strDelimiter = (strDelimiter || ",");
// Create a regular expression to parse the CSV values.
var objPattern = new RegExp(
(
// Delimiters.
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
// Quoted fields.
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
// Standard fields.
"([^\"\\" + strDelimiter + "\\r\\n]*))"
),
"gi"
);
// Create an array to hold our data. Give the array
// a default empty first row.
var arrData = [
[]
];
// Create an array to hold our individual pattern
// matching groups.
var arrMatches = null;
// Keep looping over the regular expression matches
// until we can no longer find a match.
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([]);
}
// 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.
var strMatchedValue = arrMatches[2].replace(
new RegExp("\"\"", "g"),
"\""
);
} else {
// We found a non-quoted value.
var strMatchedValue = arrMatches[3];
}
// Now that we have our value string, let's add
// it to the data array.
arrData[arrData.length - 1].push(strMatchedValue);
}
// Return the parsed data.
return (arrData);
}
How can I import datas from a single column of CSV file in spreadsheet?
I have tried this code for single column and it works fine.
function importCSV() {
var getfile = "singleColumn.csv";
var getFolder = DriveApp.getFolderById(fId);
var fi = getFolder.getFilesByName(getfile);
if (fi.hasNext()) {
var ssNew = SpreadsheetApp.getActiveSpreadsheet();
var newSheet = ssNew.getSheetByName("csv");
var file = fi.next();
var csv = file.getBlob().getDataAsString();
var csvData = CSVToArray(csv); // see below for CSVToArray function
// loop through csv data array and insert (append) as rows into the sheet
for (var i = 0; i < csvData.length; i++) {
newSheet.appendRow(csvData[i]);
//newSheet.getRange(newSheet.getLastRow()+1, 1, csvData.length,1).setValue(csvData[i][0]);
}
Browser.msgBox("CSV imported successfully!");
}
}
You may read more from this article.