Can I list all the times I saved a file? - google-apps-script

My company uses Google Drive and we are still mainly using Microsoft Office documents.
Is it possible to see my activity or each time I saved a document in the Shared Drive even though it is not Google Documents I want to see the activity of?
I found this link https://developers.google.com/apps-script/advanced/drive-activity , but the code only returns Google Docs activity. Not non-Google documents like Word and Excel.

You might want to check if using Revisions.list will fit your needs.
Revisions.list
Lists the current file's revisions.
Path parameters:
fileId - The ID of the file.
Optional query parameters:
fields - The paths of the fields you want included in the response. If not specified, the response includes a default set of fields specific to this method. For development you can use the special value * to return all fields, but you'll achieve greater performance by only selecting the fields you need. For more information, see Return specific fields for a file.
pageSize - The maximum number of revisions to return per page. Acceptable values are 1 to 1000, inclusive. (Default: 200)
pageToken - The token for continuing a previous list request on the next page. This should be set to the value of 'nextPageToken' from the previous response.
Response Body:
{
"kind": "drive#revisionList",
"nextPageToken": string,
"revisions": [
revisions Resource
]
}
Revisions Resource Representation:
You can obtain useful information related to the revision done to the file such as the modified time and last modifying user.
{
"kind": "drive#revision",
"id": string,
"mimeType": string,
"modifiedTime": datetime,
"keepForever": boolean,
"published": boolean,
"publishedLink": string,
"publishAuto": boolean,
"publishedOutsideDomain": boolean,
"lastModifyingUser": {
"kind": "drive#user",
"displayName": string,
"photoLink": string,
"me": boolean,
"permissionId": string,
"emailAddress": string
},
"originalFilename": string,
"md5Checksum": string,
"size": long,
"exportLinks": {
(key): string
}
}
You can specify specific fields in your request under fields parameter so that only necessary information can be shown in the response body:
Sample Fields Parameter:
nextPageToken, revisions/id, revisions/modifiedTime, revisions/lastModifyingUser/displayName, revisions/lastModifyingUser/emailAddress
Sample Response Body:
{
"revisions": [
{
"id": "1898",
"modifiedTime": "2020-12-16T22:29:02.971Z",
"lastModifyingUser": {
"displayName": "User1 Test",
"emailAddress": "user1#example.com"
}
}
]
}

Play with DriveApps file.getLastUpdated(). This is not the same as ALL the times you've updated it but it should get the last time the file was changed. https://developers.google.com/apps-script/reference/drive/file#getLastUpdated()
Or do you really need a list of all the edit times not just the most recent one? In that case you could run a script once a day that records the lastUpdated for all the files you care about or all the files in a folder and record if they've changed. What is the use case?

Thanks for that Ron. Revisions did the trick.
It took me a while to wrap my head around it, but these are the 2 functions I used. I will try to put them together at some point when I have Drive API v3.
function listFilesInFolder() {
// get the id's of files in a folder
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow(["Name", "File-Id"]);
var folder = DriveApp.getFolderById(" ID STRING ");
var contents = folder.getFiles();
var counter = 0;
var file;
while (contents.hasNext()) {
var file = contents.next();
counter++;
data = [
file.getName(),
file.getId(),
];
sheet.getRange("C2").setValue = counter
sheet.appendRow(data);
};
};
function fileactivity() {
var revs = Drive.Revisions.list(" FileID String ");
var savedList = [];
for(var i=0; i<revs.items.length; i++) {
var revision = revs.items[i];
// modifiedByMeTime requires Drive API v3
savedList.push([revision.kind, revision.modifiedDate]);
};
var sheet = SpreadsheetApp.getActiveSheet();
Logger.log(savedList);
}

Related

Google Apps Scripting Issue

1) I am trying to create a complete script that will take input data from a google form/google sheet and use the top row as as field names for a Freshdesk ticket while taking the last row (most current) as the data to input as the ticket data ..
2) I have coded onFormSubmit portion so it creates the general ticket however i would the data from the backend to be used as the certain field data
function onFormSubmit(e) {
if ((typeof GasFreshdesk)==='undefined') {
eval(UrlFetchApp.fetch('https://raw.githubusercontent.com/zixia/gas-freshdesk/master/src/gas-freshdesk-lib.js').getContentText())
}
var MyFreshdesk = new GasFreshdesk('https://***.freshdesk.com', 'API KEY'); // REPLACE redacted with real key
// Custom fields passed in. Use this for now - to test (then replace later with other valid values).
var customFields = {"email": "firstnamelastname#org.org",
"room": "IT",
"building": "**",
"devicesystem": "Device",
"problem": "problem"
};
var ticket = new MyFreshdesk.Ticket({description: "We have provided you with a loaner",
subject: "** - Device - Problem- " + "FullName", email: "firstnamelastname#org.org",
type: "Support Request", custom_fields: customFields});
}
What i'm looking for is a way to grab the values in the last row of data since that data is what will need to be inputted into the ticket.
function LastRow() {
var ss= SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
var lr = getLastRow();
Logger.log(lr);
}

Creating a YouTube Video or Channel ID validation in Apps Script

I am learning how to use the YouTube Data API through Apps Script. I am now trying to modify the example provided here https://developers.google.com/youtube/v3/quickstart/apps-script to verify if the ID exists.
I slightly modified it to look at video IDs since that is more common.
With help by #Tanaike based on my last question, Tanaike helped me create a loop to allow the script to add IDs as comma-separated. Now I'm trying to add a validation.
function channelsListByVideoid(part,params){
var response = YouTube.Videos.list(part,params);
var video = response.items[0];
var dataRow = [video.id,video.status.uploadStatus, video.snippet.title, response.pageInfo.totalResults, video.snippet.channelId, video.snippet.channelTitle];
SpreadsheetApp.getActiveSheet();
SpreadsheetApp.getActiveSpreadsheet().getSheetByName().appendRow(dataRow);
function getvideo() {
var ui = SpreadsheetApp.getUi();
var videoId = ui.prompt("Enter the YouTube Video ID: ").getResponseText();
var values = videoId.split(",");
for (var i = 0; i < values.length; i++) {
channelsListByVideoid('snippet,status,contentDetails,statistics', {'id': values[i].trim()});
Sometimes, a video ID such as https://www.youtube.com/watch?v=--fPZOu_H8g is not available. Running that ID within the apps-script example will throw an error TypeError: Cannot read property 'id' of undefined which will stop the script from running.
I was thinking about how to use video.list.response Property:
{
"kind": "youtube#videoListResponse",
"etag": etag,
"nextPageToken": string,
"prevPageToken": string,
"pageInfo": {
"totalResults": integer,
"resultsPerPage": integer
},
"items": [
video Resource
]
}
The totalResults and the resultsPerPage will show as 1 if the video is valid and 0 if invalid:
"pageInfo": {
"totalResults": 0,
"resultsPerPage": 0
which can be verified through the API explorer (https://developers.google.com/youtube/v3/docs/videos/list?apix_params=%7B%22part%22%3A%22snippet%2CcontentDetails%2Cstatistics%22%2C%22id%22%3A%22--fPZOu_H8g%22%7D)
I added ````
response.pageInfo.totalResults,
Logically to me, I need to add a
````var response````
to my getvideo() and add the validation check within the loop.
I'm not clear if you can write it as simply as
if response.pageinfo.totalResults = 1 then "valid" ELSE "Invalid"
and then how to allow the code to skip the error that is generated.
Thank you!

Last modified date in Google App Script GAS sheets

I am trying to get the last modified date of a sheet from a GAS add on which I am developing.
My current idea is to get the Drive revision list and then take the last value. This seems a bit overkill for just getting the last modified, I am also worried that this will break if the number of revisions exceeds 1000 as per this link suggests.
https://developers.google.com/drive/api/v3/reference/revisions/list
Ideally I would like to know the range which has changed too, but I do not think this is possible.
I cannot use the onEdit event because I would like to track edits made by users who have not installed the add-on.
var fileId = SpreadsheetApp.getActiveSpreadsheet().getId();
var revisions = Drive.Revisions.list(fileId);
var revisionLength = revisions.items.length;
if(revisionLength > 0){
var revision = revisions.items[revisionLength-1];
var date = new Date(revision.modifiedDate);
Logger.log(date.toString());
}
I believe you can do that as follow
var lastUpdated = DriveApp.getFileById(fileId).getLastUpdated();
See function reference
Given that you need users of your add-on to have access to revision information from non-add-on users, the Drive revision list is precisely what you need. Happily, you can get the content of revisions, so if you wish you can compute diffs. I don't know what your data looks like, so that might be easy or nigh-impossible.
Aside: to your point about more than 1000 revisions, if there are more than 1000 (or whatever your page size is) revisions, you'll get a nextPageToken like so:
{
"kind": "drive#revisionList",
"nextPageToken": "BHNMJKHJKHKVJHyugaiohasdzT1JyUmlQWG10RUJ1emx1S2xNDg4EgQzMzY1GAI=",
"revisions": [
...
]
}
If you see that you'll need to list revisions again, providing that token.
Anyway, when you list revisions, each revision will look something like this:
{
"kind": "drive#revision",
"etag": "\"som3-e-tAg\"",
"id": "3365",
"selfLink": "https://www.googleapis.com/drive/v2/files/dummydummydummy/revisions/3365",
"mimeType": "application/vnd.google-apps.spreadsheet",
"modifiedDate": "2018-10-19T19:05:41.762Z",
"published": false,
"exportLinks": {
"application/x-vnd.oasis.opendocument.spreadsheet": "https://docs.google.com/spreadsheets/export?id=dummydummydummy&revision=3365&exportFormat=ods",
"text/tab-separated-values": "https://docs.google.com/spreadsheets/export?id=dummydummydummy&revision=3365&exportFormat=tsv",
"application/pdf": "https://docs.google.com/spreadsheets/export?id=dummydummydummy&revision=3365&exportFormat=pdf",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "https://docs.google.com/spreadsheets/export?id=dummydummydummy&revision=3365&exportFormat=xlsx",
"text/csv": "https://docs.google.com/spreadsheets/export?id=dummydummydummy&revision=3365&exportFormat=csv",
"application/zip": "https://docs.google.com/spreadsheets/export?id=dummydummydummy&revision=3365&exportFormat=zip",
"application/vnd.oasis.opendocument.spreadsheet": "https://docs.google.com/spreadsheets/export?id=dummydummydummy&revision=3365&exportFormat=ods"
},
"lastModifyingUserName": "Joe User",
"lastModifyingUser": {
"kind": "drive#user",
"displayName": "Joe User",
"picture": {
"url": "https://lh3.googleusercontent.com/-asdfsadf/AAAAAAAAAAI/AAAAAAAAFOk/OIPUYOIUGO/s64/photo.jpg"
},
"isAuthenticatedUser": true,
"permissionId": "123456789",
"emailAddress": "user#gmail.com"
}
}
Provided your data isn't insanely complex or large, you could fetch the target the text/csv export link for the revisions you wish to compare, and then do that comparison in Apps Script.
That might look something like:
var fileId = SpreadsheetApp.getActiveSpreadsheet().getId();
var revisions = Drive.Revisions.list(fileId);
var revisionLength = revisions.items.length;
if(revisionLength > 1) { // something to compare!
var revision = revisions.items[revisionLength-1];
var newContent = UrlFetchApp.fetch(revision.exportLinks["text/csv"]).getContent();
newContent = Utilities.parseCsv(newContent);
var oldRevision = revisions.items[revisionLength-2];
var oldContent = UrlFetchApp.fetch(oldRevision.exportLinks["text/csv"]).getContent();
oldContent = Utilities.parseCsv(oldContent);
# TODO check they're the same size!
# where do they differ?
for (var row = 0; row < newContent.length; row++) {
for (var col = 0; col < newContent[0].length; col++) {
if (newContent[row][col] != oldContent[row][col]) {
Logger.log('Change on row ' + (row + 1) + ' column ' + (col + 1));
}
}
# when did it change?
var date = new Date(revision.modifiedDate);
Logger.log(date.toString());
}

Drive.Files.Copy and "parents" not working

I'm trying to copy a file in Team Drives to a new folder location, also in Team Drives. I get a "File not found" error on the last line of code. The newFileID has been checked using DriveApp.getFileByID and by testing in Google API Try-It.
I think the "parents" piece is incorrectly formed. When I try Google API Try-It, the file is copied into the correct folder. Yay! So what's wrong with the Google Script code?
https://developers.google.com/drive/api/v3/reference/files/copy#try-it
Google Script code (not working):
function test() {
// find Teacher's Learner Guides folder
var destinationFolderId = "1qQJhDMlHZixBO9KZkkoSNYdMuqg0vBPU";
var newFile = {
"name": "Learner Guide - test",
"description": "New student learner guide",
"parents": [destinationFolderId]
};
// create duplicate document
var newFileID = "1g6cjUn1BWVqRAIhrOyXXsTwTmPZ4QW6qGhUAeTHJSUs";
var newDoc = Drive.Files.copy(newFile, newFileID);
}
The Google API Try-It code works. Here's the javascript (working):
return gapi.client.drive.files.copy({
"fileId": "1g6cjUn1BWVqRAIhrOyXXsTwTmPZ4QW6qGhUAeTHJSUs",
"supportsTeamDrives": true,
"resource": {
"parents": [
"1qQJhDMlHZixBO9KZkkoSNYdMuqg0vBPU"
],
"name": "Learner Test2"
}
})
What would be an efficient and/or correct way of using Drive.Files.Copy in Google Script code to place the copied file into a different folder?
The parents metadata associated with the request expects a ParentReference resource for Drive API v2, which is at minimum an object with an id property and the associated fileId, e.g. {id: "some id"}.
Since you are working with Team Drives, you must tell Google that you (i.e. your code) know how to handle the associated differences between regular & Team Drives, with the supportsTeamDrives optional parameter.
Note:
A parent does not appear in the parents list if the requesting user is a not a member of the Team Drive and does not have access to the parent. In addition, with the exception of the top level folder, the parents list must contain exactly one item if the file is located within a Team Drive.
Assuming the code runner meets the criteria, the most simple code to copy a given file to a given Team Drive folder is:
function duplicate_(newName, sourceId, targetFolderId) {
if (!newName || !sourceId || !targetFolderId)
return;
const options = {
fields: "id,title,parents", // properties sent back to you from the API
supportsTeamDrives: true, // needed for Team Drives
};
const metadata = {
title: newName,
// Team Drives files & folders can have only 1 parent
parents: [ {id: targetFolderId} ],
// other possible fields you can supply:
// https://developers.google.com/drive/api/v2/reference/files/copy#request-body
};
return Drive.Files.copy(metadata, sourceId, options);
}
Additional reading:
Standard Query Parameters (these can always be passed in the optional argument)
Partial Responses (aka "fields")
Here's the solution for copying files in Team Drives. #tehhowch had an important piece about needing the optional parameters (you need to use all three parameters for copy API v2). Then the "parents" argument requires a File object, not a string. The code below works by copying the file and moving it into another Team Drives folder.
function test() {
// find Teacher's Learner Guides folder
var destFolderId = "1qQJhDMlHZixBO9KZkkoSNYdMuqg0vBPU";
var originalDocID = "1g6cjUn1BWVqRAIhrOyXXsTwTmPZ4QW6qGhUAeTHJSUs";
var destFolder = Drive.Files.get(destFolderId, {"supportsTeamDrives": true});
var newFile = {
"fileId": originalDocID,
"parents": [
destFolder // this needed to be an object, not a string
]
};
var args = {
"resource": {
"parents": [
destFolder // this needed to be an object, not a string
],
"title": "new name of document here"
},
"supportsTeamDrives": true
};
// create duplicate Learner Guide Template document
var newTargetDoc = Drive.Files.copy(newFile, originalDocID, args);
}

Looping JSON response in google script

We are trying to work out how to loop through a JSON response.
We have managed to call the API of our 3rd party database and pull the first line (the headers) but need to loop through all the rows and then copy them to a google sheets.
Any ideas?
Not too much information on what information is in the JSON you're receiving or how you're going to handle it, so here's my general answer:
Once you receive the full JSON data, you can turn it into an object by using JSON.parse( jsonString ) where jsonString is the data you received from the API. More on that here.
If your row values are stored in an array, you can easily loop through them using the forEach() method. More on that here. Below is example JSON data and a function to parse through it.
Example Data
{
"name": "Example Data",
"rows": [
{
"string": "I'm a string",
"number": 14
},
{
"string": "Chicago",
"number": 36
}
]
}
Example Parse Function
function handleJsonResponse(data) {
//Parse response and get sheet
var response = JSON.parse(data);
var spreadsheet= SpreadsheetApp.getActive().getSheetByName(response.name);
if (spreadsheet === null) {
//Error here
}
//Loop through data and add it to spreadsheet
response.rows.forEach(function( row, index ) {
//This function will be executed for every row in the rows array
//Set the index of the row to the first column in the sheet
//2 is added to the index for the row number because index starts at 0 and we want to start adding data at row 2
spreadsheet.getRange(index + 2, 1).setValue(index);
//Set the value of string to the second column
spreadsheet.getRange(index + 2, 2).setValue(row.string);
//Set the value of number to the third column
spreadsheet.getRange(index + 2, 3).setValue(row.number);
});
}
If you have any questions feel free to ask.