How to get older versions of Google Spreadsheet data? - google-apps-script

I'm trying to compare different versions of Spreadsheet data and highlight differences between the two versions. I know there is Version History feature, but what I'm trying is to get all the revision history when choosing the version.(i.e. if the latest version is five and I choose two, I want to get all the revisions made between version two and five).
I know how to access to the current version, but I'm stuck with how to do this with older versions. Is there any way to access to older versions as the same manner as the following code?
var spreadsheet = SpreadsheetApp.openById(id);
var sheet = spreadsheet.getSheets()[0];
var sheetdata = sheet.getDataRange().getValues();

You want to retrieve the values from other version of Spreadsheet using the following script.
var spreadsheet = SpreadsheetApp.openById(id);
var sheet = spreadsheet.getSheets()[0];
var sheetdata = sheet.getDataRange().getValues();
You have already got the revision ID of the Spreadsheet.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? When only values are retrieved from the other version of Spreadsheet, how about this workaround? Unfortunately, the Spreadsheet of the some revision cannot be directly retrieved as the Spreadsheet. So in this workaround, it is converted to other format and retrieved the values. Please think of this as just one of several answers.
Flow:
The flow of this workaround is as follows.
Retrieve Spreadsheet of other version as the excel format.
Convert the excel format to Google Spreadsheet.
By this, the values are separated to each sheet.
Retrieve values using your script.
Sample script:
Before you run this script, please enable Drive API at Advanced Google services. And set the variables of spreadsheetId and revisionId.
function myFunction() {
var spreadsheetId = "###"; // Please set the Spreadsheet ID.
var revisionId = "###"; // Please set the revision ID.
var url = "https://docs.google.com/spreadsheets/export?id=" + spreadsheetId + "&revision=" + revisionId + "&exportFormat=xlsx";
var blob = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}}).getBlob();
var id = Drive.Files.insert({mimeType: MimeType.GOOGLE_SHEETS, title: revisionId}, blob).id;
var spreadsheet = SpreadsheetApp.openById(id);
var sheet = spreadsheet.getSheets()[0];
var sheetdata = sheet.getDataRange().getValues();
}
Note:
In this sample script, a Spreadsheet of the version is created to the root folder.
The filename is the revision ID.
When you want to retrieve the revision IDs of the Spreadsheet, please use Revisions: list.
References:
Download and publish file revisions
Revisions: list
Revisions: get
Files: insert
Advanced Google services
If I misunderstood your question and this was not the direction you want, I apologize.

Related

Google Sheets – Find and Replace multiple Code

Maybe someone can help me with my Problem.
First some background: I have copied some WhatsApp chats to Google Sheets. I use Latex to generate a book containing the chats. Google Sheet is able to display all Emojis from WhatsApp, Latex, of course, isn't. So I downloaded the Emojis as png files and defined Latex commands to include those emojis as graphic.
For Example: Too display the regular smiling emoji I type \grin. I have a list with hundreds of emojis in one row and the corresponding command in the next row.
Until now I used search and replace -> replace all in the same sheet for every single type of emoji. But as this takes hours I wondered if there is any way to make this more effective.
Here is a spreadsheet with a small example: Google Sheet
Thanks in advance!
You want to replace the Emojis to the defined Latex commands.
For example, you want to replace as follows.
From 03.01.19, 00:29 - me: Hi 😊 to 03.01.19, 00:29 - me: Hi \nettnett.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Flow:
The flow of this sample script is as follows. This sample script uses your shared Spreadsheet.
Retrieve data from the sheet of Codes for Smileys.
Create the request body for the findReplace request of batchUpdate method of Sheets API.
Run the method of batchUpdate.
Sample script:
This script used Sheets API. So, before you run the script, please enable Sheets API at Advanced Google services.
function myFunction() {
var dataSheet = "Codes for Smileys";
var sourceSheet = "unedited Chats with Smileys";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getSheetByName(dataSheet).getDataRange().getValues();
data.shift();
var sheetId = ss.getSheetByName(sourceSheet).getSheetId();
var requests = data.map(function(row) {return {findReplace: {sheetId: sheetId, find: row[0], replacement: row[1]}}});
Sheets.Spreadsheets.batchUpdate({requests: requests}, ss.getId());
}
This sample script uses your shared Spreadsheet. So in this case, the data sheet is Codes for Smileys. And the source sheet for converting is unedited Chats with Smileys.
Note:
If you change the sheet name, also please modify above script. Please be careful this.
When you run the script for the first time, the authorization screen is opened. So please authorize the scopes for using the script.
References:
Advanced Google services
spreadsheets.batchUpdate
FindReplaceRequest
Added:
You want to put the converted values to the sheet of Chat with Latex Code.
The sample script for achieving above is as follows.
Sample script:
function myFunction2() {
var dataSheet = "Codes for Smileys";
var sourceSheet = "unedited Chats with Smileys";
var destinationSheet = "Chats with LaTeX Codes";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(dataSheet);
var data = ss.getSheetByName(dataSheet).getDataRange().getValues();
data.shift();
var srcSheet = ss.getSheetByName(sourceSheet);
var tempSheet = srcSheet.copyTo(ss);
var sheetId = tempSheet.getSheetId();
var requests = data.map(function(row) {return {findReplace: {sheetId: sheetId, find: row[0], replacement: row[1]}}});
Sheets.Spreadsheets.batchUpdate({requests: requests}, ss.getId());
var tempValues = tempSheet.getDataRange().getValues();
var destSheet = ss.getSheetByName(destinationSheet);
destSheet.getRange(destSheet.getLastRow() + 1, 1, tempValues.length, tempValues[0].length).setValues(tempValues);
ss.deleteSheet(tempSheet);
}
In this sample script, the following flow is run.
Copy the source sheet unedited Chats with Smileys as a temporal sheet.
Create request body for the batchUpdate method to the temporal sheet.
Run the batchUpdate.
Copy the converted values from the temporal sheet to the destination sheet Chats with LaTeX Codes.
In this case, the converted values are put to the last row of the sheet.
Delete the temporal sheet.

Retrieve Values for All Sheets in Google Spreadsheet for Particular Revision

This is a sample spreadsheet with 2 sheets (Sheet1 and Sheet2). The cell values are
1) [["This", "is", "first", "sheet"], ["Added row 2", "", "", ""]] for Sheet1
2) [["This", "is", "second", "sheet"], ["Added row 2", "", "", ""]] for Sheet2
When I run the following code,
var file_id = '1JqbuqOQS5NSFqfPlxJG-DY1HIW9xZl_ddjQa_F2pUv4';
var revisions = Drive.Revisions.list(file_id);
var revision = revisions.items[revisions.items.length-1];
var revUrl = Drive.Revisions.get(file_id, revision.id).exportLinks["text/csv"] + "&access_token=" + ScriptApp.getOAuthToken();
var revString = UrlFetchApp.fetch(revUrl).getContentText();
I only get revString = "This,is,first,sheet\nAdded row 2,,,"
How can I get the second sheet values?
You want to retrieve the latest revision.
This is from var revision = revisions.items[revisions.items.length-1].
You want to also retrieve CSV data from other revisions.
You want to retrieve CSV data from all sheets for a certain revision.
You want to use Google Apps Script.
If my understanding is correct, how about this answer?
Issue:
Unfortunately, CSV data from all sheets cannot be retrieved from revUrl of your script. The default endpoint of exportLinks["text/csv"] indicates 1st tag of the Spreadsheet. If you want to retrieve other sheets, it is required to add the query parameter of gid={sheetId} for each sheet.
And in the current stage, the Spreadsheet of ceatain revision cannot be directly accessed using Spreadsheet Service and Sheets API. So when the CSV data is retrieved from the revision data, if the existing sheets had already been removed and/or new sheets had already been added, all sheet IDs cannot be retrieved.
Workaround 1:
This workaround supposes the condition that the sheets in the Spreadsheet are removed and added.
In this modification, I used the following flow.
Retrieve XLSX data from the Spreadsheet of a certain revision.
Convert XLSX data to Google Spreadsheet as a temporal Spreadsheet.
Retrieve all values from the converted Spreadsheet as CSV data.
Delete temporal Spreadsheet.
By above flow, CSV data from all sheets with the certain revision can be retrieved.
Modified script:
var file_id = '1JqbuqOQS5NSFqfPlxJG-DY1HIW9xZl_ddjQa_F2pUv4';
var revisions = Drive.Revisions.list(file_id);
var revision = revisions.items[revisions.items.length-1];
// I modified following script.
var revUrl = Drive.Revisions.get(file_id, revision.id).exportLinks["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] + "&access_token=" + ScriptApp.getOAuthToken();
var blob = UrlFetchApp.fetch(revUrl).getBlob();
var tempSSId = Drive.Files.insert({mimeType: MimeType.GOOGLE_SHEETS}, blob).id;
var sheets = SpreadsheetApp.openById(tempSSId).getSheets();
for (var i = 0; i < sheets.length; i++) {
var csv = sheets[i].getDataRange().getValues().map(function(e) {return e.join(",")}).join("\n");
Logger.log(csv)
}
Drive.Files.remove(tempSSId);
Workaround 2:
This workaround supposes the condition that the sheets in the Spreadsheet are not removed and not added.
In this modification, I used the following flow.
Retrieve all sheet IDs from the current Spreadsheet.
Create requests using retrieved sheet IDs.
Retrieve CSV data from each sheet.
By above flow, CSV data from all sheets with the certain revision can be retrieved.
Modified script:
var file_id = '1JqbuqOQS5NSFqfPlxJG-DY1HIW9xZl_ddjQa_F2pUv4';
var sheetIds = SpreadsheetApp.openById(file_id).getSheets().map(function(e) {return e.getSheetId()});
var token = ScriptApp.getOAuthToken();
var revisions = Drive.Revisions.list(file_id);
var revision = revisions.items[revisions.items.length-1];
var reqs = sheetIds.map(function(e) {
var revUrl = Drive.Revisions.get(file_id, revision.id).exportLinks["text/csv"] + "&access_token=" + token + "&gid=" + e;
return {url: revUrl, muteHttpExceptions: true};
});
var res = UrlFetchApp.fetchAll(reqs);
for (var i = 0; i < res.length; i++) {
if (res[i].getResponseCode() == 200 ) {
Logger.log(res[i].getContentText())
}
}
Note:
This answer supposes that Drive API is enabled at Advanced Google Services.
References:
Files: insert
Files: delete
fetchAll()
If I misunderstood your question and this was not the result you want, I apologize.

Importing XLSX file from the monthly e-mail (in Gmail) to the designated Google Sheet

I receive an XLSX file from our client on regular basis, and I would like to automate the process of importing it from Gmail (it's automatically labeled) into Google Sheets. So far I managed to make it work for CSV files, but XLSX files seem to be trickier. Can someone help to adjust this code I have for CSV files?
function getCSV()
{
var thread = GmailApp.getUserLabelByName(‘Reconciliation’).getThreads(0,1);
var messages = thread[0].getMessages();
var len = messages.length;
var message=messages[len-1] //get last message
var attachments = message.getAttachments(); // Get attachment of first message
var csv = attachments[0].getDataAsString();
var data = Utilities.parseCsv(csv);
var sheet = SpreadsheetApp.openById("some id").getSheetByName(‘Data’);
sheet.clearContents();
var range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
}
You want to put the data from xlsx file attached to an email to the existing Spreadsheet.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
When you use this script, please enable Drive API at Advanced Google Services and API console. You can see about this at here.
Flow of modified script:
Retrieve a blob of xlsx file.
Convert xlsx format to Google Spreadsheet.
Retrieve values from the converted Spreadsheet.
Remove the converted file.
Put the values to the sheet of Data in the existing Spreadsheet.
Modified script:
Please modify as follows.
From:
var csv = attachments[0].getDataAsString();
var data = Utilities.parseCsv(csv);
To:
var xlsxBlob = attachments[0]; // Is supposes that attachments[0] is the blob of xlsx file.
var convertedSpreadsheetId = Drive.Files.insert({mimeType: MimeType.GOOGLE_SHEETS}, xlsxBlob).id;
var sheet = SpreadsheetApp.openById(convertedSpreadsheetId).getSheets()[0]; // There is the data in 1st tab.
var data = sheet.getDataRange().getValues();
Drive.Files.remove(convertedSpreadsheetId); // Remove the converted file.
Note:
In this modification, it supposes the following points. If your situation is different from the following points, please modify it.
attachments[0] is the blob of xlsx file.
About the xlsx file, the data you want to put is in a 1st tab.
Reference:
Files: insert of Drive API v2
If I misunderstood your question and this didn't work, I apologzize.

Copying formats only from separate Google Sheet

I'm trying to copy formatting only from a source sheet that is in a different workbook than the current sheet. Essentially, the source sheet format changes and I'd like to pull updates to the formatting into my destination sheet (which is the current sheet I would be working in). I'm using the following code, but I get "Cannot convert Spreadsheet to (class)" error" on the last line. Any insights?
var ss = SpreadsheetApp.openById("Source ID")
var source = ss
var destination = SpreadsheetApp.openById("Destination ID");
var range = source.getRange("N3:AE50");
range.copyFormatToRange(destination, 10, 27, 4, 50);
You want to copy the format from Spreadsheet A to Spreadsheet B. If my understanding is correct, how about this workaround? I think that there are several answers for your situation, so please think of this as one of them.
As tehhowch pointed out, copyFormatToRange() can be used for the same sheets in a Spreadsheet. For this situation, I understood that you want to use for between 2 Spreadsheets. In my workaround, I used the following flow.
Copy the source sheet of Spreadsheet with "Source ID" to Spreadsheet with "Destination ID".
Retrieve the range from the copied sheet as the source range.
Copy the source range to the destination sheet using copyFormatToRange().
Delete the copied sheet.
Sample script :
var sourceA1Notation = "N3:AE50";
var destinationA1Notation = "D10"; // row=4, col=10 This is the cell of upper left for putting the source range.
var source = SpreadsheetApp.openById("Source ID");
var destination = SpreadsheetApp.openById("Destination ID");
var sourceSheet = source.getSheets()[0]; // You can also use source.getSheetByName(name)
var destinationSheet = destination.getSheets()[0]; // You can also use source.getSheetByName(name)
var copiedsheet = sourceSheet.copyTo(destination);
var sourceRange = copiedsheet.getRange(sourceA1Notation);
var destinationRange = destinationSheet.getRange(destinationA1Notation);
sourceRange.copyFormatToRange(
destinationSheet,
destinationRange.getColumn(),
destinationRange.getColumn() + sourceRange.getNumColumns() - 1,
destinationRange.getRow(),
destinationRange.getRow() + sourceRange.getNumRows() - 1
);
destination.deleteSheet(copiedsheet);
Note :
When you use this, please input and confirm sourceA1Notation, destinationA1Notation, "Source ID" and "Destination ID".
destinationA1Notation is the cell of upper left for putting the source range.
In this sample script, each first sheet of "Source ID" and "Destination ID" is used. If you want to use other sheet, please modify this script.
References :
copyFormatToRange()
getSheets()
getSheetByName()
If I misunderstand what you want, I'm sorry.

Google apps spreadsheet parents folder ID

I have a google spreadsheet that is keeping track of the files and their names that are inside the folder where the spreadsheet is.
I have looked and can not find how to get the location or ID of that sheet
In MS CMD script it would be "%~dp0".
How to do this in google apps spreadsheet script.
function FnctnMenuUpdateGetLocation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var SSID=ss.get.getId();
var file = DocsList.getFileById(SSID);
Browser.msgBox(SSID);
Browser.msgBox(add);
}
https://docs.google.com/spreadsheet/ccc?key=0AmsQN3N9km70dGwtLTJXVVBzbmJhdmE5OFpkbTVxelE&usp=sharing
ya, I am working in it
this is a live sheet
From what I have how do you get the address of the spreadsheet in google drive
from the spread sheet I will be having it add folder/dir and file that I need
since google docs is not google drive how is the interface from Spreadsheet to the google drive
This works as of June 2018
This answer also assumes that you are only interested in the first folder in the list of parents.
var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var spreadsheetFile = DriveApp.getFileById(spreadsheetId);
var folderId = spreadsheetFile.getParents().next().getId();
Both the Document Service and Drive Service can report which folder(s) a spreadsheet is in.
Here's your function, updated to show the name of a folder that contains the spreadsheet, using both services.
function FnctnMenuUpdateGetLocation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var SSID=ss.getId();
var fileInDocs = DocsList.getFileById(SSID);
var folderInDocs = fileInDocs.getParents()[0].getName();
var fileInDrive = DriveApp.getFolderById(SSID);
var folderinDrive = fileInDrive.getParents().next().getName();
Browser.msgBox("Docs says: " + folderInDocs +
", Drive says: " + folderinDrive);
}
Document Service's .getParents() method returns an array of folder objects, while Drive's returns an iterator, so the details of how to retrieve a specific parent folder(s) once you've got all of them differs.
Note that a spreadsheet may be "contained" in multiple folders... in this case, we've assumed that the first folder in the list is the only one - that assumption may be invalid.