From 79168811c5840dd66047e9573ed7ada94293513c Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Mon, 13 May 2013 09:20:19 -0300 Subject: [PATCH 01/12] added license info --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 84da9dc..699186d 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,4 @@ http://developers.google.com/apps-script http://script.google.com +All code in this folder released under [Apache 2.0 license](http://www.apache.org/licenses/LICENSE-2.0). From 98205dac6578560da90b15d12d6dda2fa2fc19e7 Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Wed, 15 May 2013 12:48:37 -0400 Subject: [PATCH 02/12] Sample code for YouTube Analytics + Apps Script talk --- IO2013/YouTubeAnalytics/Code.gs | 60 +++++++++++ IO2013/YouTubeAnalytics/oauth2.gs | 118 ++++++++++++++++++++++ IO2013/YouTubeAnalytics/oauthsuccess.html | 12 +++ IO2013/YouTubeAnalytics/twitter.gs | 32 ++++++ IO2013/YouTubeAnalytics/ui.html | 113 +++++++++++++++++++++ 5 files changed, 335 insertions(+) create mode 100644 IO2013/YouTubeAnalytics/Code.gs create mode 100644 IO2013/YouTubeAnalytics/oauth2.gs create mode 100644 IO2013/YouTubeAnalytics/oauthsuccess.html create mode 100644 IO2013/YouTubeAnalytics/twitter.gs create mode 100644 IO2013/YouTubeAnalytics/ui.html diff --git a/IO2013/YouTubeAnalytics/Code.gs b/IO2013/YouTubeAnalytics/Code.gs new file mode 100644 index 0000000..8a48e38 --- /dev/null +++ b/IO2013/YouTubeAnalytics/Code.gs @@ -0,0 +1,60 @@ +function onOpen(){ + SpreadsheetApp.getActiveSpreadsheet() + .addMenu('YouTube Analytics',[{name:'Ad hoc report', functionName:'showUI'}, + {name: 'Twitter trending', functionName:'twitter'}]); +} + +function showUI(){ + var ui = HtmlService.createTemplateFromFile('ui').evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE).setHeight(350); + SpreadsheetApp.getActiveSpreadsheet().show(ui); +} + +function sendNightlyEmail(){ + var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Published Dashboard'); + var chart = sheet.getCharts()[0]; + MailApp.sendEmail(Session.getActiveUser().getEmail(), 'Daily YouTube Sharing report', 'See attached image', {name:'Sharing chart',attachments:[chart.getBlob()]}); +} + +function alertOnViewsIncrease(){ + //perform business logic here to compare against previously stored values/thresholds +} + +function doGet(e) { + var HTMLToOutput; + if(e.parameter.code){//if we get "code" as a parameter in, then this is a callback. we can make this more explicit + getAndStoreAccessToken(e.parameters.code); + HTMLToOutput = HtmlService.createHtmlOutputFromFile('oauthsuccess').getContent(); + } + else if(isTokenValid()){//we already have a valid token but this should never start here. + HTMLToOutput = '

Invalid access

'; + } + else {//we are starting from scratch but this should never start here. + HTMLToOutput = "

Invalid access

"; + } + return HtmlService.createHtmlOutput(HTMLToOutput).setSandboxMode(HtmlService.SandboxMode.NATIVE); +} + + +function getData(startdate,enddate,metrics,dimensions){ + var ss = SpreadsheetApp.getActiveSpreadsheet(); + //these are the channels and contentOwner settings we care for - + var ids = 'contentOwner==promo-pso-brazil'; + var filters = 'channel==UCEN58iXQg82TXgsDCjWqIkg'; + + var sheet = ss.insertSheet(); + ss.setActiveSheet(sheet); + var base_url = 'https://www.googleapis.com/youtube/analytics/v1/reports?'; + var getDataURL = base_url + 'ids='+ids+'&start-date='+startdate+'&end-date='+enddate+'&metrics='+metrics+'&dimensions='+dimensions+'&filters='+filters; + Logger.log(getDataURL); + + var dataResponse = UrlFetchApp.fetch(getDataURL,getUrlFetchOptions()).getContentText(); + var dataObj = JSON.parse(dataResponse); + var headers = []; + for(var i = 0;dataObj.columnHeaders && i +YouTube Analytics + + + + Ad hoc Report Generator + +

+Thank you for authorization the application. Please close this window and return to the spreadsheet. +

+Close window +

diff --git a/IO2013/YouTubeAnalytics/twitter.gs b/IO2013/YouTubeAnalytics/twitter.gs new file mode 100644 index 0000000..be7deec --- /dev/null +++ b/IO2013/YouTubeAnalytics/twitter.gs @@ -0,0 +1,32 @@ +function twitter(){ + var oauthCfg = UrlFetchApp.addOAuthService('twitter'); + oauthCfg.setAccessTokenUrl('https://api.twitter.com/oauth/access_token'); + oauthCfg.setRequestTokenUrl('https://api.twitter.com/oauth/request_token'); + oauthCfg.setAuthorizationUrl('https://api.twitter.com/oauth/authorize'); + oauthCfg.setConsumerKey(ScriptProperties.getProperty('TWITTER_CLIENT_ID')); + oauthCfg.setConsumerSecret(ScriptProperties.getProperty('TWITTER_CLIENT_SECRET')); + var options = {oAuthServiceName:'twitter',oAuthUseToken:'always'}; + + var WOEIDs = {}; + WOEIDs['United States'] = '23424977'; + WOEIDs['Brazil'] = '23424768' + WOEIDs['South Africa'] = '23424942' + WOEIDs['United Kindom'] = '23424975'; + WOEIDs['Canada'] = '23424775' + + var headers = []; + var ss = SpreadsheetApp.getActiveSpreadsheet(); + + for(var woe in WOEIDs){ + var url = 'https://api.twitter.com/1.1/trends/place.json?id='+WOEIDs[woe]; //eg. all = 1, london = 44418 + var response = UrlFetchApp.fetch(url, options).getContentText(); + + var myObject = JSON.parse(response)[0]; + var trends = [] + for(var i = 0;myObject.trends && i +YouTube Analytics + + + + + + + + + + Ad hoc Report Generator + + +
+

+Please click on the link below to authorize application to the YouTube Analytics API. This page will automatically refresh when you return. +

+Authorize app
+

+
+ + + +
+ +
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ +
+ + +
+
From 460a3fcf521a517066284bd7cf91b940783cff13 Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Wed, 15 May 2013 13:52:41 -0300 Subject: [PATCH 03/12] create readme.md --- IO2013/YouTubeAnalytics/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 IO2013/YouTubeAnalytics/README.md diff --git a/IO2013/YouTubeAnalytics/README.md b/IO2013/YouTubeAnalytics/README.md new file mode 100644 index 0000000..b58a707 --- /dev/null +++ b/IO2013/YouTubeAnalytics/README.md @@ -0,0 +1,9 @@ +Companion code for Google IO 2013 session - Ad Hoc Analysis with Google Apps Script and YouTube Analytics API + +https://developers.google.com/events/io/sessions/328316141 + +For setup - + +You'll need to create OAuth 2 client ID/secret at http://developers.google.com/console and store it in the right ScriptProperties. + +Similarly for the Twitter sample, you'll need to create a Key/Secret with the Twitter API. From 09a771fe38131cc8fac161045e0997f25c0d571c Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Thu, 16 May 2013 13:51:55 -0400 Subject: [PATCH 04/12] added code from drive talk --- IO2013/Drive/Code.gs | 108 ++++++++++++++++++ IO2013/Drive/SpreadsheetUtils.gs | 189 +++++++++++++++++++++++++++++++ IO2013/Drive/ui.html | 53 +++++++++ 3 files changed, 350 insertions(+) create mode 100644 IO2013/Drive/Code.gs create mode 100644 IO2013/Drive/SpreadsheetUtils.gs create mode 100644 IO2013/Drive/ui.html diff --git a/IO2013/Drive/Code.gs b/IO2013/Drive/Code.gs new file mode 100644 index 0000000..1e473d9 --- /dev/null +++ b/IO2013/Drive/Code.gs @@ -0,0 +1,108 @@ +function doGet(e) { + var HTMLToOutput; + if(e.parameter.state){ + var state = JSON.parse(e.parameter.state); + if(state.action === 'create'){ + var newFolder = DriveApp.createFolder('New Assignment'); + + var templateQuiz = DriveApp.getFileById(QUIZ_TEMPLATE); + var templateRoster = DriveApp.getFileById(ROSTER_TEMPLATE); + + var newQuiz = templateQuiz.makeCopy('Quiz Questions'); + newFolder.addFile(newQuiz); + DriveApp.getRootFolder().removeFile(newQuiz); + + var newRoster = templateRoster.makeCopy('Roster'); + newFolder.addFile(newRoster); + DriveApp.getRootFolder().removeFile(newRoster); + + var objectToSaveForClose = { type: 'close', + rosterID : newRoster.getId()} + + var objectToSaveForPublish = { type: 'publish', + assignmentFolderID : newFolder.getId(), + rosterID : newRoster.getId(), + quizID : newQuiz.getId()} + + var closeBlob = Utilities.newBlob(JSON.stringify(objectToSaveForClose), 'application/drive-assignment-creator', 'Close Quiz'); + var publishBlob = Utilities.newBlob(JSON.stringify(objectToSaveForPublish), 'application/drive-assignment-creator', 'Publish Quiz'); + newFolder.createFile(closeBlob); + newFolder.createFile(publishBlob); + + HTMLToOutput = 'Assignment folder created with necessary template. Please update the Roster, fill in the quiz questions and click on the Publish icon in the folder. '; + } else { + var fileID = state.ids[0]; + var content = DriveApp.getFileById(fileID).getBlob().getDataAsString(); + var contentObject = JSON.parse(content); + var ssID = contentObject.rosterID; + var ss = SpreadsheetApp.openById(ssID); + var sheet = ss.getSheets()[0]; + var rosterRange = ss.getRangeByName('RosterRange'); + var rosterObjects = getRowsData(sheet, rosterRange); + + + if(contentObject.type === 'close'){ + for(var i in rosterObjects){ + var rosterItem = rosterObjects[i]; + var quickCopy = DriveApp.getFileById(rosterItem.fileid); + quickCopy.removeEditor(rosterItem.studentEmailAddress); + quickCopy.addViewer(rosterItem.studentEmailAddress); + } + HTMLToOutput = 'Quiz is now closed. Your students will no longer be able to save any changes. They wil be able to view your notes on their quizes. '; + + }else if(contentObject.type === 'publish'){ + var quizFileID = contentObject.quizID; + var quiz = DriveApp.getFileById(quizFileID); + var folderID = contentObject.assignmentFolderID; + var folder = DriveApp.getFolderById(folderID); + + for(var i in rosterObjects){ + var rosterItem = rosterObjects[i]; + var quizCopy = quiz.makeCopy('Quiz - ' + rosterItem.studentName); + folder.addFile(quizCopy); + DriveApp.getRootFolder().removeFile(quizCopy); + quizCopy.addEditor(rosterItem.studentEmailAddress); + rosterItem.fileid = quizCopy.getId(); + } + setRowsData(sheet,rosterObjects); + HTMLToOutput = 'Completed publishing your quizes. Your students should be able to see the files in their "Shared with me" view.'; + } + + } + + } + else if(e.parameter.code){//if we get "code" as a parameter in, then this is a callback. we can make this more explicit + getAndStoreAccessToken(e.parameter.code); + HTMLToOutput = 'App is installed, you can close this window now or open up Google Drive.'; + } + else {//we are starting from scratch or resetting + } + var t = HtmlService.createTemplateFromFile('ui') + t.message = HTMLToOutput; + return t.evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE) +} + +function getURLForAuthorization(){ + return AUTHORIZE_URL + '?response_type=code&client_id='+CLIENT_ID+'&redirect_uri='+REDIRECT_URL +'&scope=https://www.googleapis.com/auth/drive.install'; +} + +function getAndStoreAccessToken(code){ + var parameters = { method : 'post', + payload : 'client_id='+CLIENT_ID+'&client_secret='+CLIENT_SECRET+'&grant_type=authorization_code&redirect_uri='+REDIRECT_URL+'&code=' + code}; + //no need to do anything with the token going forward. + var response = UrlFetchApp.fetch(TOKEN_URL,parameters).getContentText(); +} + + +//replace with any template files you want - +var ROSTER_TEMPLATE = '0AkJNj_IM2wiPdEZzbFUxMjQteGtQS1JadHg5VmFMdGc'; +var QUIZ_TEMPLATE = '1_LIZp1lahFhNouXsZNI1s6UluBskt_LOwQGlJIj2ftY'; + +var AUTHORIZE_URL = 'https://accounts.google.com/o/oauth2/auth'; +var TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'; + +//put your URL below or use ScriptApp.getService().getUrl(); +var REDIRECT_URL= 'https://script.google.com/macros/s/AKfycbxK6aULLp8CL47aiUM_tHZCX9-2YNOK0yrp-ujnUQV8CrUQkUGk/exec'; +var CLIENT_ID = ScriptProperties.getProperty('CLIENT_ID') +var CLIENT_SECRET = ScriptProperties.getProperty('CLIENT_SECRET'); + diff --git a/IO2013/Drive/SpreadsheetUtils.gs b/IO2013/Drive/SpreadsheetUtils.gs new file mode 100644 index 0000000..aca6a3f --- /dev/null +++ b/IO2013/Drive/SpreadsheetUtils.gs @@ -0,0 +1,189 @@ +//https://developers.google.com/apps-script/storing_data_spreadsheets#reading + + +// getRowsData iterates row by row in the input range and returns an array of objects. +// Each object contains all the data for a given row, indexed by its normalized column name. +// Arguments: +// - sheet: the sheet object that contains the data to be processed +// - range: the exact range of cells where the data is stored +// - columnHeadersRowIndex: specifies the row number where the column names are stored. +// This argument is optional and it defaults to the row immediately above range; +// Returns an Array of objects. +function getRowsData(sheet, range, columnHeadersRowIndex) { + columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1; + var numColumns = range.getLastColumn() - range.getColumn() + 1; + var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns); + var headers = headersRange.getValues()[0]; + return getObjects(range.getValues(), normalizeHeaders(headers)); +} + +// setRowsData fills in one row of data per object defined in the objects Array. +// For every Column, it checks if data objects define a value for it. +// Arguments: +// - sheet: the Sheet Object where the data will be written +// - objects: an Array of Objects, each of which contains data for a row +// - optHeadersRange: a Range of cells where the column headers are defined. This +// defaults to the entire first row in sheet. +// - optFirstDataRowIndex: index of the first row where data should be written. This +// defaults to the row immediately below the headers. +function setRowsData(sheet, objects, optHeadersRange, optFirstDataRowIndex) { + var headersRange = optHeadersRange || sheet.getRange(1, 1, 1, sheet.getMaxColumns()); + var firstDataRowIndex = optFirstDataRowIndex || headersRange.getRowIndex() + 1; + var headers = normalizeHeaders(headersRange.getValues()[0]); + + var data = []; + for (var i = 0; i < objects.length; ++i) { + var values = [] + for (j = 0; j < headers.length; ++j) { + var header = headers[j]; + // If the header is non-empty and the object value is 0... + if ((header.length > 0) && (objects[i][header] == 0)) { + values.push(0); + } + // If the header is empty or the object value is empty... + else if ((!(header.length > 0)) || (objects[i][header]=='')) { + values.push(''); + } + else { + values.push(objects[i][header]); + } + } + data.push(values); + } + + var destinationRange = sheet.getRange(firstDataRowIndex, headersRange.getColumnIndex(), + objects.length, headers.length); + destinationRange.setValues(data); +} + +// getColumnsData iterates column by column in the input range and returns an array of objects. +// Each object contains all the data for a given column, indexed by its normalized row name. +// Arguments: +// - sheet: the sheet object that contains the data to be processed +// - range: the exact range of cells where the data is stored +// - rowHeadersColumnIndex: specifies the column number where the row names are stored. +// This argument is optional and it defaults to the column immediately left of the range; +// Returns an Array of objects. +function getColumnsData(sheet, range, rowHeadersColumnIndex) { + rowHeadersColumnIndex = rowHeadersColumnIndex || range.getColumnIndex() - 1; + var headersTmp = sheet.getRange(range.getRow(), rowHeadersColumnIndex, range.getNumRows(), 1).getValues(); + var headers = normalizeHeaders(arrayTranspose(headersTmp)[0]); + return getObjects(arrayTranspose(range.getValues()), headers); +} + + +// For every row of data in data, generates an object that contains the data. Names of +// object fields are defined in keys. +// Arguments: +// - data: JavaScript 2d array +// - keys: Array of Strings that define the property names for the objects to create +function getObjects(data, keys) { + var objects = []; + for (var i = 0; i < data.length; ++i) { + var object = {}; + var hasData = false; + for (var j = 0; j < data[i].length; ++j) { + var cellData = data[i][j]; + if (isCellEmpty(cellData)) { + continue; + } + object[keys[j]] = cellData; + hasData = true; + } + if (hasData) { + objects.push(object); + } + } + return objects; +} + +// Returns an Array of normalized Strings. +// Arguments: +// - headers: Array of Strings to normalize +function normalizeHeaders(headers) { + var keys = []; + for (var i = 0; i < headers.length; ++i) { + var key = normalizeHeader(headers[i]); + if (key.length > 0) { + keys.push(key); + } + } + return keys; +} + +// Normalizes a string, by removing all alphanumeric characters and using mixed case +// to separate words. The output will always start with a lower case letter. +// This function is designed to produce JavaScript object property names. +// Arguments: +// - header: string to normalize +// Examples: +// "First Name" -> "firstName" +// "Market Cap (millions) -> "marketCapMillions +// "1 number at the beginning is ignored" -> "numberAtTheBeginningIsIgnored" +function normalizeHeader(header) { + var key = ""; + var upperCase = false; + for (var i = 0; i < header.length; ++i) { + var letter = header[i]; + if (letter == " " && key.length > 0) { + upperCase = true; + continue; + } + if (!isAlnum(letter)) { + continue; + } + if (key.length == 0 && isDigit(letter)) { + continue; // first character must be a letter + } + if (upperCase) { + upperCase = false; + key += letter.toUpperCase(); + } else { + key += letter.toLowerCase(); + } + } + return key; +} + +// Returns true if the cell where cellData was read from is empty. +// Arguments: +// - cellData: string +function isCellEmpty(cellData) { + return typeof(cellData) == "string" && cellData == ""; +} + +// Returns true if the character char is alphabetical, false otherwise. +function isAlnum(char) { + return char >= 'A' && char <= 'Z' || + char >= 'a' && char <= 'z' || + isDigit(char); +} + +// Returns true if the character char is a digit, false otherwise. +function isDigit(char) { + return char >= '0' && char <= '9'; +} + +// Given a JavaScript 2d Array, this function returns the transposed table. +// Arguments: +// - data: JavaScript 2d Array +// Returns a JavaScript 2d Array +// Example: arrayTranspose([[1,2,3],[4,5,6]]) returns [[1,4],[2,5],[3,6]]. +function arrayTranspose(data) { + if (data.length == 0 || data[0].length == 0) { + return null; + } + + var ret = []; + for (var i = 0; i < data[0].length; ++i) { + ret.push([]); + } + + for (var i = 0; i < data.length; ++i) { + for (var j = 0; j < data[i].length; ++j) { + ret[j][i] = data[i][j]; + } + } + + return ret; +} \ No newline at end of file diff --git a/IO2013/Drive/ui.html b/IO2013/Drive/ui.html new file mode 100644 index 0000000..c009cf0 --- /dev/null +++ b/IO2013/Drive/ui.html @@ -0,0 +1,53 @@ + +Google Drive Quiz Manager + + + + + + + +
Google Drive Quiz Manager
+ + +
+

+

Please click on the link below to install the Quiz Manager app to your Google Drive. You will be redirected to a standard Google authorization page.

+

+
+Install app!
+

+
+ +
+

+

+

+
+Close window
+

+
+ +

+

+ +

+ + From 81fd63fa7a8dfd732ede91e32fbec988850795e9 Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Thu, 16 May 2013 14:56:06 -0300 Subject: [PATCH 05/12] created readme.md --- IO2013/Drive/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 IO2013/Drive/README.md diff --git a/IO2013/Drive/README.md b/IO2013/Drive/README.md new file mode 100644 index 0000000..9748d95 --- /dev/null +++ b/IO2013/Drive/README.md @@ -0,0 +1,7 @@ +Companion code for Google IO 2013 session - Integrate Google Drive with Google Apps Script + +https://developers.google.com/events/io/sessions/325412094 + +For setup - + +You'll need to create OAuth 2 client ID/secret at http://developers.google.com/console and store it in the right ScriptProperties. From c81cbcbd243113f28d6a9a1de62c0462499a4b33 Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Sat, 1 Jun 2013 15:44:36 -0400 Subject: [PATCH 06/12] added white house hackday code --- WhiteHouseHackday/Code.gs | 155 ++++++++++++++++++++++++++++++++++++++ WhiteHouseHackday/ui.html | 128 +++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 WhiteHouseHackday/Code.gs create mode 100644 WhiteHouseHackday/ui.html diff --git a/WhiteHouseHackday/Code.gs b/WhiteHouseHackday/Code.gs new file mode 100644 index 0000000..698b601 --- /dev/null +++ b/WhiteHouseHackday/Code.gs @@ -0,0 +1,155 @@ +function onOpen(){ + SpreadsheetApp.getActiveSpreadsheet() + .addMenu('We the people',[{name:'Search Petitions', functionName:'showUI'}, + {name:'Load Signatures', functionName:'loadSignatures'}, + null, + {name: 'Twitter trending', functionName:'twitter'}]);//future ideas +} + +function showUI(){ + var ui = HtmlService.createTemplateFromFile('ui').evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE).setHeight(550); + + SpreadsheetApp.getActiveSpreadsheet().show(ui); +} + +function returnDate(unix_timestamp){ + //var unix_timestamp = '1370025951'; + var date = new Date(unix_timestamp*1000); + //Logger.log(date); + return date; +} + +function returnUnixtimestamp(dateString){ + //var dateString = '2013-04-01'; + var parts = dateString.match(/(\d+)/g); + var date = new Date(parts[0], parts[1]-1, parts[2]); // months are 0-based + var timestamp = date.getTime()/1000 + ''; + return timestamp; +} + + +function createOrSetActiveSheet(sheetName){ + var ss = SpreadsheetApp.getActiveSpreadsheet(); + var sheets = ss.getSheets(); + var sheet; + for(var i = 0;i +We the People + + + + + + + + + + Search Petitions + +
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ +
+ + +
+
From ed38101bee39ced1b6fb861e490649a927a5f378 Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Sat, 1 Jun 2013 15:47:01 -0400 Subject: [PATCH 07/12] Create README.md --- WhiteHouseHackday/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 WhiteHouseHackday/README.md diff --git a/WhiteHouseHackday/README.md b/WhiteHouseHackday/README.md new file mode 100644 index 0000000..63d72aa --- /dev/null +++ b/WhiteHouseHackday/README.md @@ -0,0 +1,9 @@ +Title: Google Spreadsheet Integration +Developers: Arun Nagarajan from Google and Daniel McLaughlin from the Boston Globe +Description: Easily search and process petition data directly through Google Spreadsheets. This tool could be a framework to exploring other open APIs within an environment familiar to many researchers and journalists. + +Code is very early and cleanup/refactoring needed ;) + +![Screenshot of "We the Spreadsheet"](https://f.cloud.github.com/assets/979487/595221/47b9c01c-caeb-11e2-9095-b67156071732.png +) + From 0d48b467ab7c3897192d04c58763c8de582ec483 Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Sat, 1 Jun 2013 17:07:34 -0300 Subject: [PATCH 08/12] Update README.md --- WhiteHouseHackday/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WhiteHouseHackday/README.md b/WhiteHouseHackday/README.md index 63d72aa..ec46994 100644 --- a/WhiteHouseHackday/README.md +++ b/WhiteHouseHackday/README.md @@ -1,5 +1,7 @@ Title: Google Spreadsheet Integration + Developers: Arun Nagarajan from Google and Daniel McLaughlin from the Boston Globe + Description: Easily search and process petition data directly through Google Spreadsheets. This tool could be a framework to exploring other open APIs within an environment familiar to many researchers and journalists. Code is very early and cleanup/refactoring needed ;) From a38c1f6aec05ae44268bfcb205e5333c883d5a3a Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Fri, 21 Jun 2013 17:20:19 -0400 Subject: [PATCH 09/12] removed some old userdata --- .../UserInterfaceState.xcuserstate | Bin 15541 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.xcworkspace/xcuserdata/lassebunk.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.xcworkspace/xcuserdata/lassebunk.xcuserdatad/UserInterfaceState.xcuserstate b/ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.xcworkspace/xcuserdata/lassebunk.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index cd6258548cc3c7b9a0d05c582d93d5b4f73f0f13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15541 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 E2fpSL_W%F@ From 56854924a52c085cfa12ec70f91f80ce4f2dfe76 Mon Sep 17 00:00:00 2001 From: Kartik Ayyar Date: Thu, 17 Oct 2013 18:06:31 -0700 Subject: [PATCH 10/12] Update oauth2.gs Use correct variable name for for logging errors. --- IO2013/YouTubeAnalytics/oauth2.gs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/IO2013/YouTubeAnalytics/oauth2.gs b/IO2013/YouTubeAnalytics/oauth2.gs index 8481a51..9da9a28 100644 --- a/IO2013/YouTubeAnalytics/oauth2.gs +++ b/IO2013/YouTubeAnalytics/oauth2.gs @@ -1,4 +1,3 @@ - var AUTHORIZE_URL = 'https://accounts.google.com/o/oauth2/auth'; //step 1. we can actually start directly here if that is necessary var TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'; //step 2. after we get the callback, go get token @@ -43,7 +42,7 @@ function getUrlFetchOptions() { function attemptTokenRefresh_() { var refreshToken = UserProperties.getProperty(refreshTokenPropertyName); if (!refreshToken) { - Logger.log('No refresh token available to refresh with ' + tokenKey); + Logger.log('No refresh token available to refresh with ' + refreshTokenPropertyName); return false; } var requestData = { @@ -115,4 +114,4 @@ function isTokenPresent_() { return false; } return true; -} \ No newline at end of file +} From dde1c0a0936fda5611317a56b0becac43be08b90 Mon Sep 17 00:00:00 2001 From: radef Date: Mon, 21 Oct 2013 22:52:37 -0400 Subject: [PATCH 11/12] Update MakePhoneCall.gs line 23 was missing the ".getUrl()" --- Twilio/MakePhoneCall/MakePhoneCall.gs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Twilio/MakePhoneCall/MakePhoneCall.gs b/Twilio/MakePhoneCall/MakePhoneCall.gs index b4f866e..66cc6bd 100644 --- a/Twilio/MakePhoneCall/MakePhoneCall.gs +++ b/Twilio/MakePhoneCall/MakePhoneCall.gs @@ -19,7 +19,7 @@ function readRows() { function makePhoneCall(name,number, message){ //URL is the callback to the current service with the message - var url = ScriptApp.getService() + '?MSG'+message.replace(/ /g,'+'); //can't seem to send = in here + var url = ScriptApp.getService().getUrl() + '?MSG'+message.replace(/ /g,'+'); //can't seem to send = in here Logger.log(url); var payload = { "From" : "2246773902" From 672e8209d88d7fd390b1258a74c2e8bd5060ccd9 Mon Sep 17 00:00:00 2001 From: Arun Nagarajan Date: Mon, 16 Jun 2014 14:57:35 -0400 Subject: [PATCH 12/12] Initial SAP commits Sample code from SAP demos. --- SAP/Forms/Code.gs | 51 ++++++++ SAP/Forms/product_template.html | 24 ++++ SAP/Gmail Schemas/Code.gs | 6 + SAP/Gmail Schemas/mail_template.html | 45 +++++++ SAP/README.md | 16 +++ SAP/Sheets/Code.gs | 174 +++++++++++++++++++++++++++ SAP/Sheets/ui.html | 94 +++++++++++++++ 7 files changed, 410 insertions(+) create mode 100644 SAP/Forms/Code.gs create mode 100644 SAP/Forms/product_template.html create mode 100644 SAP/Gmail Schemas/Code.gs create mode 100644 SAP/Gmail Schemas/mail_template.html create mode 100644 SAP/README.md create mode 100644 SAP/Sheets/Code.gs create mode 100644 SAP/Sheets/ui.html diff --git a/SAP/Forms/Code.gs b/SAP/Forms/Code.gs new file mode 100644 index 0000000..47f2236 --- /dev/null +++ b/SAP/Forms/Code.gs @@ -0,0 +1,51 @@ +function onFormSubmit(e) { + var myObject = {}; + var itemResponses = e.response.getItemResponses(); + for (var j = 0; j < itemResponses.length; j++) { + var itemResponse = itemResponses[j]; + myObject[itemResponse.getItem().getTitle()] = itemResponse.getResponse(); + //Logger.log('"%s" was "%s"',itemResponse.getItem().getTitle(), itemResponse.getResponse()); + } + + Logger.log(myObject); + + var t = HtmlService.createTemplateFromFile('product_template'); + t.data = myObject; + var postPayload = t.evaluate().getContent(); + + Logger.log(postPayload); + + var base_url = "https://sapes1.sapdevcenter.com/sap/opu/odata/IWBEP/GWDEMO/ProductCollection"; + var additionParams = "$format=json"; + var user = 'YOUR_USERNAME'; + var password = 'YOUR_PASSWORD'; + var header = 'Basic '+Utilities.base64Encode(user+':'+password); + + var headers = { + Authorization: header, + 'X-CSRF-Token' : 'Fetch', + "Content-Type": "application/atom+xml" + } + + + //Logger.log(headers); + var response = UrlFetchApp.fetch(base_url,{headers: headers, muteHttpExceptions: true}); + var csrf_token = response.getAllHeaders()['x-csrf-token']; + var cookie = response.getAllHeaders()['set-cookie']; + + //Logger.log(response.getAllHeaders()); + + headers['X-CSRF-Token'] = csrf_token; + headers['Cookie'] = cookie.join('; '); + + + + Logger.log(headers); + + //var deleteResponse = UrlFetchApp.fetch(id,{headers: headers, method : 'DELETE', muteHttpExceptions: true}); + + + var createResponse = UrlFetchApp.fetch(base_url,{headers: headers, method : 'POST', contentType : "application/atom+xml", payload: postPayload, muteHttpExceptions: true}); + + Logger.log(createResponse.getContentText()); +} diff --git a/SAP/Forms/product_template.html b/SAP/Forms/product_template.html new file mode 100644 index 0000000..7d56b01 --- /dev/null +++ b/SAP/Forms/product_template.html @@ -0,0 +1,24 @@ + + + + +Regular VAT +ARUN- +MTR +500.00 +4.200 +0.320 +0100000000 + +Notebooks +1 +EA +PR +KGM +SAP +EUR +0.040 +0.210 + + + \ No newline at end of file diff --git a/SAP/Gmail Schemas/Code.gs b/SAP/Gmail Schemas/Code.gs new file mode 100644 index 0000000..74fb569 --- /dev/null +++ b/SAP/Gmail Schemas/Code.gs @@ -0,0 +1,6 @@ +function testSchemas() { + var htmlBody = HtmlService.createHtmlOutputFromFile('mail_template').getContent(); + + GmailApp.sendEmail(Session.getEffectiveUser().getEmail(),'Alert for SAP - ' + new Date(),'', + {htmlBody: htmlBody}); +} diff --git a/SAP/Gmail Schemas/mail_template.html b/SAP/Gmail Schemas/mail_template.html new file mode 100644 index 0000000..4864715 --- /dev/null +++ b/SAP/Gmail Schemas/mail_template.html @@ -0,0 +1,45 @@ + + + + + +

+ This a test for a Go-To action in Gmail. +

+ + \ No newline at end of file diff --git a/SAP/README.md b/SAP/README.md new file mode 100644 index 0000000..0b89520 --- /dev/null +++ b/SAP/README.md @@ -0,0 +1,16 @@ +SAP Demos +================ + +Sample SAP integrations. You'll need to create a [sample SAP account](http://scn.sap.com/docs/DOC-40986) at the SCN. + +You can watch the video of the [sample demos here](http://www.slideshare.net/SAPAppsPartner/tehcnical-webinar-technical-webinar-building-mashups-with-google-apps-and-sap-using-sap-netweaver-gateway) (start at about 1 hr in for Google Apps demos) + +These are not official Google samples. These are more of proof of concepts to support demos and other presentations. + +Please use this code as a guidline and not as production ready code. + +http://developers.google.com/apps-script + +http://script.google.com + +All code in this folder released under [Apache 2.0 license](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/SAP/Sheets/Code.gs b/SAP/Sheets/Code.gs new file mode 100644 index 0000000..6e6055f --- /dev/null +++ b/SAP/Sheets/Code.gs @@ -0,0 +1,174 @@ +//sample instance +//http://scn.sap.com/docs/DOC-40986 + + +function onOpen(){ + SpreadsheetApp.getActiveSpreadsheet() + .addMenu('SAP NetWeaver',[{name:'SAP Data Wizard', functionName:'showUI'}, + null, + {name:'Load Business Partners', functionName:'getBusinessPartnerData'}, + {name:'Load Sales Orders', functionName:'getSalesOrderData'}, + null, + {name:'Load Items for Sales Order', functionName:'loadSalesItems'}, + null, + {name: 'Post to Google+', functionName:'googleplus'}]);//future ideas +} + +function showUI(){ + var ui = HtmlService.createTemplateFromFile('ui').evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE).setHeight(550); + + SpreadsheetApp.getActiveSpreadsheet().show(ui); +} + +function returnDate(unix_timestamp){ + //var unix_timestamp = '1370025951'; + var date = new Date(unix_timestamp*1000); + //Logger.log(date); + return date; +} + +function returnUnixtimestamp(dateString){ + //var dateString = '2013-04-01'; + var parts = dateString.match(/(\d+)/g); + var date = new Date(parts[0], parts[1]-1, parts[2]); // months are 0-based + var timestamp = date.getTime()/1000 + ''; + return timestamp; +} + + +function createOrSetActiveSheet(sheetName){ + var ss = SpreadsheetApp.getActiveSpreadsheet(); + var sheets = ss.getSheets(); + var sheet; + for(var i = 0;i + + + + + + +SAP NetWeaver + +
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ +
+ + +
+
+ + +