Google script to login, retrieve cookie and get csv to sheets - csv

I can get the cookie with curl -d "_u=user_name" -d "_p=password" --cookie-jar ./cookie https://url.tologin.com/admin/login successfully but can't do the same with google script
function myFunction() {
var payload = {"_u" : "user_name","_p" : "password"};
var opt ={"payload":payload,"method":"post"};
var response = UrlFetchApp.fetch("https://url.tologin.com/admin/login",opt);
var headers = response.getAllHeaders();
Logger.log(headers);
var cookie = headers['Set-Cookie'];
Logger.log(cookie);
Logger.log(response);
response.getContentText();
var header = {'Cookie':cookie};
var opt2 = {"headers":header};
var pagedata = UrlFetchApp.fetch("https://url.tologin.com/admin/sales/order/export/csv",opt2);
Logger.log(pagedata);
}
I get answer from webserver with failed login page and I can't figure out what is wrong with google script

As written in the MDN documentation, The request cookie header should be of the format:
Cookie: name=value; name2=value2; name3=value3
A list of name-value pairs in the form of <cookie-name>=<cookie-value>. Pairs in the list are separated by a semicolon and a space (;).
As written in the UrlFetchApp#HttpResponse documentation, getAllHeaders() returns
Returns an attribute/value map of headers for the HTTP response, with headers that have multiple values returned as arrays.
When a array is implicitly converted to a string, it is joint using a comma , ,which is not a valid cookie string. You could get a formatted cookie header from a Set-cookie header using a strip function like this:
const getCookie = setCookie =>
Array.isArray(setCookie)
? setCookie.map(getCookie).join("; ") //get each cookie and join them by ;
: setCookie.split("; ")[0]//get only the first part of cookie; remove irrelevant info like `Max-Age`, `expires` etc.

I got everything working
GAS script to
Login and fetch cookie with POST
build a newCookie and use it in GET requests
Get CSV and parse it to replace separators when necessary
Upload CSV content to an active sheet
function parseCsvResponse(csvString) {
var retArray = [];
var strLines = csvString.split(/\n/g);
var strLineLen = strLines.length;
for (var i = 0; i < strLineLen; i++) {
var line = strLines[i];
if (line != '') {
retArray.push(line.replace(/"/g, "").split(/;/));
//replace ; with separator from your CSV file
}
}
return retArray;
}
function myFunction(sheet) {
var payload = {'user_name' : 'username','password' : 'password'};
//replace with values on your login page "name=user_name" and "name=password"
//if your username contains # send it directly or use %40 instead
var opt ={
'payload': payload,
'method':'post',
"followRedirects": false,
"testcookie": 1
};
var response = UrlFetchApp.fetch("https://url.tologin.com/admin/login",opt);
//inspect the right link via Chrome inspect of your login page
Logger.log(response);
Logger.log(response.getResponseCode());
if ( response.getResponseCode() == 200 ) {
// Incorrect user/pass combo
} else if ( response.getResponseCode() == 302 ) {
// Logged-in
Logger.log("Logged in");
var headers = response.getAllHeaders();
Logger.log(headers);
var cookies = headers['Set-Cookie'];
// Extract the cookies from the login response
var cookieParts = [];
for (var i = 0; i < cookies.length; i++) {
var arr = cookies[i].split('; ');
cookieParts.push(arr[0]);
}
// Create a new cookie to send with subsequent requests
var newCookie = cookieParts.join('; ');
Logger.log(newCookie);
};
opt2 = {
"method" : "get",
"headers": {
"Cookie": newCookie
}
};
var url = "https://url.tologin.com/admin/sales/order/export/csv";
response2 = UrlFetchApp.fetch(url, opt2);
var resp1=response2.getContentText();
var csvContent = parseCsvResponse(response2.getContentText());
Logger.log(resp1);
Logger.log(csvContent);
// clear everything in the sheet
var sheet = SpreadsheetApp.getActiveSheet();
sheet.clearContents().clearFormats();
// set the values in the sheet (as efficiently as we know how)
sheet.getRange(1, 1, csvContent.length /* rows */, csvContent[0].length /* columns */).setValues(csvContent);
}
then
Import myFunction to sheet "Tools > Macros > Import"
First run asks for Google authentication of your App where myFunction is
Run myFunction "Tools > Macros > myFunction"
And you get correctly formatted CSV data to your sheet

Related

Copy data from download URL using Google Script

I'm new to App scripts and need help with copying the data to spreadsheet from URL.
However, URL is not a website but link which after clicking with directly download csv file into the computer. Also, its not ending with .csv as I have seen in other examples here.
URL basically coming to my inbox at a specific time. I'm trying to use Fetch URL but its not working at all.
Sample URL -
https://docs.google.com/spreadsheets/d/1oPUPPUmy7psliSznUItT0DnHvilXwZHzyrmdyHpHi18/export?format=csv
function ABC () {
const searchQuery = 'XYZ';
const threads = GmailApp.search(searchQuery, 0,1);
const urls = [];
threads.forEach(thread => {
const messages = thread.getMessages();
messages.forEach(message => {
const body = message.getBody();
var re = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'"".,<>?«»“”‘’]))/i;
const match = body.match(re);
if (match) { urls.push(match[1]); }
});
}) ;
Logger.log(urls);
url = urls.toString().replace("[","").replace("]","") ;
Logger.log(url);
function getData() {
var attValue = '';
// making a call to the target website
var response = UrlFetchApp.fetch(url);
//logging response from target website - In Script Editor > View > Logs
Logger.log(response.getContentText());
//parsing the response data from website
//https://developers.google.com/apps-script/reference/url-fetch/http-response
var rawData = response.getContentText();
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[1]);
var cell = sheet.getRange(1, 1);
cell.setValue(rawData);
}
};
Kindly help so that I can copy the data directly into spreadsheet or store the file in Google Drive with filename as combination of text and date.
Thanks
SUGGESTION
You can try the tweaked script below.
In my understanding, here is your goal:
Get your email messages that contain URLs (CSV file) via "XYZ" search terms.
Process the URL using URLFetchApp service
Place the CSV data into your second sheet tab.
Note: If there's anything else missing or something may have been misunderstood, feel free to let me know.
Tweaked Script
function ABC() {
/**TWEAKED: Created a function call method called "getData" */
const url = {
getData: function () {
const searchQuery = 'XYZ';
const threads = GmailApp.search(searchQuery, 0, 1);
const urls = [];
threads.forEach(thread => {
const messages = thread.getMessages();
messages.forEach(message => {
const body = message.getBody();
var re = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'"".,<>?«»“”‘’]))/i;
const match = body.match(re);
if (match) { urls.push(match[1]); }
});
});
Logger.log(urls);
/**TWEAKED: Instead of using the redundant replace method,
* used "regex" inside a single replace method to replace
* all [ and ] characters */
var geturl = urls.toString().replace(/\[|]/gm, "");
console.log(geturl)
return geturl;
}
}
var attValue = '';
/**TWEAKED: Call the "url" variable's "getData" function that will return the URL */
var response = UrlFetchApp.fetch(url.getData.call());
//logging response from target website - In Script Editor > View > Logs
Logger.log(response.getContentText());
//parsing the response data from website
//https://developers.google.com/apps-script/reference/url-fetch/http-response
var rawData = response.getContentText();
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[1]);
var cell = sheet.getRange(1, 1);
cell.setValue(rawData);
};
Demonstration
After running the ABC() function on the Apps Script editor, the second sheet tab gets populated with the CSV data:
The Apps Script execution log view
References:
JavaScript Function call()

Google Apps Script API iterate/ loop through ID individually

i have an endpoint that i need to go through X number of times (dependent on how many IDs), Each call will need to assign its individual LineItem ID and bring back a JSON response.
I have tried the following code, and it seems I can call the API but can't seem to figure out how to translate the response back to my sheet, so in the case below i may have upto 10 LI ids that will need to be called up individually > results brought back> copied to last row of a particular range and then the next API call with the next LI id, etc...
function ListLI360API_Agetest(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('MySheet');
var adID = 1558211;
var LIs =sheet.getRange(2, 3, sheet.getLastRow(), 1).getValues().filter(String);
var LIArrayLength = LIs.length;
for (var i = 0; i <= LIArrayLength; i++) {
if(LIs[i]!== undefined){
var url = 'https://displayvideo.googleapis.com/v1/advertisers/'+adID+'/lineItems/'+LIs[i]+'/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions'
//Logger.log(url);
var response = callApi5 (url, 'GET');
//Logger.log(response);
var content = response.getContentText();
//Logger.log(content);
var json = JSON.parse(content);
//Logger.log(json);
var ageData = json["assignedTargetingOptions"];
//Logger.log(ageData);
var rows = [],
data;
for (i = 0; i <= ageData.length; i++) {
data = ageData[i];
rows.push([data.name]);
}
//save results to spreadsheet in the next blank column and then API for next LI ID
Logger.log(rows);
}
}//endfor
}
I seem to be getting stuck on reading the results, i have tried with the following added into the script above but i get an error
"TypeError: Cannot read property "name" from undefined", im guessing there are some nulls/ blanks being returned in the JSON and hence it cant read the length
JSON looks like...
[20-06-24 21:34:57:159 BST] {
"assignedTargetingOptions": [
{
"name": "advertisers/1558211/lineItems/36917016/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503004",
"assignedTargetingOptionId": "503004",
"targetingType": "TARGETING_TYPE_AGE_RANGE",
"inheritance": "NOT_INHERITED",
"ageRangeDetails": {
"ageRange": "AGE_RANGE_45_54",
"targetingOptionId": "503004"
}
},
{
"name": "advertisers/1558211/lineItems/36917016/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503005",
"assignedTargetingOptionId": "503005",
"targetingType": "TARGETING_TYPE_AGE_RANGE",
"inheritance": "NOT_INHERITED",
"ageRangeDetails": {
"ageRange": "AGE_RANGE_55_64",
"targetingOptionId": "503005"
}
},
{
"name": "advertisers/1558211/lineItems/36917016/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503006",
"assignedTargetingOptionId": "503006",
"targetingType": "TARGETING_TYPE_AGE_RANGE",
"inheritance": "NOT_INHERITED",
"ageRangeDetails": {
"ageRange": "AGE_RANGE_65_PLUS",
"targetingOptionId": "503006"
}
}
]
}
[20-06-24 21:34:57:694 BST] {
"assignedTargetingOptions": [
{
"name": "advertisers/1558211/lineItems/36917017/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503004",
"assignedTargetingOptionId": "503004",
"targetingType": "TARGETING_TYPE_AGE_RANGE",
"inheritance": "NOT_INHERITED",
"ageRangeDetails": {
"ageRange": "AGE_RANGE_45_54",
"targetingOptionId": "503004"
}
},
{
"name": "advertisers/1558211/lineItems/36917017/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503005",
"assignedTargetingOptionId": "503005",
"targetingType": "TARGETING_TYPE_AGE_RANGE",
"inheritance": "NOT_INHERITED",
"ageRangeDetails": {
"ageRange": "AGE_RANGE_55_64",
"targetingOptionId": "503005"
}
},
{
"name": "advertisers/1558211/lineItems/36917017/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503006",
"assignedTargetingOptionId": "503006",
"targetingType": "TARGETING_TYPE_AGE_RANGE",
"inheritance": "NOT_INHERITED",
"ageRangeDetails": {
"ageRange": "AGE_RANGE_65_PLUS",
"targetingOptionId": "503006"
}
}
]
}
From this Example there are 2 LI Ids so 2 separate outputs, i need to take parts of these outputs and print them into the spreadsheet
API function looks like...
function callApi5(url, methodType, requestBody) {
var service = getService();
if (service.hasAccess()) {
var headers = {
'Content-Type': 'application/json',
'Accept' :'application/json',
'Authorization': 'Bearer ' + getService().getAccessToken()
};
var options = {
method: methodType,
headers : headers,
muteHttpExceptions: true
};
if (requestBody) {
options.payload = requestBody;
}
return UrlFetchApp.fetch(url, options);
} else {
var authorizationUrl = service.getAuthorizationUrl();
Logger.log('Open the following URL and re-run the script: %s',
authorizationUrl);
}
}
function getService() {
// Create a new service with the given name. The name will be used when
// persisting the authorized token, so ensure it is unique within the
// scope of the property store.
return OAuth2.createService('MyService')
// Set the endpoint URLs, which are the same for all Google services.
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the client ID and secret, from the Google Developers Console.
.setClientId("xxxxx.apps.googleusercontent.com")
.setClientSecret("xxxxxx")
// Set the name of the callback function in the script referenced
// above that should be invoked to complete the OAuth flow.
.setCallbackFunction('authCallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Set the scopes to request (space-separated for Google services).
// this is blogger read only scope for write access is:
.setScope('https://www.googleapis.com/auth/display-video')
// Below are Google-specific OAuth2 parameters.
// Sets the login hint, which will prevent the account chooser screen
// from being shown to users logged in with multiple accounts.
.setParam('login_hint', 'xxxx#xxxs.com')
// Requests offline access.
.setParam('access_type', 'offline')
// Forces the approval prompt every time. This is useful for testing,
// but not desirable in a production application.
.setParam('approval_prompt', 'force');
}
I believe your goal as follows.
You want to retrieve the values from all requests, which used the URLs created by 'https://displayvideo.googleapis.com/v1/advertisers/'+adID+'/lineItems/'+LIs[i]+'/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions', and put them to the Spreadsheet.
For this, how about this answer? From your question, I thought that your script of callApi5() works and json of var json = JSON.parse(content); is the value you showed in your question. So I would like to propose to modify the function of ListLI360API_Agetest.
Modification points:
When the array is used in the for loop, please loop from 0 to array.length - 1. Because the 1st index of array is 0. So, when for (var i = 0; i <= LIArrayLength; i++) is used, an error occurs at the last loop of LIArrayLength. In this case, please modify to for (var i = 0; i < LIArrayLength; i++). Also, this can be said for for (i = 0; i <= ageData.length; i++) {.
In your script, 1 for loop is included in the for loop. And, each loop uses the variable i. In this case, the variables of i of each loop are affected. By this, the loop cannot be correctly worked.
I think that your error of TypeError: Cannot read property "name" from undefined might be due to above 2 points.
LIs of var LIs =sheet.getRange(2, 3, sheet.getLastRow(), 1).getValues().filter(String); is 2 dimensional array. So in this case, I think that LIs[i][0] is suitable instead of LIs[i].
When above points are reflected to your script, it becomes as follows.
Modified script:
Please copy and paste the following script, and set the destination sheet name to the last line of ss.getSheetByName("###").getRange(1, 10, result.length, 1).setValues(result);.
function ListLI360API_Agetest(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('MySheet'); // Modified
var adID = 1558211;
var LIs = sheet.getRange(2, 3, sheet.getLastRow(), 1).getValues().filter(String);
var LIArrayLength = LIs.length;
var result = []; // Added
for (var i = 0; i < LIArrayLength; i++) { // Modified
if (LIs[i][0] !== undefined) { // Modified
var url = 'https://displayvideo.googleapis.com/v1/advertisers/'+adID+'/lineItems/'+LIs[i][0]+'/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions' // Modified
var response = callApi5 (url, 'GET');
var content = response.getContentText();
var json = JSON.parse(content);
var ageData = json["assignedTargetingOptions"];
for (var j = 0; j < ageData.length; j++) { // Modified
var data = ageData[j];
result.push([data.name]); // Modified
}
}
}
// Please set the destination sheet name.
ss.getSheetByName("###").getRange(1, 1, result.length, 1).setValues(result); // Added
}
If data.name is not existing, you don't want to put the values, please modify result.push([data.name]); to if (data.name) result.push([data.name]);.
Note:
In this modified script, it supposes that the structure of JSON object retrieved from each URL is the same. If the structure is different for each URL created by LIs[i][0], it is required to modify the script. Please be careful this.
I couldn't understand the result situation that the values are put to the Spreadsheet from your question. So in this modified script, the values are put to the destination sheet. If this is different from your actual situation, please modify the script.
References:
Array
getValues()
I have tested the answer provided by Tanike and modified the last part to be able to print to the spreadsheet. I have added a few more fields from JSON to test this, and finally added:
dataRange = sheet.getRange(lr+1, 17, result.length,result[0].length).setValues(result);
to print onto the spreadhseet.
function ListLI360API_Agetest(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('MySheet'); // Modified
var adID = 1558211;
var LIs = sheet.getRange(2, 3, sheet.getLastRow(), 1).getValues().filter(String);
var LIArrayLength = LIs.length;
var result = []; // Added
var lr = sheet.getRange('Q1').getDataRegion().getLastRow(); //Added
for (var i = 0; i < LIArrayLength; i++) { // Modified
if (LIs[i][0] !== undefined) { // Modified
var url = 'https://displayvideo.googleapis.com/v1/advertisers/'+adID+'/lineItems/'+LIs[i][0]+'/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions' // Modified
var response = callApi5 (url, 'GET');
var content = response.getContentText();
var json = JSON.parse(content);
var ageData = json["assignedTargetingOptions"];
for (var j = 0; j < ageData.length; j++) { // Modified
var data = ageData[j];
result.push([
data.name,
data.assignedTargetingOptionId,
data.ageRangeDetails.ageRange]); // Modified
}
}
}
// Each Set of results is pushed one after another
dataRange = sheet.getRange(lr+1, 17, result.length,result[0].length).setValues(result);//Modified
}

How to escape "/" character in Google Script

I'm trying to bring Wordpress data to Google Sheets.
The script below worked for almost all columns but I can't bring the emails and if I try to scape the "-" my script won't run.
function getPage(offset,per_page) {
// gets posts in chunks of per_page
var ui = SpreadsheetApp.getUi(); // used for error messages
var ss = SpreadsheetApp.getActiveSpreadsheet();
var options = {
'method': 'get',
"contentType" : "application/json",
'muteHttpExceptions': true
}
var apiHost = 'https://domain.com.br/wp-json'; // set to your own domain
url = apiHost + '/acf/v3/cadastros?per_page='+per_page+'&offset='+offset;
try {
var response = UrlFetchApp.fetch(url, options);
var data = JSON.parse(response)
// loop through the map and output to sheet
for (i = 0; i < data.length; i++) {
row=offset+i+2; // set the row to make sure it is below header and takes into account the paging
ss.getRange('A'+row).setValue(data[i].id);
ss.getRange('B'+row).setValue(data[i].acf.contato);
ss.getRange('C'+row).setValue(data[i].acf.e-mail); //this line is breaking everything I've tried to use "\", put quotes but nothing seems to work
ss.getRange('D'+row).setValue(data[i].acf.telefone);
ss.getRange('E'+row).setValue(data[i].acf.cnpj);
ss.getRange('F'+row).setValue(data[i].acf.endereco);
ss.getRange('G'+row).setValue(data[i].acf.principais_produtos);
ss.getRange('H'+row).setValue(data[i].acf.volume_disponivel);
ss.getRange('I'+row).setValue(data[i].acf.estoque_disponivel);
ss.getRange('J'+row).setValue(data[i].acf.aceite);
}
return data.length;
} catch(error) {
var result = ui.alert( error.toString());
}
return 0;
}
How do I fix this?
Quick and easy will be to replace data[i].acf.e-mail with data[i]['acf']['e-mail']

How to use Google Photos API Method: mediaItems.search in Google apps script for a spreadsheet

I really tried to figure this out on my own...
I am trying to load photo metadata from google photos into a sheet using the Google Photos API and google apps script.
I was able to make some progress after a lot of help on a previous question
Is it possible to load google photos metadata into google sheets?
I now have two functions.
function photoAPI_ListPhotos() - Uses Method: mediaItems.list and gives me all my photos that are not archived
function photoAPI_ListAlbums() - Uses Method: albums.list and gives me all my albums
What I want to do is retrieve all photos from a specific album. Method: mediaItems.search should do this but it uses the POST protocol and the previous working examples I found only use GET. Looking at the examples available on that page, there is a javascript portion but it does not work in apps script.
The documentation for UrlFetchApp tells me how to format a POST request but not how to add the parameters for authentication.
The external APIs also is not giving me the examples I am looking for.
I feel like I'm missing some essential tiny piece of info and I hope I'm not wasting everyone's time asking it here. Just a solid example of how to use POST with oauth in apps script should get me where I need to go.
Here is my working function for listing all non-archived photos.
function photoAPI_ListPhotos() {
/*
This function retrieves all photos from your personal google photos account and lists each one with the Filename, Caption, Create time (formatted for Sheet), Width, Height, and URL in a new sheet.
it will not include archived photos which can be confusing if you happen to have a large chunk of archived photos some pages may return only a next page token with no media items.
Requires Oauth scopes. Add the below line to appsscript.json
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]
Also requires a standard GCP project with the appropriate Photo APIs enabled.
https://developers.google.com/apps-script/guides/cloud-platform-projects
*/
//Get the spreadsheet object
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Check for presence of target sheet, if it does not exist, create one.
var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length);
//Make sure the target sheet is empty
photos_sh.clear();
var narray = [];
//Build the request string. Max page size is 100. set to max for speed.
var api = "https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=100";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
//This variable is used if you want to resume the scrape at some page other than the start. This is needed if you have more than 40,000 photos.
//Uncomment the line below and add the next page token for where you want to start in the quotes.
//var nexttoken="";
var param= "", nexttoken;
//Start counting how many pages have been processed.
var pagecount=0;
//Make the first row a title row
var data = [
"Filename",
"description",
"Create Time",
"Width",
"Height",
"ID",
"URL",
"NextPage"
];
narray.push(data);
//Loop through JSON results until a nextPageToken is not returned indicating end of data
do {
//If there is a nextpagetoken, add it to the end of the request string
if (nexttoken)
param = "&pageToken=" + nexttoken;
//Get data and load it into a JSON object
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
//Check if there are mediaItems to process.
if (typeof json.mediaItems === 'undefined') {
//If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken
//var data = ["","","","","","","",json.nextPageToken];
//narray.push(data);
} else {
//Loop through the JSON object adding desired data to the spreadsheet.
json.mediaItems.forEach(function (MediaItem) {
//Check if the mediaitem has a description (caption) and make that cell blank if it is not present.
if(typeof MediaItem.description === 'undefined') {
var description = "";
} else {
var description = MediaItem.description;
}
//Format the create date as appropriate for spreadsheets.
var d = new Date(MediaItem.mediaMetadata.creationTime);
var data = [
MediaItem.filename,
"'"+description, //The prepended apostrophe makes captions that are dates or numbers save in the sheet as a string.
d,
MediaItem.mediaMetadata.width,
MediaItem.mediaMetadata.height,
MediaItem.id,
MediaItem.productUrl,
json.nextPageToken
];
narray.push(data);
});
}
//Get the nextPageToken
nexttoken = json.nextPageToken;
pagecount++;
//Continue if the nextPageToaken is not null
//Also stop if you reach 400 pages processed, this prevents the script from timing out. You will need to resume manually using the nexttoken variable above.
} while (pagecount<4 && nexttoken);
//Continue if the nextPageToaken is not null (This is commented out as an alternative and can be used if you have a small enough collection it will not time out.)
//} while (nexttoken);
//Save all the data to the spreadsheet.
photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
You want to retrieve all photos of the specific album using Google Photo API.
You want to know how to use the method of mediaItems.search using Google Apps Script.
You have already been able to retrieve the data using Google Photo API.
If my understanding is correct, how about this sample script? Please think of this as just one of several answers.
Sample script 1:
var albumId = "###"; // Please set the album ID.
var headers = {"Authorization": "Bearer " + ScriptApp.getOAuthToken()};
var url = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
var mediaItems = [];
var pageToken = "";
do {
var params = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify({albumId: albumId, pageSize: 100, pageToken: pageToken}),
}
var res = UrlFetchApp.fetch(url, params);
var obj = JSON.parse(res.getContentText());
Array.prototype.push.apply(mediaItems, obj.mediaItems);
pageToken = obj.nextPageToken || "";
} while (pageToken);
Logger.log(mediaItems)
At the method of mediaItems.search, albumId, pageSize and pageToken are included in the payload, and the values are sent as the content type of application/json.
Sample script 2:
When your script is modified, how about the following modified script?
function photoAPI_ListPhotos() {
var albumId = "###"; // Please set the album ID.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length);
photos_sh.clear();
var narray = [];
var api = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var nexttoken = "";
var pagecount = 0;
var data = ["Filename","description","Create Time","Width","Height","ID","URL","NextPage"];
narray.push(data);
do {
var options = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify({albumId: albumId, pageSize: 100, pageToken: nexttoken}),
}
var response = UrlFetchApp.fetch(api, options);
var json = JSON.parse(response.getContentText());
if (typeof json.mediaItems === 'undefined') {
//If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken
//var data = ["","","","","","","",json.nextPageToken];
//narray.push(data);
} else {
json.mediaItems.forEach(function (MediaItem) {
if(typeof MediaItem.description === 'undefined') {
var description = "";
} else {
var description = MediaItem.description;
}
var d = new Date(MediaItem.mediaMetadata.creationTime);
var data = [
MediaItem.filename,
"'"+description,
d,
MediaItem.mediaMetadata.width,
MediaItem.mediaMetadata.height,
MediaItem.id,
MediaItem.productUrl,
json.nextPageToken
];
narray.push(data);
});
}
nexttoken = json.nextPageToken || "";
pagecount++;
} while (pagecount<4 && nexttoken);
photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
Note:
This script supposes as follows.
Google Photo API is enabed.
The scope of https://www.googleapis.com/auth/photoslibrary.readonly or https://www.googleapis.com/auth/photoslibrary are included in the scopes.
Reference:
Method: mediaItems.search
If I misunderstood your question and this was not the result you want, I apologize.

Generate unique Form URL for each respondent

I have a Google Form With a Google spreadsheet to store responses. In my spread sheet I have 4 columns: name, email, revenue, and a fourth column, Id, which is used to identify the particular recipient.
What I am trying to accomplish is to generate a unique URL for each respondent so that they can respond to the form and use the same URL to edit the form at a later time.
I've looked at the getEditUrl() (Google Apps Script) method which creates a unique URL for the respondent after submitting the response-- code below:
function myFunction() {
assignEditUrls();
}
function assignEditUrls() {
var form = FormApp.openById('1vsqvwomoqSXwF6TlNkmmktAAk2av2r-2LRrBYJdv3VQ');
//enter form ID here
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form Responses');
//Change the sheet name as appropriate
var data = sheet.getDataRange().getValues();
var urlCol = 5; // column number where URL's should be populated; A = 1, B = 2 etc
var responses = form.getResponses();
var timestamps = [], urls = [], resultUrls = [];
for (var i = 0; i < responses.length; i++) {
var resp = responses[i];
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(shortenUrl(responses[i].getEditResponseUrl()));
withItemResponse(responses[i])
}
for (var j = 1; j < data.length; j++) {
var dop = data[j][0]
resultUrls.push([data[j][0]?urls[timestamps.indexOf(data[j][0].setMilliseconds(0))]: '']);
}
sheet.getRange(2, urlCol, resultUrls.length).setValues(resultUrls);
}
function shortenUrl(longUrl) {
// google url shortener api key
var key = "AIzaSyBVG4Q5i1mNI0YAO0XVGZ3suZU8etTvK34";
var serviceUrl="https://www.googleapis.com/urlshortener/v1/url?key="+key;
var options={
muteHttpExceptions:true,
method:"post",
contentType: "application/json",
payload : JSON.stringify({'longUrl': longUrl })
};
var response=UrlFetchApp.fetch(serviceUrl, options);
if(response.getResponseCode() == 200) {
var content = JSON.parse(response.getContentText());
if ( (content != null) && (content["id"] != null) )
return content["id"];
}
return longUrl;
}
However I want to do it the other way which is to first generate the unique URL to be then sent to respondents so they can submit and edit their responses without the need of sending them another URL (e.g. the editurlresponse).
Is this possible to do?
Originally posted to https://webapps.stackexchange.com/a/86399/88163
Yes, it's possible, but with slight different approach:
Submit one answer for each respondent in order to get one edit URL by each of them, then send the corresponding URL to each respondent.
Below is a code snippet that programmatically submits a response and log some response attributes including the edit response url by through two code lines, the first one has an issue, the second one is a workaround. Please note that these lines use getEditResponseUrl() not the toPrefilledUrl().
It will work as a stand alone or as a bounded script.
/*
This code shows how to get the edit response url of a
programmatically submitted response to a Google Form
*/
// Replace the form ID by your own form
var formID = '1234567890abcdefghijklmnopqrstuvwxyz';
function myFunction() {
var form = FormApp.openById(formID);
var response = form.createResponse();
var items = form.getItems();
var item = items[0];
if (item.getType() == 'TEXT') {
var textItem = item.asTextItem();
var itemResponse = textItem.createResponse('my text');
response.withItemResponse(itemResponse);
}
// Submit response
var submittedResponse = response.submit();
// Get submitted response attributes
var values = {
ID : submittedResponse.getId(),
TS : submittedResponse.getTimestamp(),
/*
Issue 4476: FormApp: getEditResponseUrl() produces invalid URL
https://code.google.com/p/google-apps-script-issues/issues/detail?id=4476
*/
ER1 : submittedResponse.getEditResponseUrl(),
/* Workaround from
https://code.google.com/p/google-apps-script-issues/issues/detail?id=4476#c2
*/
ER2 : submittedResponse.getEditResponseUrl().replace(
/\?edit2=.*/,"?edit2=" + submittedResponse.getId()
)
};
Logger.log(values);
}
References
Class FormResponse - Google Apps Script Reference
Issue 4476: FormApp: getEditResponseUrl() produces invalid URL