google sheet script inserting data into sheet - google-apps-script

So i have a script the gets some data from a server.
I want my script to publish the data to a sheet name "market items".
I got this working if I'm running the script directly from the sheet by using =getMarketItemsTrigger(1).
It posts all 11,669 items to my sheet.
The problem with this is that it refreshes every time the sheet is reloaded; I need it to only run once a month.
I've been trying to create a script which needs no reference in the given sheet but posts directly to a pre-named sheet but I can't figure out how I can get the data into the sheet
this is the script file i'm using
var version = '9a'
function getMarketItemsTrigger(refresh)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Market Items");
Logger.log(sheet.getMaxColumns());
Logger.log(sheet.getMaxRows());
sheet.clear();
if(sheet.getMaxColumns()>2){
Logger.log('deleting colums');
sheet.deleteColumns(2, sheet.getMaxColumns()-2);
}
if(sheet.getMaxRows()>2){
Logger.log('deleting rows');
sheet.deleteRows(2,sheet.getMaxRows()-1);
}
var marketItemsEndpoint = 'https://crest-tq.eveonline.com/market/types/';
var marketItemsResponse = JSON.parse(fetchUrl(marketItemsEndpoint));
var totalPages = marketItemsResponse['pageCount'];
var itemList = [];
var headers = ['Item Name', 'ID'];
itemList.push(headers);
for (var currentPage = 1; currentPage <= totalPages; currentPage++)
{
Logger.log('Processing page ' + currentPage);
var marketItems = marketItemsResponse['items'];
for (var itemReference in marketItems)
{
var item = marketItems[itemReference];
itemList.push([item['type']['name'], item['id']]);
}
if (currentPage < totalPages)
{
var nextEndpoint = marketItemsResponse['next']['href'];
marketItemsResponse = JSON.parse(fetchUrl(nextEndpoint));
}
}
//sheet.insertRows(1,itemList.length+1);
// var range = sheet.getRange(1, 1,itemList.length+1,3);
// for(var i = 1;i<itemList.length;i++){
// range.getCell(i, 1).setValue([itemList]);
// range.getCell(1, i).setValue(itemList.);
// }
// Logger.log("don");
//sheet.getRange(1, 1, 1, itemList.length).setValues(itemList);
// sheet.getRange(itemList.length+1, 2).setValues(itemList);
// sheet.getDataRange().setValues([itemList]);
// sheet.appendRow(itemList);
// sheet.getRange(12+totalPages, 1, itemList.length, 1).setValues(itemList);
return itemList;
}
/**
* Private helper method that wraps the UrlFetchApp in a semaphore
* to prevent service overload.
*
* #param {url} url The URL to contact
* #param {options} options The fetch options to utilize in the request
*/
function fetchUrl(url)
{
if (gcsGetLock())
{
// Make the service call
headers = {"User-Agent": "Google Crest Script version " + version + " (/u/nuadi #Reddit.com)"}
params = {"headers": headers}
httpResponse = UrlFetchApp.fetch(url, params);
}
return httpResponse;
}
/**
* Custom implementation of a semaphore after LockService failed to support GCS properly.
* Hopefully this works a bit longer...
*
* This function searches through N semaphores, until it finds one that is not defined.
* Once it finds one, that n-th semaphore is set to TRUE and the function returns.
* If no semaphore is open, the function sleeps 0.1 seconds before trying again.
*/
function gcsGetLock()
{
var NLocks = 150;
var lock = false;
while (!lock)
{
for (var nLock = 0; nLock < NLocks; nLock++)
{
if (CacheService.getDocumentCache().get('GCSLock' + nLock) == null)
{
CacheService.getDocumentCache().put('GCSLock' + nLock, true, 1)
lock = true;
break;
}
}
}
return lock;
}
/**
* Private helper function that will check for a new version of GCS.
*/
function versionCheck()
{
var versionEndpoint = 'https://raw.githubusercontent.com/nuadi/googlecrestscript/master/version';
var newVersion = fetchUrl(versionEndpoint);
if (newVersion != null)
{
newVersion = newVersion.getContentText().trim();
Logger.log('Current version from Github: ' + newVersion);
var message = 'You are using the latest version of GCS. Fly safe. o7';
var title = 'No updates found';
if (newVersion > version)
{
message = 'A new version of GCS is available on GitHub.';
title = 'GCS version ' + newVersion + ' available!';
}
SpreadsheetApp.getActiveSpreadsheet().toast(message, title, 120);
}
}
All the code in the function getMarketItemsTrigger that's commented out is what I have tyred without luck .
The short version of this question is how can i post the values in itemList to column a and b in sheet market items

You can write the array itemList to the sheet by adding:
//your code
ss.getSheetByName('name_of_sheet_here')
.getRange(1, 1, itemList.length, itemList[0].length)
.setValues(itemList)
//more code (if needed)
} //end of code
-->> change sheet name and range to suit.

There are two ways to do this. If you did want it to run as a custom function those have access to the script property service. You could save a time stamp in the script properties and check it every time the custom function runs.
https://developers.google.com/apps-script/reference/properties/
https://developers.google.com/apps-script/guides/sheets/functions#using_apps_script_services
The second is to create a time trigger to run the code as a cron job every month.
https://developers.google.com/apps-script/guides/triggers/installable#time-driven_triggers
https://developers.google.com/apps-script/guides/triggers/installable#managing_triggers_manually

Related

Sheets Contained App Script Custom Function - results keep disappearing after several hours

I hope you can assist me here.
disclaimer: I'm new to Google Sheets and JavaScript, but have many years of programming experience.
I've got a pretty simple Custom Function in a sheet (Contained) that works absolutely fine if the Sheet is open in the browser. It basically takes in 5 arrays from the sheet and outputs 5 arrays from the cell from which it was called.
The sheet provides data to several other sheets that calculate from that data and then in turn share that to a further 'public' sheet that is then shared to a WordPress site.
ThisData -> CalculationSheet -> PublicViewSheet -> WordPress site
The problem kicks in after an hour or two after I close the Google Sheet in my browser, and I notice in the WordPress site that the data that was nicely displaying the leaderboard now outputs no athletes or their scores.
If I then:
merely open ThisData sheet and do nothing else, the calculated data is there and then all the athletes scores appears in the WordPress site 5 mins later due to caching.
instead open CalculationSheet or PublicViewSheet the 'QUERY( IMPORTRANGE(' that retrieves the data from ThisData retrieves no data (merely just the static headers).
I thought it might be the Google Sheets Custom Function caching problem and I've tried the trick of setting a cell to a Date/Time (see 'updateLastEditDate' in code below) and then referencing that cell through the score calculation (CalculateScores) as a dummy variable, and then setting up a trigger every hour to change the date which forces a recalculation... but I'm still getting the same disappearing results issues.
I've got a logger running to take a look at the output every time the CustomFunction runs, and when reviewing the logs, I can see that the output data is correct, but it seems that it's not getting written to the sheet. Or it might be getting written, but it's not showing further down the line ???
I guess I could rather change the CustomFunction to a function that is triggered onEdit as well as once a day (via triggers) and directly read the data from the sheet and then write it back... but a CustomFunction seems a neater solution, and allows me to move data ranges, add columns, etc without having to edit and hardwire the CalculateScores function.
I'm just scratching my head on why it's causing the above behavior... any ideas would be most welcome.
See code below: (for all it's worth... as it produces the expected output)
/** #OnlyCurrentDoc */
/**
* Calculates Precision Rifle scores..
*
* #param {lMatchNames}.
* #return {array} a full array of calculated scores.
* #customfunction
*/
function CalculateScores(lMatchNames, lAthleteIDs, lAthleteDivisions, lAthleteScores, lTieBreakScores, lTieBreakTimes, lDate ){
if(lMatchNames==undefined || lAthleteIDs==undefined || lAthleteDivisions==undefined || lAthleteScores==undefined || lTieBreakScores==undefined || lTieBreakTimes==undefined){
console.log("Setting null parameters");
var lMatchNames = [[]];
var lAthleteIDs = [[]];
var lAthleteDivisions = [[]];
var lAthleteScores = [[]];
var lTieBreakScores = [[]];
var lTieBreakTimes = [[]];
}else{
}
console.log(lNumMatches);
var lNumMatches = lMatchNames.length;
var lMatchData = [];
for (var i = 1; i < lNumMatches; i++) {
//Find Match Object
var lMatchName = lMatchNames[i][0];
var lAthleteID = lAthleteIDs[i][0];
var lAthleteDivision = lAthleteDivisions[i][0];
var lAthleteScore = lAthleteScores[i][0];
var lMatchObject = lMatchData.find(item => item.name == lMatchName);
if (lMatchObject === undefined) {
// Setup new match object in Array
lMatchObject = {
name:lMatchName,
combinedScores:[],
tieBreakScores:[],
};
lMatchData.push(lMatchObject);
}
// Add info to Match Object
if(lAthleteID!="0"){
addAndSort(lMatchObject.combinedScores, lAthleteScore);
var lTieBreakScore = (lAthleteScore * 100000000) + (lTieBreakScores[i][0] * 100000) - lTieBreakTimes[i][0]
addAndSort(lMatchObject.tieBreakScores, lTieBreakScore);
let lKeys = Object.keys(lMatchObject);
if(lKeys.indexOf(lAthleteDivision)==-1){
lMatchObject[lAthleteDivision]=[];
var lAthleteDivisionTieBreak = lAthleteDivision + "TieBreak";
lMatchObject[lAthleteDivisionTieBreak]=[];
}
addAndSort(lMatchObject[lAthleteDivision], lAthleteScore);
addAndSort(lMatchObject[lAthleteDivisionTieBreak], lTieBreakScore);
}
}
//Loop back through the data and calculate outputs
var lOutputArray = [];
lOutputArray.push(["Overall%", "OverallRank", "Division%", "DivisionRank", "RankTotal"]);
for (var i = 1; i < lNumMatches; i++) {
var lMatchName = lMatchNames[i][0];
if (lMatchName != ""){
var lAthleteID = lAthleteIDs[i][0];
var lAthleteDivision = lAthleteDivisions[i][0];
var lAthleteDivisionTieBreak = lAthleteDivision + "TieBreak";
var lAthleteScore = lAthleteScores[i][0];
var lTieBreakScore = (lAthleteScore * 100000000) + (lTieBreakScores[i][0] * 100000) - lTieBreakTimes[i][0]
if(lAthleteID=="0"){
var lOverallPercentage = "";
var lOverallRank = "";
var lDivisionPercentage = "";
var lDivisionRank = "";
var lTieBreakScore = "";
}else{
var lMatchObject = lMatchData.find(item => item.name == lMatchName);
var lOverallPercentage = lAthleteScore / lMatchObject.combinedScores[lMatchObject.combinedScores.length-1];
lOverallPercentage = Math.round(lOverallPercentage * 100000) / 100000;
var lOverallRank = lMatchObject.tieBreakScores.length - lMatchObject.tieBreakScores.lastIndexOf(lTieBreakScore);
var lDivisionPercentage = lAthleteScore / lMatchObject[lAthleteDivision][lMatchObject[lAthleteDivision].length-1];
lDivisionPercentage = Math.round(lDivisionPercentage * 100000) / 100000;
var lDivisionRank = lMatchObject[lAthleteDivisionTieBreak].length - lMatchObject[lAthleteDivisionTieBreak].lastIndexOf(lTieBreakScore);
}
lOutputArray.push([lOverallPercentage, lOverallRank, lDivisionPercentage, lDivisionRank, lTieBreakScore]);
}
}
console.log(lOutputArray);
return lOutputArray;
SpreadsheetApp.flush();
}
function addAndSort(arr, val) {
arr.push(val);
i = arr.length - 1;
item = arr[i];
while (i > 0 && item < arr[i-1]) {
arr[i] = arr[i-1];
i -= 1;
}
arr[i] = item;
return arr;
}
function onEdit() {
updateLastEditDate();
};
function updateLastEditDate() {
SpreadsheetApp.getActiveSpreadsheet().getRange('BC1').setValue(new Date().toTimeString());
};

Quicken google apps script so I can return success response within Shopify 5 second limit

I have this google apps script I wrote that I'm using as a web app as an endpoint for a Shopify webhook.
The issue I'm having is that Shopify has a 5 second limit to receive a success response otherwise the webhook will fire again to ensure you don't miss it.
The problem is my script takes too long to finish triggering a duplicate webhook which runs my code multiple times which I don't want.
Is there a way to respond quicker or clean up my script to finish quicker?
PLEASE NOTE: I need my script to be easily modified since exact values might change or be different in final version as I'm still developing this app. (additionally I need a way that if a value is missing it will leave that column blank, hence not mixing up value with column headers)
function doPost(e){
var data = JSON.parse(e.postData.contents);
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var l = data.line_items.length;
for (var i=0;i<l;i++){
var prop = data.line_items[i].properties;
if (prop.length>0){
var pdf = prop.find(function(x) {if(x.name == "_pdf") return x});
if (!pdf){pdf = "Prop not found";}else{pdf = pdf.value};
var shape = prop.find(function(x) {if(x.name.toLowerCase() == "shape") return x});
if (!shape){shape = "Prop not found";}else{shape = shape.value};
var test = prop.find(function(x) {if(x.name == "test") return x});
if (!test){test = "Prop not found";}else{test = test.value};
}else{
var pdf = "N/A"
var shape = "N/A"
var test = "N/A"
};
var count = "Item "+ (i+1) + " of " + l;
var qty = data.line_items[i].quantity;
var title = data.line_items[i].title;
var id = data.id.toString();
var email = data.email;
var totalPrice = data.total_price;
var discounts = data.total_discounts;
var acceptAds = data.buyer_accepts_marketing;
var orderStatus = data.order_status_url;
var addr = data.shipping_address.address1;
var city = data.shipping_address.city;
var state = data.shipping_address.province;
var zip = data.shipping_address.zip;
var phone = data.shipping_address.phone;
var firstName = data.shipping_address.first_name;
var lastName = data.shipping_address.last_name;
var orderNum = data.name;
var d = new Date(data.created_at).toLocaleString();
ss.appendRow([d,orderNum,email,count,title,shape,test,qty,totalPrice,discounts,pdf,firstName,lastName,addr,city,state,zip,phone,orderStatus]);
if (pdf != "N/A"){
if (pdf != "Prop not found"){
var res = UrlFetchApp.fetch(pdf);
var blob = res.getBlob();
var createFile = DriveApp.getFolderById('xxxxxxxxxxxxx').createFile(blob.getAs('application/pdf'));
var fileName = orderNum + " " + qty;
createFile.setName(fileName);
}}
};
}
It's slower than using the PropertiesService, but I like using Sheets as a queue. (I use this with services that require responses within 3 seconds.) Not only is it easier to work with, but I've actually had issues with using Properties that are addressed with the appendRow() method:
Appends a row to the spreadsheet. This operation is atomic; it prevents issues where a user asks for the last row, and then writes to that row, and an intervening mutation occurs between getting the last row and writing to it.
When you receive the POST data, simply add it to the queue and terminate. Apps Script will send a 200 success response, so Shopify shouldn't send duplicate requests.
Then have a time-driven trigger that runs a processQueue() function at the interval of your choice.
function doPost(e) {
const queue = new Queue(SpreadsheetApp.getActive().getId(), "Unprocessed", "Processed");
queue.append(e.postData.contents, skipRefresh = true);
}
function processQueue() {
const queue = new Queue(SpreadsheetApp.getActive().getId(), "Unprocessed", "Processed");
while (queue.hasNext()) {
try {
const data = JSON.parse(queue.next());
doSomethingWithShopifyData(data); // Process your data
queue.moveToProcessed();
} catch (error) {
console.error(error);
queue.skip();
}
}
}
function doSomethingWithShopifyData(data) { /* your existing code, but with appropriate modifications */ }
Here's the class I use to abstract the spreadsheet into a queue. I have it setup to preserve all of the data moving it from an unprocessed to a processed sheet. You may prefer to simply delete the data once processed.
/**
* A spreadsheet is used as a makeshift queue for processing requests asynchronously.
* #param {string} spreadsheetId - The ID of the spreadsheet to be used for the queue.
* #param {string} unprocessedSheetName - The name of the sheet to be used for storing unprocessed items.
* #param {string} processedSheetName - The name of the sheet to be used for storing processed items.
*/
class Queue {
constructor(spreadsheetId, unprocessedSheetName, processedSheetName) {
this.index = 0;
this.row = 1;
this.spreadsheet = SpreadsheetApp.openById(spreadsheetId);
this.unprocessedSheet = this.spreadsheet.getSheetByName(unprocessedSheetName);
this.processedSheet = this.spreadsheet.getSheetByName(processedSheetName);
}
/**
* Determines whether calling next() will return an item.
* #returns {boolean}
*/
hasNext() {
if (this.unprocessedValues == null) { this.refreshUnprocessedValues(); }
return this.index < this.unprocessedValues.length;
}
/**
* Get and save the unprocessed element values to the queue.
* #returns {object[]}
*/
refreshUnprocessedValues() {
try {
const range =this.unprocessedSheet.getRange(1, 1, this.unprocessedSheet.getLastRow());
this.unprocessedValues = range.getValues();
} catch (error) {
this.unprocessedValues = [];
}
return this.unprocessedValues;
}
/**
* Get the next element from the queue.
* #returns {string}
*/
next() {
return this.unprocessedValues[this.index++][0];
}
/**
* Skip the current queue element. Update row property to maintain synchronization
* with the spreadsheet range.
*/
skip() {
this.row++;
}
/**
* Add new data to the queue for processing.
* #param {string} data - The data to add to the queue.
* #param {boolean} [skipRefresh] - Default: false. If true, will skip refreshing the queue values.
*/
append(data, skipRefresh) {
this.unprocessedSheet.appendRow([data]);
if (!skipRefresh) { this.refreshUnprocessedValues(); }
}
/**
* Move a payload out of the unprocessed sheet and into the processed sheet. Uses the payload
* at the top of the unprocessed range.
*/
moveToProcessed() {
const cell = this.unprocessedSheet.getRange(this.row, 1);
// Move into processed sheet
this.processedSheet.appendRow([cell.getValue()]);
// Move out of unprocessed sheet
cell.deleteCells(SpreadsheetApp.Dimension.ROWS);
}
}

User Drive's usage without shared files

I'd like to create a report of storage usage for all my users. To do so I use AdminReports app, like so (found in google example, somewhere. Just had to adapt the "parameters" and the "row" arrays) :
function generateUserUsageReport() {
var today = new Date();
var oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
var timezone = Session.getScriptTimeZone();
var date = Utilities.formatDate(oneWeekAgo, timezone, 'yyyy-MM-dd');
var parameters = [
'accounts:gmail_used_quota_in_mb',
'accounts:drive_used_quota_in_mb',
'accounts:total_quota_in_mb ',
'accounts:used_quota_in_percentage'
];
var rows = [];
var pageToken;
var page;
do {
page = AdminReports.UserUsageReport.get('all', date, {
parameters: parameters.join(','),
maxResults: 500,
pageToken: pageToken
});
if (page.warnings) {
for (var i = 0; i < page.warnings.length; i++) {
var warning = page.warnings[i];
Logger.log(warning.message);
}
}
var reports = page.usageReports;
if (reports) {
for (var i = 0; i < reports.length; i++) {
var report = reports[i];
var parameterValues = getParameterValues(report.parameters);
var row = [
report.date,
report.entity.userEmail,
parseInt(parameterValues['accounts:drive_used_quota_in_mb']),
parseInt(parameterValues['accounts:gmail_used_quota_in_mb']),
parseInt(parameterValues['accounts:total_quota_in_mb']),
((parseInt(parameterValues['accounts:gmail_used_quota_in_mb'])+parseInt(parameterValues['accounts:drive_used_quota_in_mb']))/parseInt(parameterValues['accounts:total_quota_in_mb']))*100
];
rows.push(row);
}
}
pageToken = page.nextPageToken;
} while (pageToken);
if (rows.length > 0) {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
// Append the headers.
var headers = [['Date', 'User mail', 'Drive use','Gmail use', 'Total available',
'Total(%)']];
sheet.getRange(1, 1, 1, 6).setValues(headers);
// Append the results.
sheet.getRange(2, 1, rows.length, 6).setValues(rows);
Logger.log('Report spreadsheet created: %s', spreadsheet.getUrl());
} else {
Logger.log('No results returned.');
}
}
/**
* Gets a map of parameter names to values from an array of parameter objects.
* #param {Array} parameters An array of parameter objects.
* #return {Object} A map from parameter names to their values.
*/
function getParameterValues(parameters) {
return parameters.reduce(function(result, parameter) {
var name = parameter.name;
var value;
if (parameter.intValue !== undefined) {
value = parameter.intValue;
} else if (parameter.stringValue !== undefined) {
value = parameter.stringValue;
} else if (parameter.datetimeValue !== undefined) {
value = new Date(parameter.datetimeValue);
} else if (parameter.boolValue !== undefined) {
value = parameter.boolValue;
}
result[name] = value;
return result;
}, {});
}
The issue I have is that the parameters "accounts:drive_used_quota_in_mb" gives you the drive usage WITH the shared files (which is irrelevant to calculate the storage used by a user ( to determine whether he needs more space or not)).
I even tried to use 'accounts:used_quota_in_percentage' which seemed to be exactly what I need, but it calculate the percentage the same way i do : ((drive + mail)/total space)*100, and no way to ignore shared files to do so.
I'm working on the possibility to check every files of the drive, but you know the next problem : slowness.. (just for 1User with few docs, it take 1-2minutes)
Is there a way to do so by script, with another class, or something that is done for it in google that I didn't see ?
Thanks for your reading, forgive my english.
Ok, there is no issue, I just freaked out because a user was at 30Go consumption although he has his account for only 2month.
But after discussing with him, he did upload heavy files one week ago, and since, he deleted it.
And executing the script for only 2days ago gives the correct result, since he deleted these files between these two dates.
The reason of my mistake is that my script was providing stats that was 1 Week old (without me being conscious of that), and I was checking the veracity of theses stats on the web interface, that incidates nowadays stats.

How to export Mixpanel raw data to Google Sheet with Mixpanel's API?

Here is a solution to get raw data from Mixpanel to fill a Google spreadsheet. (It's not really a question :-) )
Get your Mixpanel Api secret key: https://help.mixpanel.com/hc/en-us/articles/115004490503-Project-Token-API-Key-API-Secret
Create google spreadsheet or open existing one.
2.1 Open the scripts editor
[Tools] -> [Script Editor]
2.2 Create fonction e.g: exportRawData(){ }
2.3 Prepare an Http Header like that:
/**
* Call Mixpanel raw export endpoint
**/
exportRawData(){
// Prepare header with the secret key
var API_SECRET = "Replace by your Secret Key";
var headers = {
"Authorization" : "Basic " + Utilities.base64Encode(API_SECRET)
};
var params = {
"method":"GET",
"headers":headers
};
// prepare call args
// WARNING: date are yyyy-mm-dd format
// replace fixed date by dynamic range (now - 7 days or other)
var url = "https://data.mixpanel.com/api/2.0/export/?from_date=2018-11-07&to_date=2018-11-14";
Logger.log('Call Mixpanel:' + url);
var response = UrlFetchApp.fetch(url, params);
var rawData = response.getContentText();
Logger.log('Response length:' + rawData.length);
var items = rawData.split("\n");
// prepare the array that it will be pushed into the google spreadsheet
var structuredData; // the json decoded data
var row;
var outer = []; // array of rows
for ( i in items ){
structuredData = JSON.parse( items[i] );
// In this example, we take Mobile usages only
// with Event, Browser, User
if ( structuredData.properties.$browser.indexOf('Mobile') >= 0 ){
row = new Array(structuredData.event, structuredData.properties.$browser, structuredData.properties.distinct_id);
// push the single row into the array of rowS
outer.push(row);
} //eo fi
} //eo rof
pushInSpreadsheet('Mobile Usage', outer);
} // end of exportRawData
/**
* Push data to the Google Spreadsheet
* sheetName: String
* data: Array of rows
* return: nothing
**/
function pushInSpreadsheet(sheetName, data){
Logger.log("push " + data.length + " item(s) into to sheet " + sheetName);
// Prepare the sheet into the active spread sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// select of exist or create new one
var sheet = ss.getSheetByName(sheetName) ||
ss.insertSheet(sheetName, 1);
sheet.clear();
// The expected structure for the horizontal range setting is:
// [ ["cell A1"],["cell B1"],["cell C1"] ]
var range = sheet.getRange(1, 1, data.length, data[0].length );
range.setValues( data );
}
I hope that it will be helpful. Any comments are welcome.

Creating Google Docs file by getting data from Google Sheets.

I am an experienced programmer who isn't experienced with using the script editor of Google Drive.
Since I need to make some reports, I was wondering about ways to exploit the script functionale of Google Drive to ease my process.
So my goal is there's this format that I created in Words, and for some parts of the Words, I need to put in each student's score. However, as doing this manually is very demanding, i was wondering ways to utilize google sheets and google docs for this.
So I was wondering if there's a way for me to get certain data from the spreadsheet (one column for each doc) and put the numbers in the appropriate space in the google docs file, and save it in google drive or send it as an email. Then, I will repeat this process for each column in the spreadsheet until everyone's report has been created.
If you professional programmers can help me out here it will be deeply appreciated. I never had any experience with google script editor and I do not know where to start. Thank you!
You may check this Script for generating Google documents from Google spreadsheet data source tutorial.
/**
* Generate Google Docs based on a template document and data incoming from a Google Spreadsheet
*
* License: MIT
*
* Copyright 2013 Mikko Ohtamaa, http://opensourcehacker.com
*/
// Row number from where to fill in the data (starts as 1 = first row)
var CUSTOMER_ID = 1;
// Google Doc id from the document template
// (Get ids from the URL)
var SOURCE_TEMPLATE = "xxx";
// In which spreadsheet we have all the customer data
var CUSTOMER_SPREADSHEET = "yyy";
// In which Google Drive we toss the target documents
var TARGET_FOLDER = "zzz";
/**
* Return spreadsheet row content as JS array.
*
* Note: We assume the row ends when we encounter
* the first empty cell. This might not be
* sometimes the desired behavior.
*
* Rows start at 1, not zero based!!! 🙁
*
*/
function getRowAsArray(sheet, row) {
var dataRange = sheet.getRange(row, 1, 1, 99);
var data = dataRange.getValues();
var columns = [];
for (i in data) {
var row = data[i];
Logger.log("Got row", row);
for(var l=0; l<99; l++) {
var col = row[l];
// First empty column interrupts
if(!col) {
break;
}
columns.push(col);
}
}
return columns;
}
/**
* Duplicates a Google Apps doc
*
* #return a new document with a given name from the orignal
*/
function createDuplicateDocument(sourceId, name) {
var source = DocsList.getFileById(sourceId);
var newFile = source.makeCopy(name);
var targetFolder = DocsList.getFolderById(TARGET_FOLDER);
newFile.addToFolder(targetFolder);
return DocumentApp.openById(newFile.getId());
}
/**
* Search a paragraph in the document and replaces it with the generated text
*/
function replaceParagraph(doc, keyword, newText) {
var ps = doc.getParagraphs();
for(var i=0; i<ps.length; i++) {
var p = ps[i];
var text = p.getText();
if(text.indexOf(keyword) >= 0) {
p.setText(newText);
p.setBold(false);
}
}
}
/**
* Script entry point
*/
function generateCustomerContract() {
var data = SpreadsheetApp.openById(CUSTOMER_SPREADSHEET);
// XXX: Cannot be accessed when run in the script editor?
// WHYYYYYYYYY? Asking one number, too complex?
//var CUSTOMER_ID = Browser.inputBox("Enter customer number in the spreadsheet", Browser.Buttons.OK_CANCEL);
if(!CUSTOMER_ID) {
return;
}
// Fetch variable names
// they are column names in the spreadsheet
var sheet = data.getSheets()[0];
var columns = getRowAsArray(sheet, 1);
Logger.log("Processing columns:" + columns);
var customerData = getRowAsArray(sheet, CUSTOMER_ID);
Logger.log("Processing data:" + customerData);
// Assume first column holds the name of the customer
var customerName = customerData[0];
var target = createDuplicateDocument(SOURCE_TEMPLATE, customerName + " agreement");
Logger.log("Created new document:" + target.getId());
for(var i=0; i<columns.length; i++) {
var key = columns[i] + ":";
// We don't replace the whole text, but leave the template text as a label
var text = customerData[i] || ""; // No Javascript undefined
var value = key + " " + text;
replaceParagraph(target, key, value);
}
}
As #James Donnellan stated, please check the official documentation on how to use the service which allows scripts to create, access, and modify Google Sheets files.