need some help with a Google Script that's responsible to fetch a CSV and upload to Google Analytics.
This is how I receive my CSV from my fetch:
sku|maxCPO
123-2-F|10
3212-123-G|10
145-1234-S|70.5
53455-245-A|52.5
12455-13-H|15.5
33352-45-E|10
2443-BOX-H|150
3455-BOX-N|200
What I have in my GoogleScript
function findCsvAttachment() {
var csvDoc = UrlFetchApp.fetch("mycsv");
var csvData = Utilities.parseCsv(csvDoc, "|");
return csvData;
}
function processCsv(csvData) {
var dataRows ="";
var headers = 'ga:productSku,ga:metric3\n';
for (var row = 4979; row < csvData.legth && csvData[row][0]!= ''; row++) {
var currentDataRow= csvData[row][0] + ',' + csvData[row][1]+'\n';
dataRows += String(currentDataRow);
}
var dataForUpload = headers + dataRows;
return dataForUpload;
}
//assumes media dataType for upload https://developers.google.com/analytics/devguides/config/mgmt/v3/mgmtReference/management/uploads/uploadData
function uploadDataToAnalytics(data, accountId, webPropertyId, customDataSourceId) {
var dataBlob = Utilities.newBlob(data, "application/octet-stream");
var upload = Analytics.Management.Uploads.uploadData(accountId, webPropertyId, customDataSourceId, dataBlob);
return upload;
}
///////// ACTUAL IMPLEMENTATION OF SCRIPT /////////////
var csv = findCsvAttachment();
var csvForUpload = processCsv(csv);
var analyticsUpload = uploadDataToAnalytics(csvForUpload, CONFIG.analyticsAccountId, CONFIG.analyticsPropertyId, CONFIG.customDataSourceId);
}
I'm only getting the headers when I run the script, I don't get the rows... Any help is MUCH appreciated! Thanks in advance!
Related
I am trying to pull an attachment from an email which is a zip file and send that file to Google Analytics to upload the data. I am struggling with the getAttachment part in the script as it is showing as undefined. I have no idea what I am doing wrong. Any help would be greatly appreciated. I have now fixed where it is pulling the right attachment by using: var attachments = messages[0].getAttachments(); However I am now getting a new error where it seems to be looking for a title of the attachment but it is still showing as undefined.
function refundImport() {
/// use custom report to schedule the email - will need to adjust the processCsv() function based on your schema
/// if you are using a non-bing data source - you will probably need to adjust the findCsvAttachments() function as it grabs a zip file now
var CONFIG = {
'emailSubject': 'Refunded or Partially Refunded Orders - TKS',
'customDataSourceId': 'xxxxxxxxxxxxxxxxxxxxx',
'now': new Date(),
'zipFileName': 'refunded_or_partially_refunded_orders.zip',
//'csvFileName': 'refunded_or_partially_refunded_orders.csv',
'analyticsAccountId': '12345678',
'analyticsPropertyId': 'UA-12345678-1'
}
//adds one whole day to a date object - can take negative days if you want yesterday etc
function addDaysToDate(DATE, DAYS) {
var newDate = DATE.getTime() + DAYS * 3600000 * 24;
var newDate1 = new Date(newDate);
return newDate1;
}
//takes a date object and formats it as a string
function formatDateAsString(DATE) {
var dateString = Utilities.formatDate(DATE, 'GMT+12:00', 'yyyy/MM/dd');
return dateString;
}
//after and before must be date strings - use the above function
function grabEmailAttachments(SUBJECT, AFTER, BEFORE) {
var query = 'subject:' + SUBJECT + ' ' + 'has:attachment after:' + AFTER + ' ' + 'before:' + BEFORE;
//assumes only 1 will match - if more than 1 - will match the first one
var thread = GmailApp.search('in:inbox from:"noreply#highviewapps.com"');
var messages = thread[0].getMessages();
var content = messages[0].getPlainBody();
//var attachments = thread.getAttachments()[0];
var attachments = messages[0].getAttachments();
//thread.moveToTrash();
return attachments;
}
// finds csv attachment and creates 2d array of row,column e.g. csv[0][1] = value in row 0 column 1 of csv
function findCsvAttachment(attachments, zipFileToSearch, fileNameToSearch) {
var counter = 0;
for (i = 0; i < attachments.length; i++) {
if (attachments[i].getName().search(zipFileToSearch) != -1) {
var unzip = Utilities.unzip(attachments[i]);
var csvData = Utilities.parseCsv(unzip[0].getDataAsString(), ",");
counter = counter + 1;
}
}
if (counter == 0) {
Logger.log('No file with ' + fileNameToSearch + ' in its name was found.');
}
if (counter == 1) {
return csvData;
}
if (counter > 1) {
Logger.log('More than 1 file with ' + fileNameToSearch + ' in its name was found - the last one was used.');
}
}
function processCsv(csvData, date) {
var headers = 'ga:transactionId,ga:productSku,ga:productPrice,ga:quantityRefunded,ga:transactionRevenue';
var dataForUpload = headers;
return dataForUpload;
}
//assumes media dataType for upload https://developers.google.com/analytics/devguides/config/mgmt/v3/mgmtReference/management/uploads/uploadData
function uploadDataToAnalytics(data, accountId, webPropertyId, customDataSourceId) {
var dataBlob = Utilities.newBlob(data, "application/octet-stream");
var upload = Analytics.Management.Uploads.uploadData(accountId, webPropertyId, customDataSourceId, dataBlob);
return upload;
}
///////// ACTUAL IMPLEMENTATION OF SCRIPT /////////////
var tomorrowString = formatDateAsString(addDaysToDate(CONFIG.now, 1));
var yesterdayString = formatDateAsString(addDaysToDate(CONFIG.now, -1));
var todayString = formatDateAsString(CONFIG.now);
var emailAttachments = grabEmailAttachments(CONFIG.emailSubject, yesterdayString, tomorrowString);
var csv = findCsvAttachment(emailAttachments, CONFIG.zipFileName, CONFIG.csvFileName);
var csvForUpload = processCsv(csv, yesterdayString);
var analyticsUpload = uploadDataToAnalytics(csvForUpload, CONFIG.analyticsAccountId, CONFIG.analyticsPropertyId, CONFIG.customDataSourceId);
}
Try changing these lines of code
From:
var attachments = messages.getAttachments();
and
var unzip = Utilities.unzip(attachments[i]);
To:
var attachments = messages[0].getAttachments();
and
var unzip = Utilities.unzip(attachments[i].copyBlob());
I have a sheet named "IMS Import" and I am trying to export the range A3:E to a CSV file by a Google Apps script which will be triggered by a button. The length of the range to be exported changes from week to week so I only want to capture the last populated row. I have the following script but I get a Google error "403. That’s an error. We're sorry, but you do not have access to this page. That’s all we know.":
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var csvMenuEntries = [{name: "Download IMS Import File", functionName: "saveAsCSV"}];
ss.addMenu("Creating a Timetable", csvMenuEntries);
};
function saveAsCSV() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('IMS Import');
// create a folder from the name of the spreadsheet
var folder = DriveApp.createFolder(ss.getName().toLowerCase().replace(/ /g,'_') + '_csv_' + new Date().getTime());
// append ".csv" extension to the sheet name
fileName = sheet.getName() + ".csv";
// convert all available sheet data to csv format
var csvFile = convertRangeToCsvFile_(fileName, sheet);
// create a file in the Docs List with the given name and the csv data
var file = folder.createFile(fileName, csvFile);
//File downlaod
var downloadURL = file.getDownloadUrl().slice(0, -8);
showurl(downloadURL);
}
function showurl(downloadURL) {
//Change what the download button says here
var link = HtmlService.createHtmlOutput('Click here to download');
SpreadsheetApp.getUi().showModalDialog(link, 'Your CSV file is ready!');
}
function convertRangeToCsvFile_(csvFileName, sheet) {
// get available data range in the spreadsheet
var activeRange = sheet.getDataRange();
try {
var data = activeRange.getValues();
var csvFile = undefined;
// loop through the data in the range and build a string with the csv data
if (data.length > 1) {
var csv = "";
for (var row = 2; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// join each row's columns
// add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
I get the error at this point in the script:
SpreadsheetApp.getUi().showModalDialog(link, 'Your CSV file is ready!');
I am hoping someone can help me to get a functioning script so that it creates a CSV from A3 to the last populated row.
If you only want the last row of the range change this
var activeRange = sheet.getDataRange();
to this:
var activeRange = sheet.getRange(sheet.getLastRow(),1,1,sheet.getLastColumn());
As far as page access goes I can't be sure. Perhaps you're logged into to two different accounts.
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
I'm new to App Script. I'm trying to read the submitted data using App Script. I tried to use the answer provided here, but I'm not able to read it.
I tried many other method, they are also not working.
function myFunction() {
var form = FormApp.openById('1_HABIBYRZ-zsEMuzMCnsrAOYFlQSKwaIhmeT1y2SY6Y');
var formResponses = form.getResponses();
MailApp.sendEmail ("test#appirio.com", "Form Submited: Foo feedback " + Date.now(), +formResponses);
}
I also want to know, how to debug any issue in App Script.
Thanks in Advance!
The other answer above may work, but it's a bit complicated. To let you keep the form responses as a variable inside your program, which would allow you to email it, you can do this:
function formResponsesToArray() {
var app = FormApp.openById(...),
responses = app.getResponses(), stringResponses = []
responses.forEach(function(r) {
var response = []
r.getItemResponses().forEach(function(i) {
response.push(i.getResponse())
})
stringResponses.push(response)
})
Logger.log(stringResponses)
}
Which will log an array of responses like this:
The way I'd recommend doing it is feed your form responses into a google sheet, and then export is as a csv via attachment in an email. like so below. It is important to make sure the correct API's are active, such as Drive API, Gmail API and that your oauthScopes are added to the manifest if need be.
editing my answer to recommend stewarts answer. much cleaner, simpler and way more efficient.
function ExportToCSVCrntMonth(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1'); //change this to sheet name
// Change the below line to get the folder where you want the CSV files stored.
var folder = DriveApp.createFolder(ss.getName().toLowerCase().replace(/ /g,'_') + '_csv_' + new Date().getTime());
fileName = ss.getName() + ".csv";
var csvFile = convertRangeToCsvFileCrnt_(fileName, ss);
folder.createFile(fileName, csvFile);
var csvmail = folder.getFilesByName('your sheet name + .csv');
MailApp.sendEmail({
to: "your email ere",
subject: "CSV attached",
body: "body text",
attachments: [csvmail.next()]
});
}
function convertRangeToCsvFileCrnt_(csvFileName, ss) {
var activeRange = ss.getDataRange();
try {
var data = activeRange.getValues();
var csvFile = undefined;
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
I have a Google sheet want to export as CSV file. But there are 2 columns in the sheet I don't want to export.
For example, in the picturecolumn, I don't want to export column "N" and "P"
Here are the Apps Script code I wrote for export
function menu() {
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu('Menu');
var item = menu.addItem('PICC', 'picc');
var item2 = menu.addItem('Export to CSV', 'csv');
item.addToUi();
item2.addToUi()
};
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var csvMenuEntries = [{name: "Download Primary Time File", functionName: "saveAsCSV"}];
//ss.addMenu("Creating a Timetable", csvMenuEntries);
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu('Menu');
var item = menu.addItem('PICC', 'picc');
var item2 = menu.addItem('Export to CSV', 'csv');
item.addToUi();
item2.addToUi()
};
function saveAsCSV() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("sheet1");
// create a folder from the name of the spreadsheet
var folder = DriveApp.createFolder(ss.getName().toLowerCase().replace(/ /g,'_') + '_csv_' + new Date().getTime());
// append ".csv" extension to the sheet name
fileName = sheet.getName() + ".csv";
// convert all available sheet data to csv format
var csvFile = convertRangeToCsvFile_(fileName, sheet);
// create a file in the Docs List with the given name and the csv data
var file = folder.createFile(fileName, csvFile);
//File downlaod
var downloadURL = file.getDownloadUrl().slice(0, -8);
showurl(downloadURL);
}
function showurl(downloadURL) {
var app = UiApp.createApplication().setHeight('60').setWidth('150');
//Change what the popup says here
app.setTitle("Your timetable CSV is ready!");
var panel = app.createPopupPanel()
//Change what the download button says here
var link = app.createAnchor('Click here to download', downloadURL);
panel.add(link);
app.add(panel);
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
function convertRangeToCsvFile_(csvFileName, sheet) {
// get available data range in the spreadsheet
var activeRange = sheet.getDataRange();
try {
var data = activeRange.getValues();
var csvFile = undefined;
// loop through the data in the range and build a string with the csv data
if (data.length > 1) {
var csv = "";
for (var row = 1; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// join each row's columns
// add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
As you can see, I used for loop to export the rows and columns, how can I make change to let the two columns not showing in the export CSV
How about this modification?
Modification points :
Modify convertRangeToCsvFile_().
From data retrieved by getValues(), it removes the columns "N" and "P".
In order to reflect this, please modify as follows.
From :
var data = activeRange.getValues();
var csvFile = undefined;
To :
var data = activeRange.getValues();
data = data.map(function(e){return e.filter(function(_, i){return i != 13 && i != 15})}); // Added
var csvFile = undefined;
If I misunderstand your question, please tell me. I would like to modify it.