Combine Google Docs documents - google-apps-script

Is it possible to merge 100 Google Docs documents into one?
I've tried copy-pasting, but it seems too long and it's not possible to copy comments.

This can be done with Google Apps Script. See this example. The most relevant parts (example assumes nothing but Google Docs in the folder):
function combine() {
var folder = DriveApp.getRootFolder();
if (folder == null) { Logger.log("Failed to get root folder"); return; }
var combinedTitle = "Combined Document Example";
var combo = DocumentApp.create(combinedTitle);
var comboBody = combo.getBody();
var hdr = combo.addHeader();
hdr.setText(combinedTitle)
var list = folder.getFiles();
while (list.hasNext()) {
var doc = list.next();
var src = DocumentApp.openById(doc.getId());
var srcBody = src.getBody();
var elems = srcBody.getNumChildren();
for (var i = 0; i < elems; i++ ) {
elem = srcBody.getChild(i).copy();
// fire the right method based on elem's type
switch (elem.getType()) {
case DocumentApp.ElementType.PARAGRAPH:
comboBody.appendParagraph(elem);
break;
case // something
}
}
}
}
Note that you don't copy the source document's contents in one lump; you have to loop through them as individual elements and fire the correct append* method to add them to the merged/destination file.

I expanded on #noltie's answer to support merging docs in a folder structure recursively, starting from an arbitrary folder (not necessarily the root folder of google docs) and guard agains script failures on too many unsaved changes.
function getDocsRec(rootFolder) {
var docs = [];
function iter(folder) {
var childFolders = folder.getFolders();
while (childFolders.hasNext()) {
iter(childFolders.next());
}
var childFiles = folder.getFiles();
while (childFiles.hasNext()) {
var item = childFiles.next();
var docName = item.getName();
var docId = item.getId();
var doc = {name: docName, id: docId};
docs.push(doc);
}
}
iter(rootFolder);
return docs;
}
function combineDocs() {
// This function assumes only Google Docs files are in the root folder
// Get the id from the URL of the folder.
var folder = DriveApp.getFolderById("<root folder id>");
if (folder == null) { Logger.log("Failed to get root folder"); return; }
var combinedTitle = "Combined Document Example";
var combo = DocumentApp.create(combinedTitle);
var comboBody = combo.getBody();
// merely get the files recursively, does not get them in alphabetical order.
var docArr = getDocsRec(folder);
// Log all the docs we got back. Click "Edit -> Logs" to see.
docArr.forEach(function(item) {
Logger.log(item.name)
});
// this sort will fail if you have files with identical names
// docArr.sort(function(a, b) { return a.name < b.name ? -1 : 1; });
// Now load the docs into the combo doc.
// We can't load a doc in one big lump though;
// we have to do it by looping through its elements and copying them
for (var j = 0; j < docArr.length; j++) {
// There is a limit somewhere between 50-100 unsaved changed where the script
// wont continue until a batch is commited.
if (j % 50 == 0) {
combo.saveAndClose();
combo = DocumentApp.openById(combo.getId());
comboBody = combo.getBody();
}
var entryId = docArr[j].id;
var entry = DocumentApp.openById(entryId);
var entryBody = entry.getBody();
var elems = entryBody.getNumChildren();
for (var i = 0; i < elems; i++) {
var elem = entryBody.getChild(i).copy();
switch (elem.getType()) {
case DocumentApp.ElementType.HORIZONTAL_RULE:
comboBody.appendHorizontalRule();
break;
case DocumentApp.ElementType.INLINE_IMAGE:
comboBody.appendImage(elem);
break;
case DocumentApp.ElementType.LIST_ITEM:
comboBody.appendListItem(elem);
break;
case DocumentApp.ElementType.PAGE_BREAK:
comboBody.appendPageBreak(elem);
break;
case DocumentApp.ElementType.PARAGRAPH:
comboBody.appendParagraph(elem);
break;
case DocumentApp.ElementType.TABLE:
comboBody.appendTable(elem);
break;
default:
var style = {};
style[DocumentApp.Attribute.BOLD] = true;
comboBody.appendParagraph("Element type '" + elem.getType() + "' could not be merged.").setAttributes(style);
}
}
// page break at the end of each entry.
comboBody.appendPageBreak();
}
}
You can create and run a script with the above code on https://script.google.com/home

Both the above fail for me with the script returning a red lozenge:
Service unavailable: Docs Dismiss
(the documents in the folder are found, as are the document id's, and the combined doc is created, but empty)
Fixed that - had a document in the list that wasn't owned by me or was created by conversion. Removed that and away we go.

Google Docs does not support any type of merge, yet.
You can select all 100 docs, download them and try to merge them offline.

Download all the files as Docx, then use Microsoft Word or Open Office to merge the documents using the "master document" feature. (Word also refers to this as "Outline.")

Related

.getEditors() returns 'DriveUser,DriveUser'

Before adding new editors to a Google drive folder, I'd like to check first which ones already exist in that folder's editors. This is to avoid unnecessary share notification if user is already an existing editor.
However, .getEditors() always returns 'DriveUser,DriveUser' so all editors get added even if existing already.
Thanks to advise if you have a solution to this.
Here's my code:
var dropboxID = "zzxxccvv112233";
var folderList = DriveApp.getFolderById(dropboxID).getFoldersByName(employee);
if (folderList.hasNext()) {
var employeeFolder = folderList.next(); //folder already exists so just add new Editors, if any
var currentEditors = employeeFolder.getEditors();
Logger.log("currentEditors = " + currentEditors);
var newEditors = emailTo + "," + emailCc;
newEditors = newEditors.replace(/\s/g, '');
newEditors = newEditors.split(',');
var editorsToAdd = [];
for (var i=0 ; i<newEditors.length ; i++) {
if (currentEditors.indexOf(newEditors[i]) < 0) {
editorsToAdd.push(newEditors[i]);
}
}
employeeFolder.addEditors(editorsToAdd);
} else {
and Logger shows this:
[20-03-05 21:23:46:637 HKT] currentEditors = DriveUser,DriveUser
You should be comparing by email address with User.getEmail()
// Get all of the current editor emails in one array
var currentEditorEmails = currentEditors.map(function (editor) { return editor.getEmail() });
// Check if the new editor emails exist in the currentEditorEmails array
for (var i=0 ; i<newEditors.length ; i++) {
if (currentEditorEmails.indexOf(newEditors[i]) < 0) {
editorsToAdd.push(newEditors[i]);
}
}
If you're using V8, you could use an arrow function instead.
const currentEditorEmails = currentEditors.map(editor => editor.getEmail());

DocumentApp.openById() fails with “Service unavailable”

I am trying to read the contents of a spreadsheet which contains some folderId, fileName and targetFile and then based on the data entered in the spreadsheet.
I am finding the latest fileId in the drive for the same fileName as multiple files with the same name are getting added into the folder daily (this is done by the function mostRecentFiIeInFolder) and then I am trying to copy the contents of the latest file with ID dociIdSource into a different file with ID docIdTarget (which is done by the function docCopy).
But when I tried Implementing this using DocumentApp, I am getting a weird error which says
Service unavailable: Docs
for the code var baseDoc = DocumentApp.openById(docID);.
May I know where I am going wrong?
// Test function to call applyDocCopytoList.
function test(){
applyDocCopytoList();
}
// Read the values from the spreadsheet.
function applyDocCopytoList(){
var originalSpreadsheet = SpreadsheetApp.openById('sheet Id goes here').getSheetByName("sheet name goes here");
var getRange = originalSpreadsheet.getDataRange();
var data = originalSpreadsheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
var folderId = data[i][1];
var fileName = data[i][2];
var targetFile = data[i][3];
Logger.log('****************Record No: ' + i);
Logger.log('folderId: ' + data[i][1]);
Logger.log('fileName: ' + data[i][2]);
Logger.log('targetFile: ' + data[i][3]);
var latestFileId = mostRecentFiIeInFolder(folderId, fileName);
if(latestFileId!= undefined){
docCopy(latestFileId, targetFile);
}
}
}
// Log the id of the latest file with a particular name in the folder.
function mostRecentFiIeInFolder(folderId, fileName) {
var folder = DriveApp.getFolderById(folderId);
Logger.log(folder);
var files = DriveApp.getFilesByName(fileName);
Logger.log(fileName);
var result = [];
// Checks whether the given file is in the folder or not
if(!files.hasNext()){
Logger.log('No such file in the folder with the given name');
}
else{
while (files.hasNext()) {
var file = files.next();
result.push([file.getDateCreated(), file.getId()]);
}
Logger.log('************All the file ids with the same file name and their dates created************');
Logger.log(result);
result.sort(function (x, y){
var xp = x[0];// get first element in inner array
var yp = y[0];
return xp == yp ? 0 : xp > yp ? -1 : 1;// choose the sort order, here its in descending order of created date
});
var id = result[0][1];
Logger.log(id);
return id;
}
}
// Copy the contents of the latest file in the target file.
function docCopy(dociIdSource, docIdTarget){
Logger.log('The file with id: ' + dociIdSource + ' will be copied to the target id: ' + docIdTarget);
var docID = docIdTarget;
var baseDoc = DocumentApp.openById(docID); //Service unavailable: Docs error is thrown for this line of code
var body = baseDoc.getBody();
var otherBody = DocumentApp.openById(dociIdSource).getBody();
var totalElements = otherBody.getNumChildren();
for( var j = 0; j < totalElements; ++j ) {
var element = otherBody.getChild(j).copy();
var type = element.getType();
if( type == DocumentApp.ElementType.PARAGRAPH )
body.appendParagraph(element);
else if( type == DocumentApp.ElementType.TABLE )
body.appendTable(element);
else if( type == DocumentApp.ElementType.LIST_ITEM )
body.appendListItem(element);
else if( type == DocumentApp.ElementType.INLINE_IMAGE )
body.appendImage(element);
else if( type == DocumentApp.ElementType.TEXT )
body.setText(element);
else
throw new Error("According to the doc this type couldn't appear in the body: " + type);
}
}
Note that your mostRecentFiIeInFolder function never actually uses the folder, and doesn't ever check that the files are of the correct type - i.e., are actually Google Docs files. Thus if your searched name should have found nothing (i.e. there is no recent file with that name in your target folder), but you had some alternate file elsewhere in your Drive, one that is not a Google Docs file, your script will find it and treat it as something it is not.
The solution is to restrict your search to your desired folder, and again by actual Google Docs mimetype:
function testIt() {
var folderIds = [
"", // Search all of Drive
"123428349djf8234", // Search that specific folder
];
// Log (in Stackdriver) the file id of the most recently created Google Docs file with the name "some name" in the various folders:
folderIds.forEach(function (folderId) {
console.log(getMostRecentFileIdWithName("some name", folderId, MimeType.GOOGLE_DOCS));
});
}
function getMostRecentFileIdWithName(fileName, folderId = "", mimeType = "") {
// If a folder was given, restrict the search to that folder. Otherwise, search with
// DriveApp. (Throws an error if the folderId is invalid.)
var parent = folderId ? DriveApp.getFolderById(folderId) : DriveApp;
// I assume your fileName variable does not contain any unescaped single-quotes.
var params = "name='" + fileName + "'";
// If a mimetype was given, search by that type only, otherwise search any type.
if (mimeType)
params += " and mimeType='" + mimeType + "'";
var matches = parent.searchFiles(params),
results = [];
// Collect and report results.
while (matches.hasNext())
results.push(matches.next());
if (!results.length)
throw new Error("Bad search query \"" + params + "\" for folder id = '" + folderId + "'.");
// Sort descending by the creation date (a native Date object).
// (a - b sorts ascending by first column).
results.sort(function (a, b) { return b.getDateCreated() - a.getDateCreated(); });
return results[0].getId();
}
You can read more about the acceptable search parameters in the Drive REST API documentation, and more about the Apps Script native implementation in the DriveApp documentation.

How to use ContinuationToken with recursive folder iterator

Because of Drive API Quotas, Services Quotas and limit of script execution time 6 min it's often critical to split Google Drive files manipulations on chunks.
We can use PropertiesService to store continuationToken for FolderIterator or FileIterator.
This way we can stop our script and on next run continue from the place we stop.
Working example (linear iterator)
// Logs the name of every file in the User's Drive
// this is useful as the script may take more that 5 minutes (max execution time)
var userProperties = PropertiesService.getUserProperties();
var continuationToken = userProperties.getProperty('CONTINUATION_TOKEN');
var start = new Date();
var end = new Date();
var maxTime = 1000*60*4.5; // Max safe time, 4.5 mins
if (continuationToken == null) {
// firt time execution, get all files from Drive
var files = DriveApp.getFiles();
} else {
// not the first time, pick up where we left off
var files = DriveApp.continueFileIterator(continuationToken);
}
while (files.hasNext() && end.getTime() - start.getTime() <= maxTime) {
var file = files.next();
Logger.log(file.getName());
end = new Date();
}
// Save your place by setting the token in your user properties
if(files.hasNext()){
var continuationToken = files.getContinuationToken();
userProperties.setProperty('CONTINUATION_TOKEN', continuationToken);
} else {
// Delete the token
PropertiesService.getUserProperties().deleteProperty('CONTINUATION_TOKEN');
}
Problem (recursive iterator)
For retrieve tree-like structure of folder and get it's files we have to use recursive function. Somethiong like this:
doFolders(DriveApp.getFolderById('root folder id'));
// recursive iteration
function doFolders(parentFolder) {
var childFolders = parentFolder.getFolders();
while(childFolders.hasNext()) {
var child = childFolders.next();
// do something with folder
// go subfolders
doFolders(child);
}
}
However, in this case I have no idea how to use continuationToken.
Question
How to use ContinuationToken with recursive folder iterator, when we need to go throw all folder structure?
Assumption
Is it make sense to construct many tokens with name based on the id of each parent folder?
If you're trying to recursively iterate on a folder and want to use continuation tokens (as is probably required for large folders), you'll need a data structure that can store multiple sets of continuation tokens. Both for files and folders, but also for each folder in the current hierarchy.
The simplest data structure would be an array of objects.
Here is a solution that gives you the template for creating a function that can recursively process files and store continuation tokens so it can resume if it times out.
Simply modify MAX_RUNNING_TIME_MS to your desired value (now it's set to 1 minute).
You don't want to set it more than ~4.9 minutes as the script could timeout before then and not store its current state.
Update the processFile method to do whatever you want on files.
Finally, call processRootFolder() and pass it a Folder. It'll be smart enough to know how to resume processing the folder.
Sure there is room for improvement (e.g. it simply checks the folder name to see if it's a resume vs. a restart) but this will most likely be sufficient for 95% of people that need to iterate recursively on a folder with continuation tokens.
function processRootFolder(rootFolder) {
var MAX_RUNNING_TIME_MS = 1 * 60 * 1000;
var RECURSIVE_ITERATOR_KEY = "RECURSIVE_ITERATOR_KEY";
var startTime = (new Date()).getTime();
// [{folderName: String, fileIteratorContinuationToken: String?, folderIteratorContinuationToken: String}]
var recursiveIterator = JSON.parse(PropertiesService.getDocumentProperties().getProperty(RECURSIVE_ITERATOR_KEY));
if (recursiveIterator !== null) {
// verify that it's actually for the same folder
if (rootFolder.getName() !== recursiveIterator[0].folderName) {
console.warn("Looks like this is a new folder. Clearing out the old iterator.");
recursiveIterator = null;
} else {
console.info("Resuming session.");
}
}
if (recursiveIterator === null) {
console.info("Starting new session.");
recursiveIterator = [];
recursiveIterator.push(makeIterationFromFolder(rootFolder));
}
while (recursiveIterator.length > 0) {
recursiveIterator = nextIteration(recursiveIterator, startTime);
var currTime = (new Date()).getTime();
var elapsedTimeInMS = currTime - startTime;
var timeLimitExceeded = elapsedTimeInMS >= MAX_RUNNING_TIME_MS;
if (timeLimitExceeded) {
PropertiesService.getDocumentProperties().setProperty(RECURSIVE_ITERATOR_KEY, JSON.stringify(recursiveIterator));
console.info("Stopping loop after '%d' milliseconds. Please continue running.", elapsedTimeInMS);
return;
}
}
console.info("Done running");
PropertiesService.getDocumentProperties().deleteProperty(RECURSIVE_ITERATOR_KEY);
}
// process the next file or folder
function nextIteration(recursiveIterator) {
var currentIteration = recursiveIterator[recursiveIterator.length-1];
if (currentIteration.fileIteratorContinuationToken !== null) {
var fileIterator = DriveApp.continueFileIterator(currentIteration.fileIteratorContinuationToken);
if (fileIterator.hasNext()) {
// process the next file
var path = recursiveIterator.map(function(iteration) { return iteration.folderName; }).join("/");
processFile(fileIterator.next(), path);
currentIteration.fileIteratorContinuationToken = fileIterator.getContinuationToken();
recursiveIterator[recursiveIterator.length-1] = currentIteration;
return recursiveIterator;
} else {
// done processing files
currentIteration.fileIteratorContinuationToken = null;
recursiveIterator[recursiveIterator.length-1] = currentIteration;
return recursiveIterator;
}
}
if (currentIteration.folderIteratorContinuationToken !== null) {
var folderIterator = DriveApp.continueFolderIterator(currentIteration.folderIteratorContinuationToken);
if (folderIterator.hasNext()) {
// process the next folder
var folder = folderIterator.next();
recursiveIterator[recursiveIterator.length-1].folderIteratorContinuationToken = folderIterator.getContinuationToken();
recursiveIterator.push(makeIterationFromFolder(folder));
return recursiveIterator;
} else {
// done processing subfolders
recursiveIterator.pop();
return recursiveIterator;
}
}
throw "should never get here";
}
function makeIterationFromFolder(folder) {
return {
folderName: folder.getName(),
fileIteratorContinuationToken: folder.getFiles().getContinuationToken(),
folderIteratorContinuationToken: folder.getFolders().getContinuationToken()
};
}
function processFile(file, path) {
console.log(path + "/" + file.getName());
}

Is there a limit to how many G-Forms i can link to a singe google spreadshee?

I have a script that is designed to create about 120 forms and link them to a single spreadsheet where I will analyze the data. I don't have any issues with the script until my spreadsheet has about a dozen forms linked to it. Then I get an error saying the destination ID is invalid, after logging the id, and entering it manually into a url I see no issues with the ID....
var ssSummaryId = '******redacted*******';
var form = FormApp.create('RM#' + rmNumber).setDestination(FormApp.DestinationType.SPREADSHEET, ssSummaryId);
form.setDescription(valuesSummary[ii][2])
.setConfirmationMessage('Thanks for the update on room ' + rmNumber)
.setShowLinkToRespondAgain(false);
// form.setDestination(FormApp.DestinationType.SPREADSHEET, ssSummaryId);
var formId = form.getId();
var formUrl = form.getPublishedUrl();
EDIT I'm adding my complete script and some additional info, just in case someone wants to check my code out and point out all it rookie mistakes.
I'm using Google scripts to build a spreadsheet and then create 120 slightly altered Google forms that are linked to a single spreadsheet, all the responses are by design on separate sheets named "form responses n". I consistently hit a wall once I exceed 10 forms linked to one sheet. Note; in initial testing I remember having a spreadsheet with 46 forms (and therefore sheets) linked to it. As you can see in the code below, I have the app restart from where it's left off after every 5 forms are created, so I'm not getting any 'extended runtime errors". Just the error below, typically after the script runs twice from Google Scripts IDE.
I'm just finally getting the hand of basic javascript after years of using and modifying js in web developing. So I apologize in advanced for the poor code.
Failed to set response destination. Verify the destination ID and try again. (line 54, file "Code")
function spreadsheet_builder() {
var ssSummaryId = '<<REDACTED>>';
var ssFormDataId = '<<REDACTED>>';
var batchSize = 5;
var buildStatus = false;
var ssSummary = SpreadsheetApp.openById(ssSummaryId);
SpreadsheetApp.setActiveSpreadsheet(ssSummary);
if (ssSummary.getSheetByName('Summary') == null) {
var sheetSummary = ssSummary.getSheetByName('Sheet1');
} else {
var sheetSummary = ssSummary.getSheetByName('Summary');
}
var rangeSummary = sheetSummary.getDataRange();
var valuesSummary = rangeSummary.getValues();
buildStatus = get_last_position(valuesSummary, buildStatus); //either returns last position in array or 'true' if task is complete
if (buildStatus != true || buildStatus > 0) {
var formCreation = [];
var formData = get_form_data(ssFormDataId); // Get form questions from form Data ss, might be better to keep everything on the same sheet
batchSize = buildStatus + batchSize;
for ( var ii = buildStatus; ii < batchSize; ii++ ) {
if (valuesSummary[ii][1] != '') {
var formCreationReturn = form_builder(formData, valuesSummary, ii, ssSummaryId);
formCreation.push(formCreationReturn);
}
}
var aSum = [ssSummary, sheetSummary, rangeSummary];
final_storing_operation(formCreation, aSum, buildStatus);
}
if (sheetSummary.getName() != 'Summary') {
SpreadsheetApp.setActiveSpreadsheet(ssSummary);
sheetSummary.activate().setName('Summary');
sheetSummary.setFrozenColumns(3);
sheetSummary.setFrozenRows(1);
sheetSummary.hideColumns(1);
//var sSumIndex = sheetSummary.getIndex();
}
SpreadsheetApp.setActiveSpreadsheet(ssSummary);
sheetSummary.activate();
ssSummary.moveActiveSheet(1);
}
function form_builder(formData, valuesSummary, ii, ssSummaryId) {
var lastFormCreated = ii;
var rmNumber = valuesSummary[ii][1];
var form = FormApp.create('RM#' + rmNumber).setDestination(FormApp.DestinationType.SPREADSHEET, ssSummaryId);
form.setDescription(valuesSummary[ii][2])
.setConfirmationMessage('Thanks for the update on room ' + rmNumber)
.setShowLinkToRespondAgain(false);
// form.setDestination(FormApp.DestinationType.SPREADSHEET, ssSummaryId);
var formId = form.getId();
var formUrl = form.getPublishedUrl();
var sectionHeader = 'SECTION_HEADER'; //preformatted form question types.
var list = 'LIST';
var paragraphText = 'PARAGRAPH_TEXT';
for (var j = 1; j < formData.length; j++) { //top row is header
switch (formData[j][0]) {
case sectionHeader:
form.addSectionHeaderItem().setTitle(formData[j][1]);
break;
case list:
var item = form.addListItem();
item.setTitle(formData[j][1]).setHelpText(formData[j][2]);
item.setChoices([
item.createChoice(formData[j][3]),
item.createChoice(formData[j][4]),
item.createChoice(formData[j][5])
]);
break;
case paragraphText:
form.addParagraphTextItem().setTitle(formData[j][1]);
break;
default:
form.addSectionHeaderItem().setTitle('OOPS u\'don MESSED up');
break;
}
}
return [formId, formUrl, lastFormCreated, rmNumber];
}
function final_storing_operation(formCreation, aSum, buildStatus) {
SpreadsheetApp.setActiveSpreadsheet(aSum[0]);
aSum[1].activate();
var startRow = formCreation[0][2] + 1;
var newRange = aSum[1].getRange(startRow, 1, formCreation.length, 2); //row, clmn, rows, columns
var newValues = [];
for ( var ij = 0; ij < formCreation.length; ij++) {
var values = [formCreation[ij][0], "\=HYPERLINK(\"" + formCreation[ij][1] + "\", \"RM#" + formCreation[ij][3] + "\")"];
newValues.push(values);
}
newRange.setValues(newValues);
}
function get_last_position (valuesSummary, buildStatus) {
var rowPos = 1; // start at 1 to ignore headers
while (valuesSummary[rowPos][1] != '') {
if (valuesSummary[rowPos][0] == '') {
return rowPos;
}
rowPos++;
}
if(valuesSummary[rowPos][0] != '' && valuesSummary[rowPos][1] != '') {
buildStatus = true;
return buildStatus;
}
}
function get_form_data (ssFormDataId) {
var ssFormData = SpreadsheetApp.openById(ssFormDataId);
SpreadsheetApp.setActiveSpreadsheet(ssFormData);
var sheetFormData = ssFormData.getSheets()[0];
var rangeFormData = sheetFormData.getDataRange();
var valuesFormData = rangeFormData.getValues();
return valuesFormData;
}
As an alternative, you could create the forms, and intentionally not link them to a spreadsheet, then have some code that looped through every form, and extracted the data. You'd probably want to put the forms into their own folder.
Or, you'd need to build a form with Apps Script HTML Service, embed it into a Apps Script Gadget in a Google Site, and have everyone fill out the form from Sites.

Summing same cell across hundreds of google Spreadsheets

I have hundreds of reports in my google drive. Each report is its own file (Spreadsheet, with one sheet).
I need a total of cell B10 from all those spreadsheets. It would be great if there was a function that took two parameters:
the name of the directory containing the Spreadsheet files
the specific cell you want totaled.
I tried to do script
function Suma(cell)
{
var files = DocsList.getFilesByType('spreadsheet', 0, 100);
for (var i = 0; i < files.length; i++)
{
var sheets = SpreadsheetApp.open(files[i]).getSheets();
var sum = 0;
for (var i = 0; i < sheets.length ; i++ )
{
var sheet = sheets[i];
var val = sheet.getRange(cell).getValue();
if (typeof(val) == 'number')
{
sum += val;
}
}
}
return sum;
}
need some help of course :)
THX
What you are doing is ok however u might want to list files on a folder and not the entire drive..
Also will fail if you have too many ss and script will run out of time. To solve that is possible but requires more complex code. Better do it on client side with jsapi or appengine.
This is going to be SLOW, and has a risk of timing out, due to the time it takes to open each spreadsheet.
This version of your function will take a folder name and a target cell, and produce a sum of the numeric values at that location from all spreadsheets in the folder.
The DriveApp relies on iterators rather than arrays for collections of folders and files, so you've got two examples of using .hasNext() and .next() for accessing objects in collections.
function Suma(folderName, cell)
{
var folders = DriveApp.getFolders();
var folderFound = false;
var folder;
while (folders.hasNext() && !folderFound) {
folder = folders.next();
folderFound = folder.getName() == folderName;
}
if (!folderFound) throw "No folder named " + folderName; // Error
var sheets = folder.getFilesByType(MimeType.GOOGLE_SHEETS);
var sum = 0;
while (sheets.hasNext()) {
var sheet = SpreadsheetApp.openById(sheets.next().getId());
var val = sheet.getActiveSheet().getRange(cell).getValue();
if (typeof(val) == 'number') {
sum += val;
}
}
return sum;
}