I found this very useful script on here for iteratively returning a list of file metrics by directory in GDrive. My problem is I want to add columns that exclude emails from a give domain. For example on the fileItem.getEditors I might want to return a list excluding editors in the "#mycompany.com" domain.
var childFolders = parent.getFolders();
var childFiles = parent.getFiles();
var allValues = []; // Added
while (childFiles.hasNext()){
var fileItem = childFiles.next();
data = [
parentName + "/" + fileItem.getName() + "/" + fileItem.getName(),
fileItem.getName(),
fileItem.getMimeType(),
fileItem.getUrl(),
fileItem.getAccess(Session.getActiveUser()),
fileItem.getSharingPermission(),
fileItem.isShareableByEditors(),
fileItem.getOwner().getEmail(),
fileItem.getEditors().map(function(e){return [e.getEmail(), e.getName()]}).join(","),
fileItem.getViewers().map(function(e){return [e.getEmail(), e.getName()]}).join(","),
];
allValues.push(data); // Added
}
sheet.getRange(sheet.getLastRow() + 1, 1, allValues.length, allValues[0].length).setValues(allValues); // Added
Cooper's answer is already great but you don't need to include # on the comparison.
Filter your data by adding filter before the map that gets the email and name:
.filter(function(e){e.getDomain() != "mycompany.com"})
Output:
fileItem.getEditors().filter(function(e){e.getDomain() != "mycompany.com"})
.map(function(e){return [e.getEmail(), e.getName()]}).join(","),
Related
I am trying to write an App Script that takes string data from multiple different spreadsheets (completely separate documents) and puts them all in a new spreadsheet. When I run the logger, it shows me all the data I want. I want each piece of data to show up in Column A, but when I run my script, it only puts 1 data point in the spreadsheet instead of all of them. Can someone give me some guidance? Here is my code:
function pullTogether() {
var files = DriveApp.getFolderById('Folder ID').searchFiles('title != "nothing"');
const rangeName = 'Sheet1!B2:C';
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getId();
const values = Sheets.Spreadsheets.Values.get(name, rangeName).values;
for (const row in values) {
var a1 = (values[row][0]);
Logger.log(a1);
var ss = SpreadsheetApp.openById("ID of new spreadsheet"); //I have the real ID in my code
var cell = ss.getRange("A2");
cell.setValue(a1);
}
}
}
I believe your goal is as follows.
You want to retrieve the values from the column "B" of each Spreadsheet under the specific folder.
You want to put the retrieved values to the column "A" of the destination sheet.
Modification points:
About but when I run my script, it only puts 1 data point in the spreadsheet instead of all of them., when I saw your script, the retrieved value is always put to the cell "A2" of the destination sheet. I think that this might be the reason for your issue.
In your script, I thought that when the following flow is used, the process cost will become low. By this flow, your issue can be also removed.
In your situation, even when Sheets API is not used, the script might work using getValues().
When these points are reflected in your script, it becomes as follows.
Modified script:
Please set the folder ID and the destination Spreadsheet ID.
function pullTogether() {
// Retrieve values from each Spreadsheet.
var values = [];
var files = DriveApp.getFolderById('Folder ID').searchFiles(`title != 'nothing' and mimeType='${MimeType.GOOGLE_SHEETS}'`);
var sheetName = 'Sheet1'
while (files.hasNext()) {
var xFile = files.next();
var sheet = SpreadsheetApp.open(xFile).getSheetByName(sheetName);
if (sheet) {
var v = sheet.getRange("B2:B" + sheet.getLastRow()).getValues();
values = [...values, ...v];
}
}
// Put values to the destination sheet.
var ss = SpreadsheetApp.openById("ID of new spreadsheet"); //I have the real ID in my code
var dstSheet = ss.getSheets()[0];
dstSheet.getRange(2, 1, values.length, values[0].length).setValues(values);
}
Note:
Although I'm not sure about your actual situation, when the above script didn't work by the large data, please modify as follows.
From
dstSheet.getRange(2, 1, values.length, values[0].length).setValues(values);
To
Sheets.Spreadsheets.Values.update({ values }, ss.getId(), `'${dstSheet.getSheetName()}'!A2`, { valueInputOption: "USER_ENTERED" });
References:
getValues()
setValues(values)
Goal: To stack data from 90+ google workbooks, all with the same sheet name, into the one master sheet for reporting
Info:
All worksheets have the same number of columns.
I have the following script but it does not run properly, I think the issue is with how I am caching / Pushing the data to the array before pasting to the output sheet.
I am trying to build an array then paste it in one go.
The tables I am stacking have 47 columns, unknown number of rows.
The part that opens the sheets is all working perfectly.
// Get the data from the worksheets
var indexsheet = SpreadsheetApp.getActive().getSheetByName("Index");
var outputsheet = SpreadsheetApp.getActive().getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL)
var CurrentCycleName = response.getResponseText()
// Assign datasets to variables
var indexdata = indexsheet.getDataRange().getValues();
// For each workbook in the index sheet, open it and copy the data to a cache
indexdata.forEach(function(row, r) {
try {
//open Entity specific workbook
var workbookid = indexsheet.getRange(r + 1, 7, 1, 1).getValues();
var Entityworkbook = SpreadsheetApp.openById(workbookid)
// Open workhseet
Entitysheet.getSheetByName(CurrentCycleName)
// Add PR Data to cache - stacking for all countrys
var PRDataCache = Entitysheet.getDataRange().push()
} catch {}
})
// Set the all values of the sheet at once
outputsheet.getRange(r + 1, 14).setValue('Issue Splitting Data')
Entitysheet.getRange(2, 1, PRDataCache.length || 1, 47).setValues(PRDataCache)
};
This is the index tab where we are getting the workbookid from to open each file
This is the output file, we are stacking all data from each country
I believe your goal is as follows.
You want to retrieve the Spreadsheet IDs from the column "G" of "Index" sheet.
You want to give the specific sheet name using a dialog.
You want to retrieve all values from the specification sheet in all Spreadsheets. In this case, you want to remove the header row.
You want to put the retrieved values on "Output" sheet.
In this case, how about the following sample script?
Sample script:
function myFunction() {
var ss = SpreadsheetApp.getActive();
var indexsheet = ss.getSheetByName("Index");
var outputsheet = ss.getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
var CurrentCycleName = response.getResponseText();
var ids = indexsheet.getRange("G1:G" + indexsheet.getLastRow()).getValues();
var values = ids.reduce((ar, [id]) => {
try {
var [, ...values] = SpreadsheetApp.openById(id).getSheetByName(CurrentCycleName).getDataRange().getValues();
ar = [...ar, ...values];
} catch (e) {
console.log(`"${id}" was not found.`);
}
return ar;
}, []);
if (values.length == 0) return;
// If the number of columns is different in all Spreadsheets, please use the following script.
// var maxLen = Math.max(...values.map(r => r.length));
// values = values.map(r => r.length < maxLen ? [...r, ...Array(maxLen - r.length).fill("")] : r);
outputsheet.getRange(outputsheet.getLastRow() + 1, 1, values.length, values[1].length).setValues(values);
}
Note:
When the number of Spreadsheet IDs is large, the processing time might be over 6 minutes. I'm worried about this. At that time, how about separating the Spreadsheet IDs?
Reference:
reduce()
I have a function that allows me to import spreadsheet data from my gmail to Google Sheets. Previously, the spreadsheet data had only had 6 columns to import. Now, some new changes are made and a 7th column was added. After this change was implemented, my function no longer works and Google Sheets throws this error. May I please have some assistance?
So, as I'm looking at this, the intended functionality looks right to me. Skip the first 3 rows (netdata) and take everything below. Could it be the + 1, 1 ?
The Error:
TypeError: Cannot read property 'getRange' of null
My import function:
function importCSVFromGmail() {
var sheetName = "SHEET_NAME"; // Name of sheet tab.
var threads = GmailApp.search("from:EMAIL HERE label:LABEL HERE"); // "from:recipient email here label:name of your filter/label here"
var messages = threads[0].getMessages();
var message = messages[messages.length - 1];
var attachment = message.getAttachments()[0]; // [0] will by default look for only 1 attachment. If there are more than two attachment increase value. ex: [1] , [2]
var data = [];
if (attachment.getContentType() == MimeType.CSV) { // This will look for a CSV file type first
data = Utilities.parseCsv(attachment.getDataAsString(), ",");
} else if (attachment.getContentType() == MimeType.MICROSOFT_EXCEL || attachment.getContentType() == MimeType.MICROSOFT_EXCEL_LEGACY) { // If attachment is an xls, this line will look at the content to determine and convert accordingly.
var tempFile = Drive.Files.insert({title: "temp", mimeType: MimeType.GOOGLE_SHEETS}, attachment).id;
data = SpreadsheetApp.openById(tempFile).getSheets()[0].getDataRange().getValues();
Drive.Files.trash(tempFile);
}
if (data.length > 0) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var netdata = data.slice(3); // This will skip the number of rows starting from the top.
sheet.getRange(sheet.getLastRow() + 1, 1, netdata.length, netdata[0].length).setValues(netdata );
}
}
I'm think the issue is here:
sheet.getRange(sheet.getLastRow() + 1, 1, netdata.length, netdata[0].length).setValues(netdata );
The error message indicates that the issue is with the sheet variable being null. That happens when there is no sheet by the name SHEET_NAME in the spreadsheet.
To fix the error, replace SHEET_NAME with the name of the sheet you want the function to work with. Check for things like leading and trailing whitespace in the sheet's name.
I have two Spreadsheets. The first spreadsheet contains my raw data that indicates the employee # and name of employees. The second spreadsheet is the spreadsheet I want to copy into a google drive folder. I want to update the specific fields on the 2nd spreadsheet based on the employee number and employee name from the 1st spreadsheet. Everytime it updates the cells in the second spreadsheet, it will create copy of the 2nd spreadsheet into google drive folder.
However, it keeps on setting just one value inside the replicated spreadsheets. It doesn't loop the employee names and employee numbers from the 1st spreadsheet.
My code is already replicating the 2nd spreadsheet. It's just the values aren't updating.
function replicateCards() {
var ss = SpreadsheetApp.openById('xxxxxxxx');
var copyCard = SpreadsheetApp.openById('zzzzzzzzz');
var getID = DriveApp.getFileById(copyCard.getId())
var card = copyCard.getSheetByName("Card");
var mastersheet = ss.getSheetByName("Mastersheet");
var getLastRow = mastersheet.getLastRow();
var destinationFolder = DriveApp.getFolderById('yyyyyyyyyy');
;
var changeColorToGrayList = card.getRangeList(['C7', 'E7', 'G7', 'I7', 'K7', 'M7', 'O7', 'Q7',
'C9', 'E9', 'G9', 'I9', 'K9', 'M9', 'O9', 'Q9',
'C11', 'E11', 'G11', 'I11', 'K11', 'M11', 'O11', 'Q11']);
var setValueToZero = card.getRangeList(['C8', 'E8', 'G8', 'I8', 'K8', 'M8', 'O8', 'Q8',
'C10', 'E10', 'G10', 'I10', 'K10', 'M10', 'O10', 'Q10',
'C12', 'E12', 'G12', 'I12', 'K12', 'M12', 'O12', 'Q12']);
for (i = 1; i < getLastRow; i++) {
var employeeNumber = mastersheet.getRange(i + 1, 1).getValue();
var employeeName = mastersheet.getRange(i + 1, 2).getValue();
card.getRange("C3").setValue(employeeName);
card.getRange("H3").setValue(employeeNumber);
card.setActiveRangeList(changeColorToGrayList).setBackground("gray");
card.setActiveRangeList(setValueToZero).setValue(0);
// var getID = DriveApp.getFileById(card).getId();
getID.makeCopy(employeeNumber + " High Flyer Card", destinationFolder);
}
}
I expect the output of getID.makeCopy(employeeNumber + " High Flyer Card", destinationFolder); contains different employee # and employee names, not just one value inside the google folder.
Your code should work as intended, unless a bulky file makes the code overlap.
If so, implementation of flush() will make your code run sequentially, see here for a detailed explanation.
In your case, modifying the for loop to
for (i = 1; i < getLastRow; i++) {
var employeeNumber = mastersheet.getRange(i + 1, 1).getValue();
var employeeName = mastersheet.getRange(i + 1, 2).getValue();
card.getRange("C3").setValue(employeeName);
card.getRange("H3").setValue(employeeNumber);
card.setActiveRangeList(changeColorToGrayList).setBackground("gray");
card.setActiveRangeList(setValueToZero).setValue(0);
getID.makeCopy(employeeNumber + " High Flyer Card", destinationFolder);
SpreadsheetApp.flush();
}
should solve the issue.
I am currently trying to get grab values from another spreadsheet and then paste it into a destination spreadsheet. The problem I am running into is that I am getting incorrect range height and incorrect range widths when I run this code. I read something about 2d arrays but I believe I already have a 2d array here to paste to the spreadsheet. Thank you for your time.
function GmailToDrive_StaticTest(gmailSubject, importFileID){
var threads = GmailApp.search('subject:' + gmailSubject + ' -label:uploaded has:attachment'); // performs Gmail query for email threads
for (var i in threads){
var messages = threads[i].getMessages(); // finds all messages of threads returned by the query
for (var j in messages){
var attachments = messages[j].getAttachments(); // finds all attachments of found messages
var timestamp = messages[j].getDate(); // receives timestamp of each found message
var timestampMinusOne = new Date(timestamp.getTime() - (86400000)); // sets the received timestamp to exactly one day prior (# in milliseconds)
var date = Utilities.formatDate(timestampMinusOne, "MST", "yyyy-MM-dd"); // cleans the timestamp string
for (var k in attachments){
var blobs = {
dataType: attachments[k].getContentType(), // retrives the file types of the attachments
data: attachments[k].copyBlob(), // creates blob files for every attachment
fileName: attachments[k].getName()
};
var tempFile = DriveApp.createFile(blobs.data.setContentType('text/csv')).setName(blobs.fileName.split("-", 1).toString() + date); // creates csv files in drive's root per blob file
var tempFileConverted = Drive.Files.copy( {}, tempFile.getId(), {convert: true} ); // converts created files to gsheets
var importData = {
file: tempFileConverted,
ID: tempFileConverted.getId(),
Sheet1: SpreadsheetApp.openById(tempFileConverted.getId() ).getActiveSheet(),
Sheet1_Values: SpreadsheetApp.openById(tempFileConverted.getId() ).getActiveSheet().getDataRange().getValues()
};
tempFile.setTrashed(true);
var importData_Sheet1_Rows = importData.Sheet1.getMaxRows(); - 2;
var importData_Sheet1_Columns = importData.Sheet1.getMaxColumns(); - 2;
var destSheet = SpreadsheetApp.openById(importFileID).getSheets()[0];
destSheet.clearContents();
Logger.log(importData.Sheet1_Values)
destSheet.getRange(1, 1, importData_Sheet1_Rows, importData_Sheet1_Columns).setValues(importData.Sheet1_Values);
DriveApp.getFileById(importData.ID).setTrashed(true);
}
}
}
}
getMaxRows() and getMaxColumns() return the maximum number of column and rows in a sheet, while getDataRange().getValues() return all the values in a sheet that contain data .
So, unless all the cells in a sheet have data the dimensions won't match !
The best you could do is to get the actual size of the data array and use that to set the range for the values in the destination sheet.
It goes (more) simply like this :
destSheet.getRange(1, 1, importData.Sheet1_Values.length, importData.Sheet1_Values[0].length).setValues(importData.Sheet1_Values);
you don't need the other values for rows and columns, just ignore that in your script.