0

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.

Rubén
  • 34,714
  • 9
  • 70
  • 166
DanCue
  • 619
  • 1
  • 8
  • 17
  • And does `Utilities.parseCsv` work well if you simply pass it the input `dataString` without escaping? – tehhowch Jun 27 '18 at 13:47
  • A third example: Doe, John will also return \r\n. – DanCue Jun 27 '18 at 13:49
  • No, it will give me an error without escaping. – DanCue Jun 27 '18 at 13:49
  • This is how they look in notepad, respectively. (As I've written with quotes) "200,000" and "Google ""Apps"" Script"" and "Doe, John" – DanCue Jun 27 '18 at 13:52
  • If the CSV itself is malformed, you need to research and maybe ask a different question, i.e. "Correcting format errors in a CSV file". Consider also that the charset may not be utf-8, that `getDataAsString` is not `convertBlobToCsvString`, and thus that `Utilities.parseCsv` may not be a valid approach: https://stackoverflow.com/questions/20688560/retrieve-csv-attachment-file-from-gmail-and-place-the-data-in-a-google-spreadshe Use online regex testers and example gmail->GAS scripts for achieving this task, but you didn't cite your sources so I couldn't possibly know which ones you've seen. – tehhowch Jun 27 '18 at 14:08
  • I found this regex on another SO post. https://stackoverflow.com/questions/36658793/apps-script-utilities-parsecsv-assumes-new-row-on-line-breaks-within-double-quot?noredirect=1&lq=1 – DanCue Jun 27 '18 at 15:04

1 Answers1

2

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 );
}
DanCue
  • 619
  • 1
  • 8
  • 17
  • Maybe someone can answer this. If this google sheet is shared will it know to check my e-mail instead of the others who may access it? – DanCue Jun 27 '18 at 17:49
  • I can answer my own question now that I've had this running a while. This was set up as a trigger and therefore whoever set up the trigger is the person whose email will be accessed. – DanCue Feb 16 '20 at 14:16