Trouble Uncompressing Zip and then parsing CSV with Google Apps Script - google-apps-script

Trying to create a Google Apps Script that reads emails from a specific email address and copy the contents of a CSV file into a Google sheet.
Problem is sometimes the files come in as zip attachments. I'm trying to work logic into the script to uncompress the zip if it's a zip attachment and then post the data to the Google sheet.
Currently getting the following error message: Cannot find function getDataAsString in object Blob.
Any ideas / recommendations for getting this to work with potential zip files would be great.
You can find my full code below:
function myFunction() {
var threads = GmailApp.search("from:testemail#example.com");
var message = threads[0].getMessages()[0];
var attachment = message.getAttachments()[0];
Logger.log(attachment.getContentType());
// Is the attachment a CSV file
if (attachment.getContentType() === "text/csv") {
var sheet = SpreadsheetApp.getActiveSheet();
var csvData = Utilities.parseCsv(attachment.getDataAsString(), ",");
Logger.log("Found a CSV file");
// Remember to clear the content of the sheet before importing new data
sheet.clearContents().clearFormats();
sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
if (attachment.getContentType() === "application/zip") {
var sheet = SpreadsheetApp.getActiveSheet();
var files = Utilities.unzip(attachment);
Logger.log(files);
var newDriveFile = DriveApp.createFile(files[0]);
var csvData = Utilities.parseCsv(files.getDataAsString(), ",");
Logger.log("Found a ZIP file");
// Remember to clear the content of the sheet before importing new data
sheet.clearContents().clearFormats();
sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
}

Utilities.unzip will return array of blobs. So you need to refer the particular blob inside the array of blobs. Just modify the below code snippet.
var files = Utilities.unzip(attachment)[0];
Also, it is safe to mention the charset so that special characters are displayed properly.
var csvData = Utilities.parseCsv(attachment.getDataAsString('ISO-8859-1'), ",");
Entire Function:
function exportData() {
var threads = GmailApp.search("from:testemail#example.com");
var message = threads[0].getMessages()[0];
var attachment = message.getAttachments()[0];
Logger.log(attachment.getContentType());
// Is the attachment a CSV file
if (attachment.getContentType() === "text/csv") {
var sheet = SpreadsheetApp.getActiveSheet();
var csvData = Utilities.parseCsv(attachment.getDataAsString('ISO-8859-1'), ",");
Logger.log("Found a CSV file");
// Remember to clear the content of the sheet before importing new data
sheet.clearContents().clearFormats();
sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
if (attachment.getContentType() === "application/zip") {
var sheet = SpreadsheetApp.getActiveSheet();
var files = Utilities.unzip(attachment)[0];
var csvData = Utilities.parseCsv(files.getDataAsString('ISO-8859-1'), ",");
Logger.log("Found a ZIP file");
// Remember to clear the content of the sheet before importing new data
sheet.clearContents().clearFormats();
sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
}

Related

Formatting Issues with CSV import to Google Sheets from Gmail

I'm currently importing CSV data from gmail emails automatically using the following script:
function importsearchresultsCSVFromGmail2() {
var threads = GmailApp.search("Scheduled report (********** **** *******)");
var messages = threads[0].getMessages();
var message = messages[messages.length - 1];
var attachment = message.getAttachments()[0];
// Is the attachment a CSV file
attachment.setContentType('text/csv');
//attachment.setContentTypeFromExtension();
if (attachment.getContentType() === "text/csv") {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var sh0 = sheet.getSheetByName("Sheet1")
var csvData = Utilities.parseCsv(attachment.getDataAsString(), ",");
// Remember to clear the content of the sheet before importing new data
sh0.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
GmailApp.markMessageRead(message);
}
}
However, some address fields in the csv are formatted in the entire cell of the csv to be:
Address Layout 1
and some will be shown as "123 New Road, London, NE3 1RD". < These display correctly in the import, however the formatted version above is spread across a number of cells and messes up the original layout of the csv.
Is there a way to ensure the imported cell (that contains the formatted address cell) wont spread across to other cells?
Current CSV File Example
Current Google Sheet Result
Thanks in advance.
Tom
Here is a variant (not so elegant as Yuri solution)
function importCSVFromGoogleDrive() {
var file = DriveApp.getFilesByName("CSV TEST.csv").next();
var csvString = file.getBlob().getDataAsString().replace(/\n/g, '♥').replace(/\r♥/g, '\r\n')
var csvData = Utilities.parseCsv(csvString);
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('mySheet');
sheet.clear();
sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
SpreadsheetApp.flush()
let ranges = SpreadsheetApp.getActive()
.createTextFinder("♥")
.matchEntireCell(false)
.matchCase(true)
.matchFormulaText(false)
.ignoreDiacritics(true)
.findAll();
ranges.forEach(function (range) {
range.setValue(range.getValue().replace(/♥/g,"\n"));
});
}
adapted to your situation
var csvData = Utilities.parseCsv(attachment.getDataAsString().replace(/\n/g, '♥').replace(/\r♥/g, '\r\n'), ",");
sh0.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
SpreadsheetApp.flush()
let ranges = sh0
.createTextFinder("♥")
.matchEntireCell(false)
.matchCase(true)
.matchFormulaText(false)
.ignoreDiacritics(true)
.findAll();
ranges.forEach(function (range) {
range.setValue(range.getValue().replace(/♥/g, "\n"));
});
It would be better if you show your CSV as a text.
I think there are \r and \n inside a quote marks ". You can try to replace them with spaces if you change this line:
var csvData = Utilities.parseCsv(attachment.getDataAsString(), ",");
to this:
var s = attachment.getDataAsString().replace(/"[^"]+?"/, x => x.replace(/[\r\n]+/g, ' '));
var csvData = Utilities.parseCsv(s, ',');
Output:
Update
Here is the variant of about the same function. It does the same thing: replaces \r and \n within quote marks. But it replaces them with the special symbol. And replaces this symbol back to \n after the data is pasted on the sheet:
function get_csv() {
var threads = GmailApp.search("Scheduled report"); // subject
var message = threads[0].getMessages().pop(); // get last message from first thread
var attachment = message.getAttachments()[0]; // first attachment
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Sheet1");
// get the csv string and replace \n with the symbol ¶
var s = attachment.getDataAsString().replace(/"[^"]+?"/g, x => x.replace(/[\r\n]+/g, '¶'));
var csvData = Utilities.parseCsv(s, ',');
sh.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
sh.getDataRange().createTextFinder('¶').replaceAllWith('\n'); // restore \n inside cells
GmailApp.markMessageRead(message);
}
Result:
Here is the CSV data (not sure if the \r\n are intact after copy and paste):
Activity date & time,Status change,Postcode,Introducer,Marketing source,Assigned user,Lead reference,Lead date,Signed Date,Offered Date,Completed Date,Title,First name,Last name,Lead type,Lead group,Site,Product,"Security Address, if different from above"
01/02/2022,Signed,NE3 1RD,Me,,Me,172312026,15/11/2021,None,None,None,Mr.,John,Smith,Band A,Type 1,Leads.com,Fixed 5,"123 New Road, LONDON, NE3 1RD"
01/02/2022,Signed,NE3 1RD,Me,,Me,172312026,15/11/2021,None,None,None,Mr.,John,Smith,Band A,Type 1,Leads.com,Fixed 5,"123 New Road
LONDON
NE3 1RD"

comma error while importing csv using appscript to google sheets

I am trying to import a csv file to google sheets via google appscript. everything works well except in the last few rows the data is being copied after skipping a row and also it keeps adding more commas to the those empty rows. not sure why. i Have added a test file for reference
https://docs.google.com/spreadsheets/d/1jnzPalCAgO84kxUBrM-aIobIDHu7c99VStJySEyUkKo/edit?usp=sharing
function getCSVAndAppend(spreadsheetId, folderId, filename) {
var folder = DriveApp.getFolderById('1TMFXWDTJpwqTY0JsCefuAean4n9fWIIh');
var files = folder.getFilesByName('Dealers joined data.csv');
var openSpreadsheet = SpreadsheetApp.openById('1E5z7Qd3KRb5geNKlQiYhliYCOtJ0A8ICmZFcUcA1-lI');
var activeSheet = SpreadsheetApp.getActiveSheet();
if (files.hasNext()) {
var file = files.next();
var csv = file.getBlob().getDataAsString();
var csvData = Utilities.parseCsv(csv);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var header = csvData.shift();
var lastrow = activeSheet.getLastRow();
activeSheet.getRange(lastrow + 1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
}

Apend CSV from gdrive

I am trying to append data from csv saved in google drive to another file in google sheets, I want to ensure that the data gets pasted without the headers. I keep getting an error.
function getCSVAndAppend(spreadsheetId, folderId, filename)
{var folder = DriveApp.getFolderById('1TMFXWDTJpwqTY0JsCefuAean4n9fWIIh');
var files = folder.getFilesByName('Dealers joined data.csv');
var openSpreadsheet = SpreadsheetApp.openById('1E5z7Qd3KRb5geNKlQiYhliYCOtJ0A8ICmZFcUcA1-lI');
var activeSheet = SpreadsheetApp.getActiveSheet();
if ( files.hasNext())\
{var file = files.next();
var csv = file.getBlob().getDataAsString();
var csvData = Utilities.parseCsv(csv);
var csvDataNoHeader = csvData.splice(1,csvData.length-1)
var lastrow = activeSheet.getLastRow();
activeSheet.getRange(lastrow + 1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
}
It's possible that there are other issues as noted by Yuri. But this splice looks incorrect to me
var csvDataNoHeader = csvData.splice(1,csvData.length-1)
I think the 1 should be zero and the other parameter should be 1 but it might actually be easier just to use shift to remove the first row. I think csvData is a 2d array.

Exclude final row during csv file dump into google sheet

I am extracting a csv file from my Gmail and dumping it into a Google Sheet using this Google App Script.
But the csv file I am receiving has a 'total' towards the end of it, I would like to load all the data except last row from the csv file i.e., n-1
How do I tweak the below code to such that the code excludes last delimited value
function importfromGmail() {
var sheetName = "Stackoverflow"; // Please set the tab name you want to overwrite the data.
var threads = GmailApp.search("label:Stackoverflow"); // Please set here.
var messages = threads[0].getMessages();
var message = messages[messages.length - 1];
var attachment = message.getAttachments()[0];
var data = [];
Logger.log("ContentType: "+attachment.getContentType())
//Logger.log(Utilities.parseCsv(attachment.getDataAsString(), ","));
data = Utilities.parseCsv(attachment.getDataAsString(), ",");
Logger.log(data.length)
if (data.length > 0) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
Logger.log(SpreadsheetApp.getActiveSpreadsheet().getUrl());
sheet.clearContents().clearFormats();
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
}
In your situation, how about modifying your script as follows?
From:
data = Utilities.parseCsv(attachment.getDataAsString(), ",");
To:
data = Utilities.parseCsv(attachment.getDataAsString(), ",");
data.pop();
By pop(), the last element is removed from data.
Reference:
pop()

Sheets - Script - Limit number of rows imported from a CSV using a script?

I've got a script that's importing a CSV from a location on G-Drive - I just need it to import the first 300 rows, as opposed to all 2000 rows. What am I missing?
function importCSVFromGoogleDrive() {
var fSource = DriveApp.getFolderById('xxxxxxxxxxxx'); // reports_folder_id = id of folder where csv is saved
var file = DriveApp.getFilesByName("content.csv").next();
var csvData = Utilities.parseCsv(file.getBlob().getDataAsString());
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
First 300 lines of csv file
function importCSVFromGoogleDrive() {
var folder = DriveApp.getFolderById('xxxxxxxxxxxx');
var files = folder.getFilesByName("content.csv");
var n=0;
while(files.hasNext()) {
var file=files.next();
n++;
}
if(n==1) {
var csvData = Utilities.parseCsv(file.getBlob().getDataAsString()).slice(0,300);
SpreadsheetApp.getActiveSheet().getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}else{
SpreadsheetApp.getUi().alert("More Than One File with that name");
}
}
Array.slice()
I tested with a csv file of my own.