Copy data from download URL using Google Script - google-apps-script

I'm new to App scripts and need help with copying the data to spreadsheet from URL.
However, URL is not a website but link which after clicking with directly download csv file into the computer. Also, its not ending with .csv as I have seen in other examples here.
URL basically coming to my inbox at a specific time. I'm trying to use Fetch URL but its not working at all.
Sample URL -
https://docs.google.com/spreadsheets/d/1oPUPPUmy7psliSznUItT0DnHvilXwZHzyrmdyHpHi18/export?format=csv
function ABC () {
const searchQuery = 'XYZ';
const threads = GmailApp.search(searchQuery, 0,1);
const urls = [];
threads.forEach(thread => {
const messages = thread.getMessages();
messages.forEach(message => {
const body = message.getBody();
var re = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'"".,<>?«»“”‘’]))/i;
const match = body.match(re);
if (match) { urls.push(match[1]); }
});
}) ;
Logger.log(urls);
url = urls.toString().replace("[","").replace("]","") ;
Logger.log(url);
function getData() {
var attValue = '';
// making a call to the target website
var response = UrlFetchApp.fetch(url);
//logging response from target website - In Script Editor > View > Logs
Logger.log(response.getContentText());
//parsing the response data from website
//https://developers.google.com/apps-script/reference/url-fetch/http-response
var rawData = response.getContentText();
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[1]);
var cell = sheet.getRange(1, 1);
cell.setValue(rawData);
}
};
Kindly help so that I can copy the data directly into spreadsheet or store the file in Google Drive with filename as combination of text and date.
Thanks

SUGGESTION
You can try the tweaked script below.
In my understanding, here is your goal:
Get your email messages that contain URLs (CSV file) via "XYZ" search terms.
Process the URL using URLFetchApp service
Place the CSV data into your second sheet tab.
Note: If there's anything else missing or something may have been misunderstood, feel free to let me know.
Tweaked Script
function ABC() {
/**TWEAKED: Created a function call method called "getData" */
const url = {
getData: function () {
const searchQuery = 'XYZ';
const threads = GmailApp.search(searchQuery, 0, 1);
const urls = [];
threads.forEach(thread => {
const messages = thread.getMessages();
messages.forEach(message => {
const body = message.getBody();
var re = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'"".,<>?«»“”‘’]))/i;
const match = body.match(re);
if (match) { urls.push(match[1]); }
});
});
Logger.log(urls);
/**TWEAKED: Instead of using the redundant replace method,
* used "regex" inside a single replace method to replace
* all [ and ] characters */
var geturl = urls.toString().replace(/\[|]/gm, "");
console.log(geturl)
return geturl;
}
}
var attValue = '';
/**TWEAKED: Call the "url" variable's "getData" function that will return the URL */
var response = UrlFetchApp.fetch(url.getData.call());
//logging response from target website - In Script Editor > View > Logs
Logger.log(response.getContentText());
//parsing the response data from website
//https://developers.google.com/apps-script/reference/url-fetch/http-response
var rawData = response.getContentText();
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[1]);
var cell = sheet.getRange(1, 1);
cell.setValue(rawData);
};
Demonstration
After running the ABC() function on the Apps Script editor, the second sheet tab gets populated with the CSV data:
The Apps Script execution log view
References:
JavaScript Function call()

Related

Create PDF from Google Sheet Template

I am fairly new to code and App Script, but I've managed to come up with this from research.
Form submitted, Sheet populated, take entry data, copy and append new file, save as pdf, email pdf
I've created examples of what I've been trying to do
Link to form - https://docs.google.com/forms/d/e/1FAIpQLSfjkSBkn3eQ1PbPoq0lmVbm-Dk2u2TP_F_U5lb45SddsTsgsA/viewform?usp=sf_link
link to spreadsheet - https://docs.google.com/spreadsheets/d/1kWQCbNuisZsgWLk3rh6_Iq107HoK7g-qG2Gln5pmYTE/edit?resourcekey#gid=1468928415
link to template - https://docs.google.com/spreadsheets/d/1Ye7DyJQOjA3J_EUOQteWcuASBCfqlA-_lzyNw0REjY8/edit?usp=sharing
However I receive the following error - Exception: Document is missing (perhaps it was deleted, or you don't have read access?)
at Create_PDF(Code:32:34)
at After_Submit(Code:13:21)
App Script Code as follows - If I use a google Doc as a template it works. However I would like to use a spreadsheet as a template, and have the result pdf content fit to page. Please let me know if you need any additional information for this to work.
function After_Submit(e, ){
var range = e.range;
var row = range.getRow(); //get the row of newly added form data
var sheet = range.getSheet(); //get the Sheet
var headers = sheet.getRange(1, 1, 1,5).getValues().flat(); //get the header names from A-O
var data = sheet.getRange(row, 1, 1, headers.length).getValues(); //get the values of newly added form data + formulated values
var values = {}; // create an object
for( var i = 0; i < headers.length; i++ ){
values[headers[i]] = data[0][i]; //add elements to values object and use headers as key
}
Logger.log(values);
const pdfFile = Create_PDF(values);
sendEmail(e.namedValues['Your Email'][0],pdfFile);
}
function sendEmail(email,pdfFile,){
GmailApp.sendEmail(email, "Subject", "Message", {
attachments: [pdfFile],
name: "From Someone"
});
}
function Create_PDF(values,) {
const PDF_folder = DriveApp.getFolderById("1t_BYHO8CqmKxVIucap_LlE0MhslpT7BO");
const TEMP_FOLDER = DriveApp.getFolderById("1TNeI1HaSwsloOI4KnIfybbWR4u753vVd");
const PDF_Template = DriveApp.getFileById('1Ye7DyJQOjA3J_EUOQteWcuASBCfqlA-_lzyNw0REjY8');
const newTempFile = PDF_Template.makeCopy(TEMP_FOLDER);
const OpenDoc = DocumentApp.openById(newTempFile.getId());
const body = OpenDoc.getBody();
for (const key in values) {
body.replaceText("{{"+key+"}}", values[key]);
}
OpenDoc.saveAndClose();
const BLOBPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = PDF_folder.createFile(BLOBPDF);
console.log("The file has been created ");
return pdfFile;
}
You get the error message with Google Sheets because you are using a Google Doc class to create the PDF, which is not compatible with Google Sheets.
DocumentApp can only be used with Google Docs. I will advise you to change
const OpenDoc = DocumentApp.openById(newTempFile.getId());
for
const openDoc = SpreadsheetApp.openById(newTempFile.getId());
const newOpenDoc = openDoc.getSheetByName("Sheet1");
And depending on the Google Sheet where the "Body" of the information is located. Replace:
const body = OpenDoc.getBody();
for an equivalent like getRange() or any Range class that helps you target the information you need. For example:
// This example is assuming that the information is on the cel A1.
const body = newOpenDoc.getRange(1,1).getValue();
The template for the PDF should be something like this:

Is there a way to grab the FILTER VIEW unique URL using Google Scripts?

I want to send an email with my filter view unique URL but I don't want to hard code the URL. Is there a way in app scripts where I can get the filter view URL using code?! and put it into different Google Sheets. Even when its not my current active sheet.
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ss = SS.getId;
var url = '';
url += SS.getUrl();
url += 'gid='
url += 'fvid=';
url += ss.getId;
This only gives me the URL to the spreadSheet not the filter view URL which starts with 'fvid='
function getFilterViews(){
// use current sheet name here, or hardcode the sheet name string instead
const sheetName = SpreadsheetApp.getActiveSheet().getSheetName()
const response = Sheets.Spreadsheets.get(SpreadsheetApp.getActive().getId(), {
includeGridData: false,
"ranges": sheetName,
"fields":"sheets.filterViews"
})
const filterViewUrls = response.sheets[0].filterViews.map(fv=>fvToUrl(fv))
console.log(filterViewUrls)
}
function fvToUrl(fv){
const ssId = SpreadsheetApp.getActive().getId()
const url = `https://docs.google.com/spreadsheets/d/${ssId}/edit#gid=${fv.range.getSheetId()}&fvid=${fv.filterViewId}`
return url
}
If you want a specific filter view ('cat' as an example), you can use
const TITLE = 'cat'
const filterViewUrls = response.sheets[0].filterViews.filter(fv=>fv.title===TITLE).map(fv=>fvToUrl(fv))

Using Logger.Log to log different value

I was wondering: is it even possible to use Logger.Log in Google Apps Script to log different string to be posted to a spreadsheet?
I have the following code:
var ss = SpreadsheetApp.openByUrl("spreadsheet url");
var sheet = ss.getSheetByName("spreadsheet sheet");
var DocNumber = e.parameter.DocNumber;
var folderId = "Folder ID 1";
var lastFileUrl = getLatestFile(folderId); // just a function that retrieves url of latest file in the folder
Logger.log(lastFileUrl);
var addUrl = sheet.getRange(1,2,sheet.getLastRow(),1);
var fileURL = "https://drive.google.com/uc?export=view&id="+lastFileUrl;
var folderId2 = "Folder ID 2";
var lastFileUrl2 = getLatestFile(folderId2); // same as above
Logger.log(lastFileUrl2);
var addUrl2 = sheet.getRange(1,3,sheet.getLastRow(),1);
var fileURL2 = "https://drive.google.com/uc?export=view&id="+lastFileUrl2;
sheet.appendRow([DocNumber,fileURL,fileURL2]);
}
When this get posted to the spreadsheet, it only posts the second url (fileURL2) - I assume because the last value in the log is this. But I was hoping to post both URL into the spreadsheet.
I tried setting it as a var first as well:
var URL2 = Logger.log(lastFileURL2);
but then the posted value will be https://drive.google.com/uc?export=view&id=Logger
I also tried using appendRow before the second URL logging but it still only takes the second url and disregard the first url.
Therefore, I was curios whether this is even possible at all?
And if not, what's the best way to achieve this without using Logger.log?
Spreadsheet output:
URL1 and URL2 is the URL from Google Drive folder.
Also, forgot to mention, I'm using the script as a Web App, used by an android app. Posting files into the Drive folder is okay, the only problem is fetching the links of the files in different folders.
These are the codes I used to get the latest file url from my folders:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById("Folder_1_ID").getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
function getLatestFile(folderId2) {
var files2 = DriveApp.getFolderById("Folder_2_ID").getFiles();
var fileObj2 = [];
while (files2.hasNext()) {
var file2 = files2.next();
fileObj2.push({id: file2.getId(), date: file2.getDateCreated()});
}
fileObj2.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj2[0].id;
}
Problem
Having two functions declared under the same name
Solution
Step by step:
Remove one of the functions (they are identical in terms in usage)
Make the remaining one use the parameter passed in it:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById(folderId).getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
Change Logger to console - as of recently, all logs are sent to Stackdriver service, and thus there is no benefit in using Logger (besides by using console you make script more portable).
Commentary
What happens when you declare two or more functions under same name? Normally, the last one declared gets executed (basically, second declaration overwrites the first):
function clone(original) {
return `I am the clone of ${original}`;
}
function clone(cloned) {
return `I am a clone of ${cloned}'s clone`;
}
const elem = document.querySelector("#cloned");
elem.textContent = clone("Gary");
<h2 id="cloned"></h2>

Apps Script Error - Cannot find method getRange(number,number,(class),number)

I've written a custom Google Apps Script that will pull some data (2 columns wide, 50-100 rows long but this varies)in an array 2 from an API, parse it into JSON and then paste into a google sheet.
I can run the script from the editor and it works ok. But when I try to run it from a custom menu or when I run the debugger I get the following error:
'Cannot find method getRange(number,number,(class),number) (line 43)'
Line 43 is the last line of the code.
sheet.getRange(3,1,dataSet.length,2).setValues(rows);
It seems that the issue is that getRange is not able to use the variable of length of the dataset (number of rows) to set the number of rows to use in the range in which the data is to be pasted.
I cannot work out how to fix this - can anyone else see what I am doing wrong? Thanks for taking a look.
//custom menu
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('XXXX Data')
.addItem('Credit Limits','CREDITLIMITS')
.addToUi();
}
function CREDITLIMITS() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); //get active spreadsheet
var sheet = ss.getActiveSheet();
// var sheet = ss.getSheetByName('data'); //get sheet by name from active spreadsheet
// URL and params for the API
var USERNAME = 'XXXXXXX';
var PASSWORD = 'XXXXXXXXXXXXX';
var url = 'https://api.XXXX.com/api/v1/XXX/?where=type=%27XXXXXXX%27'; // var url="http://example.com/feeds?type=json"; // Paste your JSON URL here
var authHeader = 'Basic ' + Utilities.base64Encode(USERNAME + ':' + PASSWORD);
var params = {'method': 'GET','muteHttpExceptions': true,'headers': {'Authorization': authHeader,} };
//call the XXXX API
var response = UrlFetchApp.fetch(url, params); // get api endpoint
var json = response.getContentText(); // get the response content as text
var dataAll = JSON.parse(json); //parse text into json
var dataSet = dataAll;
//create empty array to hold data points
var rows=[],
data;
//loop over the retrun events
for (i=0; i < dataSet.length; i++) {
data = dataSet[i];
//push a row of data as 2d array
rows.push([data.company, data.creditLimit]);
}
// clear any previous content
sheet.getRange(1,1,500,10).clearContent();
// write data to sheet
sheet.getRange(3,1,dataSet.length,2).setValues(rows);
}

Write from Google Firebase to Google Sheets using Google Apps script

Trying to retrieve form entries which are stored in google firebase under the node called entries and append to a google sheet using the script editor in google sheets.
I have added the FirebaseApp library to google sheet script editor. Then my code looks like this:
function getAllData() {
var firebaseUrl = "https://myapp.firebaseio.com/";
var secret = "pCOCwKCC582jpqdZe2EqPqnW3IAd3UyO9oB4uaEL2";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl, secret);
var data = base.getData();
Logger.log(data);
}
when I run this nothing happens. Any ideas?
Next I need to add the returned data from firebase to the google sheet. I was using this code to do this via the sheets api, however I'm not sure how this works in the google script editor?
function addEntries() {
gapi.client.sheets.spreadsheets.values.append({
spreadsheetId: '10lyQpQtEA7euCfdU2isrqB_bgPuy-eSbW74h7oDP3ko',
range: "Sheet1!A1:D100",
majorDimension: "ROWS",
"values": [
["testa", "testb", "testc", "testd"]
],
valueInputOption: 'USER_ENTERED'
}).then(function(response) {
}, function(response) {
appendPre('Error: ' + response.result.error.message);
});
}
I'm using the newest Firebase version. This snippet code works for me.
function getFacturasClientesExistentes() {
var firebaseUrl = "https://test.firebaseio.com/FacturasBLP/clienteExistente";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
var data = base.getData();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Facturas Clientes Existentes");
var num = 2;
range = sheet.getRange("A"+num+":F"+num+"");
for(var i in data) {
var values = [
[ data[i].fecha, data[i].sucursal, data[i].cantidad, data[i].cliente, data[i].correo, data[i].estatus ]
];
range.setValues(values);
num += 1;
range = sheet.getRange("A"+num+":F"+num+"");
}
}
Some notes:
I have previously write the headers for my data in the spreadsheet
In the line range = sheet.getRange("A"+num+":F"+num+""); from A to F I have my headers
I hope this helps someone, this worked for me.
function writeSheets() {
var ss = SpreadsheetApp.openById("10lyQpQtEA7euCfdU2isrqB_bgPuy-eSbW74h7oDP3ko");
var sheet = ss.getSheets()[0];
var firebaseUrl = "https://myapp.firebaseio.com/";
var secret = "pCOCwKCC582jpqdZe2EqPqnW3IAd3UyO9oB4uaEL2"; // get this from firebase project settings
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
var data = base.getData();
var keys = Object.keys(data.entries);
var sheetRow = [];
var entryKeys;
for (index in keys) {
sheetRow = [];
entryKeys = Object.keys(data.entries[keys[index]])
for (i in entryKeys) {
sheetRow.push(data.entries[keys[index]][entryKeys[i]]);
}
//Logger.log(sheetRow);
sheet.appendRow(sheetRow);
}
}
Note: in order for this code to work, you need to install the firebaseapp library in the script editor as per these instructions, https://sites.google.com/site/scriptsexamples/new-connectors-to-google-services/firebase