Find matching value in a file for multiple spreadsheets - google-apps-script

I have multiple spreadsheets in a folder, for each spreadsheet and I would like to get the matching data from an external file.
For example if my spreadsheet is called 'iphone 7' I would like to get in that sheet all the records from my external file containing 'iphone 7'
Here is how far I got (I am pretty new to scripting !) :
function myfunction()
{
var root = DriveApp.getFoldersByName("Produits");
while (root.hasNext())
{
var folder = root.next(); //If the folder is available, get files in the folder
var files = folder.getFiles();
while(files.hasNext()) //For each file,
{
var spreadsheet = SpreadsheetApp.open(files.next());
//import data from URL
var csvUrl = "https://incensy.tempurl.host/test-ct-flux.csv";
var csvContent = UrlFetchApp.fetch(csvUrl).getContentText();
var csvData = Utilities.parseCsv(csvContent);
var sheets = spreadsheet.getSheets()
var sheetIndex=0
var sheet = sheets[sheetIndex]
sheet.getRange(2, 1, csvData.length, csvData[0].length).setValues(csvData);
//Only keep data that contains the file name
var name = spreadsheet.getName();
let range = sheet.getDataRange(),
maxRows = sheet.getMaxRows(),
srchCol_1 = 2,
srchPatt_1 = new RegExp(name, "i"),
newRangeVals = range.getValues().filter(r => r[0] && srchPatt_1.exec(r[srchCol_1])),
numRows = newRangeVals.length;
range.clearContent();
sheet.getRange(2,1, numRows, newRangeVals[0].length).setValues(newRangeVals);
console.log('myfunction')
sheet.deleteRows(numRows + 1, maxRows - numRows);
}
}
}
There is something wrong in the second part of the code I cannot figure out.

In your situation, how about the following modification?
Modified script:
function myfunction() {
var keywords = ["sample1", "sample2"]; // Please set the keywords you want to filter to the column "C".
// Retrieve CSV data.
var csvUrl = "https://incensy.tempurl.host/test-ct-flux.csv";
var csvContent = UrlFetchApp.fetch(csvUrl).getContentText();
var csvData = Utilities.parseCsv(csvContent, ";");
// Retrieve Spreadsheet and put the CSV data.
var root = DriveApp.getFoldersByName("Produits");
while (root.hasNext()) {
var folder = root.next();
var files = folder.getFiles();
while (files.hasNext()) {
var spreadsheet = SpreadsheetApp.open(files.next());
var name = spreadsheet.getName().toUpperCase();
var values = csvData.reduce((ar, r) => {
if (!keywords.some(e => r[2].includes(e)) && r.join("").toUpperCase().includes(name)) {
ar.push(r);
}
return ar;
}, []);
if (values.length == 0) continue;
var sheet = spreadsheet.getSheets()[0];
sheet.clearContents().getRange(1, 1, values.length, values[0].length).setValues(values);
}
}
}
In this modification, the CSV data is retrieved at the outside of the while loop. Using the retrieved CSV data and each Spreadsheet name, the data is put to the 1st tab of each Spreadsheet.
References:
reduce()
toUpperCase()
includes()

Related

Google sheet : Get values from the web and keep value matching each file in a folder

I am trying to do something and I am almost there but I have been scratching my head on a 'length' issue for quite some time now.
What I want to do :
Get data from an URL (web csv file)
Loop through all spreadsheets in a folder
For each spreadsheet only keep the data from the web csv file that contains the spreadsheet's name.
My code works on a single page but not when I try to apply it to all spreadsheets in the folder.
I have this error : "TypeError: Cannot read property 'length' of undefined
myfunction # Getproductfiles.gs:35"
Here is the full code :
function myfunction()
{
var root = DriveApp.getFoldersByName("Produits");
while (root.hasNext())
{
var folder = root.next(); //If the folder is available, get files in the folder
var files = folder.getFiles();
while(files.hasNext()) //For each file,
{
var spreadsheet = SpreadsheetApp.open(files.next());
//import data from URL
var csvUrl = "https://incensy.tempurl.host/test-ct-flux.csv";
var csvContent = UrlFetchApp.fetch(csvUrl).getContentText();
var csvData = Utilities.parseCsv(csvContent);
var sheets = spreadsheet.getSheets()
var sheetIndex=0
var sheet = sheets[sheetIndex]
sheet.getRange(2, 1, csvData.length, csvData[0].length).setValues(csvData);
// Always show 3 decimal points
var cell = sheet.getRange("D2:D");
cell.setNumberFormat("00");
//Only keep data that contains the file name
var name = spreadsheet.getName();
let range = sheet.getDataRange(),
maxRows = sheet.getMaxRows(),
srchCol_1 = 2,
srchPatt_1 = new RegExp(name)
newRangeVals = range.getValues().filter(r => r[0] && srchPatt_1.exec(r[srchCol_1])),
numRows = newRangeVals.length;
range.clearContent();
sheet.getRange(2,1, numRows, newRangeVals[0].length).setValues(newRangeVals);
sheet.deleteRows(numRows + 1, maxRows - numRows);
//Clean headers
var cell = sheet.getRange(1,1);
cell.setValue("Column1");
var cell = sheet.getRange(1,2);
cell.setValue("Column2");
var cell = sheet.getRange(1,3);
cell.setValue("Column3");
var cell = sheet.getRange(1,4);
cell.setValue("Column4");
var cell = sheet.getRange(1,5);
cell.setValue("Column5");
var cell = sheet.getRange(1,6);
cell.setValue("Column6");
}
}
}
Try it this way:
function myfunction() {
var csvUrl = "myurl";
var csvContent = UrlFetchApp.fetch(csvUrl).getContentText();
var csvData = Utilities.parseCsv(csvContent);
if (csvData && csvData.length > 0) {
var root = DriveApp.getFoldersByName("Produits");
while (root.hasNext()) {
var folder = root.next();
var files = folder.getFiles();
while (files.hasNext()) {
let file = files.next();
let ss = SpreadsheetApp.open(file);
let sh = ss.getSheets()[0];
sh.getRange(2, 1, csvData.length, csvData[0].length).setValues(csvData);
sh.getRange("D2:D" + sh.getLastRow()).setNumberFormat("00");
SpreadsheetApp.flush();
let name = ss.getName();
let [h,...vA] = sh.getDataRange().getValues();
let maxRows = sh.getMaxRows();
let vs = vA.filter(r => r[0] && r[2] == name);
let numRows = vs.length;
sh.getDataRange().clearContent();
SpreadsheetApp.flush();
if (vs && vs[0].length) {
sh.getRange(2, 1, numRows, vs[0].length).setValues(vs);
sh.deleteRows(numRows + 1, maxRows - numRows);
sh.getRange(1, 1, 1, 6).setValues([["Column1", "Column2", "Column3", "Column4", "Column5", "Column6"]]);
}
}
}
}
}

Import data from URL into all sheets in a Google drive Folder

I am trying to import data from a specific URL into all sheets that are in a Google drive folder, then only keep the data that contains the name of the file.
Getting the files from the folder seems to be working but I cannot manage to have SpreasheetApp working (I think this is where the problem comes from). Maybe there is something wrong with how I manage to get ranges.
Here is what I have done so far :
function myfunction()
{
var root = DriveApp.getFoldersByName("Produits");
while (root.hasNext())
{
var folder = root.next(); //If the folder is available, get files in the folder
var files = folder.getFiles();
while(files.hasNext()) //For each file,
{
var spreadsheet = SpreadsheetApp.open(files.next());
//import data from URL
var csvUrl = "myURL";
var csvContent = UrlFetchApp.fetch(csvUrl).getContentText();
var csvData = Utilities.parseCsv(csvContent);
var activeSpreadSheet = SpreadsheetApp.open(files);
var sheets = activeSpreadSheet.getSheets();
var sheet = sheets[sheetIndex]
sheet.getRange(2, 1, csvData.length, csvData[0].length).setValues(csvData);
// Always show 3 decimal points
var cell = sheet.getRange("D2:D");
cell.setNumberFormat("00");
//Only keep data that contains the file name
var name = sheet.getName();
let range = sheet.getDataRange(),
maxRows = sheet.getMaxRows(),
srchCol_1 = 2,
srchPatt_1 = new RegExp(name)
newRangeVals = range.getValues().filter(r => r[0] && srchPatt_1.exec(r[srchCol_1])),
numRows = newRangeVals.length;
range.clearContent();
sheet.getRange(2,1, numRows, newRangeVals[0].length).setValues(newRangeVals);
sheet.deleteRows(numRows + 1, maxRows - numRows);
//Clean headers
var cell = sheet.getRange(1,1);
cell.setValue("Marchand");
var cell = sheet.getRange(1,2);
cell.setValue("Image");
var cell = sheet.getRange(1,3);
cell.setValue("Titre de l'offre");
var cell = sheet.getRange(1,4);
cell.setValue("Prix");
var cell = sheet.getRange(1,5);
cell.setValue("Lien");
var cell = sheet.getRange(1,6);
cell.setValue("Categorie");
}
}
}
Change
var activeSpreadSheet = SpreadsheetApp.open(files);
var sheets = activeSpreadSheet.getSheets();
to (spreadsheet has already been defined previously)
var sheets = spreadsheet.getSheets()
and give a value to sheetIndex, for instance
var sheets = spreadsheet.getSheets();
var sheetIndex=0
var sheet = sheets[sheetIndex]

Insert the link into the keyword in the sheet file

Please help me:
I have a sheet file with 2 columns
Column A is the keyword, column B is the link to insert the keyword. Eg:
Column A Column B
Key1 Link1
Key2 Link2
... ...
How to automatically find keywords in the DOCS file and then insert Link?
Here is my idea, but it doesn't work
function insertLink() {
var file,files,folder,folders,newestFileID;
var filethaythe = DriveApp.getFilesByName('Set Link');
var ss = SpreadsheetApp.open(filethaythe.next());//ID sheet thư viện thay thế
SpreadsheetApp.setActiveSpreadsheet(ss);
SpreadsheetApp.setActiveSheet(ss.getSheets()[0]);
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Trang tính1');
var sheet = ss.getSheetByName('Trang tính1');
var values = sheet.getDataRange().getValues();
folders = DriveApp.getFoldersByName('test3');
while (folders.hasNext()) {
folder = folders.next();
files = folder.getFilesByType("application/vnd.google-apps.document");
while (files.hasNext()){
file = files.next();
var newestFileID = file.getId();
Utilities.sleep(500);
var currentDoc = DocumentApp.openById(newestFileID);
var dongcuoi= sh.getLastRow();
var dc = dongcuoi +1;
var rgtxt = currentDoc.getBody();
var rgrep = ss.getSheets()[0].getRange("A1:B"+dc);
var repA = rgrep.getValues().filter(r => r.every(c => c.toString()));
repA.forEach(e => rgtxt.setLinkUrl(...e));
currentDoc.saveAndClose();
break
}
};
}
I believe your goal as follows.
You want to set the hyperlink to the word on Google Document.
The words and hyperlinks are retrieved from Google Spreadsheet.
In this case, how about the following modification?
Modified script:
function insertLink() {
// 1. Retrieve values from Spreadsheet.
var file,files,folder,folders,newestFileID;
var filethaythe = DriveApp.getFilesByName('Set Link');
var ss = SpreadsheetApp.open(filethaythe.next());
var sheet = ss.getSheetByName('Trang tính1');
var values = sheet.getDataRange().getValues();
// 2. Retrieve Google Document.
folders = DriveApp.getFoldersByName('test3');
while (folders.hasNext()) {
folder = folders.next();
files = folder.getFilesByType("application/vnd.google-apps.document");
while (files.hasNext()) {
file = files.next();
var newestFileID = file.getId();
// 3. Search words and set hyperlinks on Google Document.
var currentDoc = DocumentApp.openById(newestFileID);
var rgtxt = currentDoc.getBody();
values.forEach(([a, b]) => {
var s = rgtxt.findText(a);
while (s) {
var start = s.getStartOffset();
s.getElement().asText().setLinkUrl(start, start + a.length - 1, b);
s = rgtxt.findText(a, s);
}
});
currentDoc.saveAndClose();
break
}
}
}
In order to search the word, the method of findText is used. And, the method of setLinkUrl sets the hyperlink to the searched word.
References:
findText(searchPattern, from)
setLinkUrl(startOffset, endOffsetInclusive, url)

Update a cell and return PDF file of the sheet

I've this spreadsheet that is of 2 sheets, Data nd Report.
function doPost(e) {
var DATA_SHEET = "Data";
var REPORT_SHEET = "Report";
var FILE_Id = "1sXSWMQfPkkwDS4lsSpnV2y54lhKFpdeY8brqyGtuc_k";
// Prevent concurrent access overwritting data
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
// As we are passing JSON in the body, we need to unpairse it
var jsonString = e.postData.getDataAsString();
e.parameter = JSON.parse(jsonString);
try {
// next set where we write the data - you could write to multiple/alternate destinations
// var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var doc = SpreadsheetApp.openById(FILE_Id);
var sheet = doc.getSheetByName(DATA_SHEET);
var report = doc.getSheetByName(REPORT_SHEET);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var lastRow = sheet.getLastRow()
var nextRow = lastRow + 1; // get next row
var row = [];
if(lastRow < 10){
RefID = "PRF.00" + lastRow
} else {
if(lastRow < 100){
RefID = "PRF..0" + lastRow
} else {
RefID = "PRF.." + lastRow
}
}
// loop through the header columns
for (i in headers){
if (headers[i] == "Ref"){ // special case if you include a 'Timestamp' column
row.push(RefID);
} else { // else use header name to get data
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
row.push(e.parameter[headers[i]]);
}
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
/*********************************************/
// report.getRange("D4").setValue(RefID);
// Need to create the PDF here and send it back to the client
/*********************************************/
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
.setMimeType(ContentService.MimeType.JSON);
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
.setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
lock.releaseLock();
}
}
The Report sheet looks like this:
And the Data sheet looks like this:
What I'm trying to do is:
[Done] Receiving data from client and post in the Data sheet, with assigning RefID and Timestamp which is done correctly.
[Done] Update the data in the merged cells (D4:F4) at the Report sheet with the latest RefID that had been added, I worked it withreport.getRange("D4").setValue(RefID);
[No idea how to make it] Once step 2 is done, some vlookup statements are working at the Report sheet, I want to generate a PDF from this sheet and send it back to the client.
UPDATE
I tried this to get the PDF, but did not work, it created copy of the sheet, but failed in converting the vlookup merged cells into text, and failed to generate the pdf file:
function printPDF() {
var file = SpreadsheetApp.openById(FILE_Id);
var sourceSheet = file.getSheetByName(REPORT_SHEET);
var folder = DriveApp.getFolderById(FOLDER_Id);
//Copy whole spreadsheet
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(FILE_Id).makeCopy("tmp_convert_to_pdf", folder))
//delete redundant sheets
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != REPORT_SHEET){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
//replace cell values with text (to avoid broken references)
var sourceRange = sourceSheet.getRange(1,1,20,8);
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, 20, 8);
destRange.setValues(sourcevalues);
//save to pdf
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(REPORT);
var newFile = folder.createFile(theBlob);
//Delete the temporary sheet
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
Modification points:
If you want to make the additional script of printPDF() work correctly, please put SpreadsheetApp.flush() before the blob is retrieved from Spreadsheet.
In the case of the Google Docs (Document, Spreadsheet and Slides), when the blob is retrieved from the file, the format is automatically changed to PDF format. It seems that this is the current specification.
When above points are reflected to your script, it becomes as follows.
Modified script:
From:
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(REPORT);
var newFile = folder.createFile(theBlob);
To:
SpreadsheetApp.flush(); // Added
var theBlob = destSpreadsheet.getBlob().setName(REPORT); // Modified. This is the same with destSpreadsheet.getBlob().getAs('application/pdf').setName(REPORT)
var newFile = folder.createFile(theBlob);
Then your full code be as:
function printPDF(RefID) {
var file = SpreadsheetApp.openById(FILE_Id);
var sourceSheet = file.getSheetByName(REPORT_SHEET);
sourceSheet.getRange("D4").setValue(RefID);
var folder = DriveApp.getFolderById(FOLDER_Id);
//Copy whole spreadsheet
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(FILE_Id).makeCopy("tmp_convert_to_pdf", folder))
// Copy sheet data into values to avoid reference issues
var destSheet = destSpreadsheet.getSheetByName(REPORT_SHEET);
var sourceRange = sourceSheet.getRange(1,1,destSheet.getMaxRows(), destSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
//delete redundant sheets
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != REPORT_SHEET){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
//save to pdf
SpreadsheetApp.flush(); // Added
var theBlob = destSpreadsheet.getBlob().setName(REPORT); // Modified. This is the same with
// destSpreadsheet.getBlob().getAs('application/pdf').setName(REPORT)
var newFile = folder.createFile(theBlob);
//Delete the temporary sheet
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
Reference:
flush()
getBlob().getAs('application/pdf') will not work. You need to use a Google API for converting a sheet to PDF.
You need to do something along the following lines:
const url = `https://docs.google.com/spreadsheets/d/${FILE_Id}/export?exportFormat=pdf&format=pdf`;
const options = {
headers: {
Authorization: `Bearer ${ScriptApp.getOAuthToken()}`
}
};
const blob = UrlFetchApp.fetch(url, options).setName(`${REPORT}`);

Script for uploading web files (via links in Google Spreadsheet) to Google Drive

I'm trying to write script for Google Spreadsheet which will upload all files linked in column A (only URLs) to specific folder (which name is written in column B).
table example
I'm partially succesfull. I can upload first linked file. But I don't know how to edit this script to upload all the files. Where am I wrong?
This script was found here and edited by me.
function downloadFile() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Pokus");
var fileURLs = sheet.getRange(1, 1, sheet.getLastRow()).getValues();
var foldernames = sheet.getRange(1, 2, sheet.getLastRow()).getValues();
for (i = 0; i < fileURLs.length; i++) {
var response = UrlFetchApp.fetch(fileURLs[i], {muteHttpExceptions: true});
var rc = response.getResponseCode();
if (rc == 200) {
var fileBlob = response.getBlob();
var folder = DriveApp.getFoldersByName(foldernames[i]).next();
if (folder != null) {
var file = folder.createFile(fileBlob);
fileName = file.getName();
fileSize = file.getSize();
}
}
var fileInfo = { "rc":rc, "fileName":fileName, "fileSize":fileSize };
return fileInfo;
}
}
I'm beginner in writing scripts and I'm not experienced. Could someone help?
How about this modification?
Modification points :
There is return fileInfo; in for loop. So at 1st loop, it's returned from the function. By this, only first linked file can be uploaded.
All information can be retrieved by modifying fileInfo to an array.
Modified script :
function downloadFile() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Pokus");
var fileURLs = sheet.getRange(1, 1, sheet.getLastRow()).getValues();
var foldernames = sheet.getRange(1, 2, sheet.getLastRow()).getValues();
var fileInfo = []; // Added
for (i = 0; i < fileURLs.length; i++) {
var response = UrlFetchApp.fetch(fileURLs[i], {muteHttpExceptions: true});
var rc = response.getResponseCode();
if (rc == 200) {
var fileBlob = response.getBlob();
var folder = DriveApp.getFoldersByName(foldernames[i]).next();
if (folder != null) {
var file = folder.createFile(fileBlob);
fileName = file.getName();
fileSize = file.getSize();
}
}
fileInfo.push({ "rc":rc, "fileName":fileName, "fileSize":fileSize }); // Modified
}
return fileInfo; // Modified
}
Note :
When there are many upload files, an error might occur.
If I misunderstand your question, I'm sorry.