├── README.md ├── algorithmia-0.2.0.js.gs ├── code.js.gs └── images ├── all_columns.jpg ├── code_ready.jpg ├── direct_algo.jpg ├── google_sheet_algorithmia.jpg ├── name_script_file.jpg ├── new_client_ready.jpg └── script_editor.jpg /README.md: -------------------------------------------------------------------------------- 1 | # algorithmia-google 2 | 3 | Algorithmia.com calls itself a "marketplace for algorithms", but I rather see it as a "rocket powered, scalable, artificial intelligence cloud hosting platform!" 4 | 5 | There are thousands of algorithms currently hosted on Algorithmia. As a developer, you can think of these as a library of API endpoints that take some input and return some output, doing some magic in between. The magic is optimized for performance using a cloud of GPUs and as they say, it is a "deep learning architecture built for scale." 6 | 7 | Currently, I'm working at an international school in Cambodia and we use Google Apps for everything. I've enjoyed writing custom functions in Google App Scripting to solve certain problems. When I learned that with Algorithmia I can add artificial intelligence functions via an API, I was excited to see if I could add some zippy artificial intelligence to my Google Sheets! 8 | 9 | However, I quickly discovered that the basic JavaScript client for Algorithmia won't work inside of Google Apps. Google Apps is a sandbox that requires you to call to the outside using only their request object. After a few modifications, I was able to get it to work and the full tutorial is below. Enjoy! 10 | 11 | # How to create a Google Apps custom function for Algorithmia 12 | 13 | ## Step 0 - Get your Algorithmia API key 14 | Head on over to http://algorithmia.com and sign up for a new account (or sign in to your old one). Once you're in, go to "My Profile" and choose "Credentials" and copy your API key. You'll need it for a later step. 15 | 16 | ## Step 1 - Create a new Google Sheet document 17 | You can do this by going to 18 | https://docs.google.com/spreadsheets and clicking the big plus icon. 19 | 20 | ## Step 2 - Name your sheet "My Algorithmia" or whatever you like 21 | Always good to choose a name. 22 | 23 | ## Step 3 - Open the "Script editor" 24 | Choose "Tools-->Script editor" from the menu. This will open a new tab with the script editor open for your sheet. 25 | 26 | On the script editor tab, you'll also want to name your script file. I suggest "Algorithmia". 27 | 28 | ![Script editor](images/script_editor.jpg) 29 | 30 | ## Step 4 - Add my basic Google App client 31 | *Remember that this code is my own simple, basic but working version that will most likely need tweaked for your particular application.* 32 | 33 | Choose "File-->New-->Script file" from the menu. This will open a dialog for you to name your file. I suggest using the name "algorithmia-0.2.0.js.gs". 34 | 35 | ![Name your script file](images/name_script_file.jpg) 36 | 37 | Now copy and paste the below code into the script editor code window. Just delete the default function that Google adds for you. 38 | 39 | (You can find the most updated code on github here: https://github.com/kenburcham/algorithmia-google/blob/master/algorithmia-0.2.0.js.gs) 40 | 41 | ```javascript 42 | //Google Apps (Javascript) Client for Algorithmia 43 | // by Ken Burcham - (mostly copied from Algorithmia's JavaScript client) 44 | // Add this file to your Google Sheet Script Editor to define the Algorithmia client you can 45 | // call from your own custom function. 46 | 47 | var Algorithmia = {}; 48 | 49 | (function() { 50 | var Client, algoPattern, getDefaultApiAddress; 51 | 52 | getDefaultApiAddress = function() { 53 | if (Algorithmia.apiAddress !== void 0) { 54 | return Algorithmia.apiAddress; 55 | } else { 56 | return "https://api.algorithmia.com/v1/web/algo"; 57 | } 58 | }; 59 | 60 | algoPattern = /^(?:algo:\/\/|\/|)(\w+\/.+)$/; 61 | 62 | Algorithmia.query = function(algo_uri, api_key, input, cb) { 63 | return Algorithmia.client(api_key).algo(algo_uri).pipe(input, function(result) { 64 | if (result.error) { 65 | return cb(result.error.message || result.error); 66 | } else { 67 | return cb(void 0, result.result); 68 | } 69 | }); 70 | }; 71 | 72 | Algorithmia.client = function(api_key, api_address) { 73 | //Logger.log("Client loading..."); 74 | api_key = api_key || Algorithmia.apiKey; 75 | api_address = api_address || getDefaultApiAddress(); 76 | return new Client(api_key, api_address); 77 | }; 78 | 79 | Client = function(api_key, api_address) { 80 | this.api_key = api_key; 81 | this.api_address = api_address; 82 | this.algo = function(algo_uri) { 83 | if (algo_uri && typeof algo_uri === "string") { 84 | return new Algorithm(this, algo_uri); 85 | } else { 86 | console.error("Invalid algorithm url: " + algo_uri); 87 | return null; 88 | } 89 | }; 90 | }; 91 | 92 | Algorithmia.algo = function(algo_uri) { 93 | return Algorithmia.client().algo(algo_uri); 94 | }; 95 | 96 | Algorithm = function(client, algo_uri) { 97 | if (!(typeof algo_uri === "string" && algo_uri.match(algoPattern))) { 98 | throw "Invalid Algorithm URI (expected /owner/algo)"; 99 | } 100 | this.client = client; 101 | this.algo_uri = algo_uri.match(algoPattern)[1]; 102 | this.pipe = function(input, cb) { 103 | endpoint_url = client.api_address + "/" + this.algo_uri; 104 | payload = JSON.stringify(input); 105 | //Logger.log(payload); 106 | var options = { 107 | 'muteHttpExceptions':false, 108 | 'method': 'post', 109 | 'headers': {"Content-Type":"application/json", 110 | "Accept":"application/json, text/javascript", 111 | "Authorization":"Simple " + client.api_key 112 | }, 113 | 'payload':payload 114 | }; 115 | 116 | var response = UrlFetchApp.fetch(endpoint_url, options); 117 | 118 | var json = response.getContentText(); 119 | var data = JSON.parse(json); 120 | 121 | return data.result; 122 | }; 123 | 124 | }; 125 | 126 | }).call(this); 127 | ``` 128 | 129 | *Make sure to type CTRL-S and save your work!* 130 | 131 | When you're done your script editor will look something like this: 132 | 133 | ![Example with client added](images/new_client_ready.jpg) 134 | 135 | ## Step 5 - Create your custom function 136 | Now that you have a client you can use to call algorithms on Algorithmia, let's setup the function you can use in your Google Sheet to do magic. 137 | 138 | Click back onto the "Code.gs" tab and delete the example function that Google gives you and create your own basic function called ALGO: 139 | 140 | (my most up to date version is here: https://github.com/kenburcham/algorithmia-google/blob/master/code.js.gs) 141 | 142 | ```javascript 143 | var api_key = "API_KEY"; //replace this with YOUR api key! 144 | algorithms = {}; 145 | 146 | //Call this function from a cell in your Sheet 147 | // with which algorithm you want to call 148 | // and the input to provide to it 149 | function ALGO(a_algorithm, a_input) { 150 | return algorithms[a_algorithm](a_input); 151 | } 152 | 153 | //define new algorithms here 154 | algorithms['analyze-url'] = function(a_inputURL){ 155 | 156 | var input = [a_inputURL]; 157 | result = Algorithmia.client(api_key) 158 | .algo("algo://web/AnalyzeURL/0.2.17") 159 | .pipe(input); 160 | 161 | // in our case, we want the "summary" field 162 | //from the resulting JSON 163 | if(result.summary == "") 164 | return ""; 165 | else 166 | return result.summary; 167 | }; 168 | 169 | ``` 170 | 171 | *Make sure to save your work.* 172 | 173 | Now your Script editor should look something like this: 174 | 175 | ![Code is ready](images/code_ready.jpg) 176 | 177 | The back-end of your custom function is ready to go. Let's switch over to the Google Sheet and see some magic! 178 | 179 | ## Step 6 - Use your shiny new custom function 180 | The moment you've been waiting for is to see some artificial intelligence happen inside your own Google Sheet courtesy of Algorithmia. 181 | 182 | The "AnalyzeURL" function that we'll be calling will read in the content of a given web page and summarize it for you using an AI natural language processing algorithm. 183 | 184 | In your Google Sheet, paste the following URL (or choose your own) into cell B1 (or whatever you like) 185 | 186 | http://www.theverge.com/2016/12/16/13989590/nasa-3d-video-carbon-dioxide-co2-earth-atmosphere 187 | 188 | In cell B2, you want to call your custom function, passing in the name of the algorithm we defined ("analyze-url") and give it the cell of your URL as input to the function (B1). So add the following function to cell B2: 189 | 190 | =ALGO("analyze-url",B1) 191 | 192 | When you move off of the cell, it will calculate the value causing it to fire your function which will make a call out to Algorithmia, hit the REST microservice that wraps the AnalyzeURL algorithm, process your URL and return back the result. Take a look at the [definition of the AnalyzeURL algorithm](https://algorithmia.com/algorithms/web/AnalyzeURL) if you are curious! 193 | 194 | If everything works properly, your sheet will magically have a summary produced by Algorithmia's AI. 195 | ![Google Sheet function calling Algorithmia](images/google_sheet_algorithmia.jpg) 196 | 197 | ## One last thing 198 | Inevitably, you'll want to see more than just a one column/one value result. Google limits a custom function to only being able to write to the same cell where the function runs. That means, whichever cell you put the "=ALGO(...)" formula is the only one that the formula can write to. This is pretty limiting when what we'd like to do is maybe write all of the columns returned to the adjacent cells to the right. 199 | 200 | There are two ways to overcome this limitation. First, if you call a function directly from the script editor, it has permission to write to anywhere in the sheet. This, of course, is sort of a "programmer-only" solution. Secondly, Google Sheet "Add-ins" have full sheet write permission. The next logical step, then, would be to wrap up your functionality into a nifty Google Add-in. 201 | 202 | You can try out the first method (calling a function directly) if by adding three more functions to your code, DIRECT_ALGO, copyDataToFields and defining a new algorithm called "analyze-url-direct". Rather than paste all of that code here, just look for those functions on my github example here: https://github.com/kenburcham/algorithmia-google/blob/master/code.js.gs 203 | 204 | Once you have those functions added, you can see all of the columns displayed in your sheet from your Algorithmia API call by running the "DIRECT_ALGO" function from inside the script editor. It will start with the cell A1 and then write all of your columns to A2 and following... then it will write all of the data to the next row under the new heading labels. 205 | 206 | Click the triangle PLAY button in the script editor after selecting DIRECT_ALGO from the menu to try it: 207 | 208 | ![Click play](images/direct_algo.jpg) 209 | 210 | And if everything works, your result will look something like this: 211 | 212 | ![All columns](images/all_columns.jpg) 213 | 214 | Next, I will be working on creating an Algorithmia side-bar for trying out more of these ideas. I'll share them on my github when I'm done! 215 | 216 | 217 | ## Conclusion 218 | I'm excited to add Algorithmia capability to my Google Sheets. I also want to create my own algorithms as well. Algorithmia's business model is to charge credits for each API call. There is a charge per second of CPU usage as well as, potentially, a royalty paid to the algorithm developer. This provides incentives for machine learning developers to host their work on Algorithmia. Not only can they scale out their service for their own use, but by sharing it with others, they can make royalties from every API call. 219 | 220 | As a consumer, you get a 10k credits for free each month, then API calls will cost you real money. But if it is adding value to your business or even personal workflow then it will be worth it. I'm just glad they give you some free credits to try it all out. 221 | 222 | Please let me know if I've made any mistakes and I'll be happy to correct them. Enjoy! 223 | -------------------------------------------------------------------------------- /algorithmia-0.2.0.js.gs: -------------------------------------------------------------------------------- 1 | //Google Apps (Javascript) Client for Algorithmia 2 | // by Ken Burcham - (mostly copied from Algorithmia's JavaScript client) 3 | // Add this file to your Google Sheet Script Editor to define the Algorithmia client you can 4 | // call from your own custom function. 5 | 6 | var Algorithmia = {}; 7 | 8 | (function() { 9 | var Client, algoPattern, getDefaultApiAddress; 10 | 11 | getDefaultApiAddress = function() { 12 | if (Algorithmia.apiAddress !== void 0) { 13 | return Algorithmia.apiAddress; 14 | } else { 15 | return "https://api.algorithmia.com/v1/web/algo"; 16 | } 17 | }; 18 | 19 | algoPattern = /^(?:algo:\/\/|\/|)(\w+\/.+)$/; 20 | 21 | Algorithmia.query = function(algo_uri, api_key, input, cb) { 22 | return Algorithmia.client(api_key).algo(algo_uri).pipe(input, function(result) { 23 | if (result.error) { 24 | return cb(result.error.message || result.error); 25 | } else { 26 | return cb(void 0, result.result); 27 | } 28 | }); 29 | }; 30 | 31 | Algorithmia.client = function(api_key, api_address) { 32 | //Logger.log("Client loading..."); 33 | api_key = api_key || Algorithmia.apiKey; 34 | api_address = api_address || getDefaultApiAddress(); 35 | return new Client(api_key, api_address); 36 | }; 37 | 38 | Client = function(api_key, api_address) { 39 | this.api_key = api_key; 40 | this.api_address = api_address; 41 | this.algo = function(algo_uri) { 42 | if (algo_uri && typeof algo_uri === "string") { 43 | return new Algorithm(this, algo_uri); 44 | } else { 45 | console.error("Invalid algorithm url: " + algo_uri); 46 | return null; 47 | } 48 | }; 49 | }; 50 | 51 | Algorithmia.algo = function(algo_uri) { 52 | return Algorithmia.client().algo(algo_uri); 53 | }; 54 | 55 | Algorithm = function(client, algo_uri) { 56 | if (!(typeof algo_uri === "string" && algo_uri.match(algoPattern))) { 57 | throw "Invalid Algorithm URI (expected /owner/algo)"; 58 | } 59 | this.client = client; 60 | this.algo_uri = algo_uri.match(algoPattern)[1]; 61 | this.pipe = function(input, cb) { 62 | endpoint_url = client.api_address + "/" + this.algo_uri; 63 | payload = JSON.stringify(input); 64 | //Logger.log(payload); 65 | var options = { 66 | 'muteHttpExceptions':false, 67 | 'method': 'post', 68 | 'headers': {"Content-Type":"application/json", 69 | "Accept":"application/json, text/javascript", 70 | "Authorization":"Simple " + client.api_key 71 | }, 72 | 'payload':payload 73 | }; 74 | 75 | var response = UrlFetchApp.fetch(endpoint_url, options); 76 | 77 | var json = response.getContentText(); 78 | var data = JSON.parse(json); 79 | 80 | return data.result; 81 | }; 82 | 83 | }; 84 | 85 | }).call(this); 86 | 87 | //# sourceMappingURL=algorithmia-0.2.0.js.map 88 | -------------------------------------------------------------------------------- /code.js.gs: -------------------------------------------------------------------------------- 1 | //Code.gs - Ken Burcham - December 21, 2016 2 | 3 | //Basic demonstration of how to call an Algorithmia algorithm from a custom function in a Google Sheet 4 | // In the Google Sheet, in the first column, paste in a URL for a website you want a summary for. 5 | // Then in the second column, give a function like: =ALGO("analyze-url",A1) 6 | // (where A1 is your URL that you want to summarize). 7 | // Google sheets will execute the function and return the summary using the Algorithmia API. 8 | 9 | //Login to Algorithmia (create an account if necessary) then put your api_key below. 10 | var api_key = "API_KEY"; 11 | 12 | /* 13 | * Call from a cell in a Google Sheet to make a call to a Algorithmia algorithm API. 14 | * Note: the algorithm must be already defined below. 15 | 16 | * a_algorithm: algorithm that you want to run. must match a configured algorithm in the array below 17 | * a_input: a cell or value that you want to provide as input 18 | * a_options: any options that you want to send to your algorithm function defined below 19 | */ 20 | function ALGO(a_algorithm, a_input, a_options) { 21 | //a_algorithm = "analyze-url"; 22 | //a_input = "http://www.theverge.com/2016/12/20/14020720/uber-self-driving-cars-bike-lane-problem"; 23 | return algorithms[a_algorithm](a_input, a_options); 24 | } 25 | 26 | //Running this code from inside the script editor will have permission 27 | // to write out all of the columns that return in the results to adjacent cells. 28 | function DIRECT_ALGO() { 29 | a_algorithm = "analyze-url-direct"; //the one we want to call below 30 | a_input = "https://www.theatlantic.com/health/archive/2016/12/no-doctor-should-work-30-straight-hours/510395/"; //test url 31 | algorithms[a_algorithm](a_input); 32 | } 33 | 34 | algorithms = {}; 35 | 36 | function add_algorithm(a_name, a_function){ 37 | algorithms[a_name]=a_function; 38 | } 39 | 40 | //Define the "analyze-url" algorithm from Algorithmia so we can call it. 41 | add_algorithm("analyze-url", 42 | function(a_inputURL){ 43 | 44 | var input = [a_inputURL]; 45 | 46 | if(input == "") 47 | throw new Error("AnalyzeURL requires an input URL"); 48 | 49 | result = Algorithmia.client(api_key) 50 | .algo("algo://web/AnalyzeURL/0.2.17") 51 | .pipe(input); 52 | 53 | // in our case, we want the "summary" field from the resulting JSON 54 | //Logger.log(result); 55 | if(result.summary == "") 56 | return ""; 57 | else 58 | return result.summary; 59 | }); 60 | 61 | //this function will copy all of the result columns/values into adjacent cells to A1 62 | add_algorithm("analyze-url-direct", 63 | function(a_inputURL){ 64 | 65 | var input = [a_inputURL]; 66 | result = Algorithmia.client(api_key) 67 | .algo("algo://web/AnalyzeURL/0.2.17") 68 | .pipe(input); 69 | 70 | //Logger.log(result); 71 | copyDataToFields(result) 72 | }); 73 | 74 | 75 | //mm - can't do this until we create this as an ADD-ON due to permission restrictions by Google... 76 | 77 | //option: copy a json result to the fields to the right of the running function 78 | function copyDataToFields(a_data) { 79 | var results = a_data; 80 | 81 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 82 | var sheet = ss.getSheets()[0]; 83 | 84 | //var cell = sheet.getActiveCell(); 85 | 86 | var active_row = 1; //cell.getRow()+1; 87 | var active_col = 1; //cell.getColumn()+1; 88 | 89 | var target_col = active_col; 90 | var target_row = active_row; 91 | 92 | //label the headers 93 | for(key in results) { 94 | target_col++; 95 | sheet.getRange(target_row, target_col).setValue(key); //key = label 96 | } 97 | 98 | target_col = active_col; //reset to beginning column 99 | target_row++; //move to the next row... 100 | 101 | //copy each json field in the top level to each consecutive cell to the right AFTER the current one 102 | for(key in results) { 103 | target_col++; 104 | sheet.getRange(target_row, target_col).setValue(results[key].toString().substring(0,255)); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /images/all_columns.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenburcham/algorithmia-google/150a16776ad05d48a0199f322fd193dde3c15263/images/all_columns.jpg -------------------------------------------------------------------------------- /images/code_ready.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenburcham/algorithmia-google/150a16776ad05d48a0199f322fd193dde3c15263/images/code_ready.jpg -------------------------------------------------------------------------------- /images/direct_algo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenburcham/algorithmia-google/150a16776ad05d48a0199f322fd193dde3c15263/images/direct_algo.jpg -------------------------------------------------------------------------------- /images/google_sheet_algorithmia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenburcham/algorithmia-google/150a16776ad05d48a0199f322fd193dde3c15263/images/google_sheet_algorithmia.jpg -------------------------------------------------------------------------------- /images/name_script_file.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenburcham/algorithmia-google/150a16776ad05d48a0199f322fd193dde3c15263/images/name_script_file.jpg -------------------------------------------------------------------------------- /images/new_client_ready.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenburcham/algorithmia-google/150a16776ad05d48a0199f322fd193dde3c15263/images/new_client_ready.jpg -------------------------------------------------------------------------------- /images/script_editor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenburcham/algorithmia-google/150a16776ad05d48a0199f322fd193dde3c15263/images/script_editor.jpg --------------------------------------------------------------------------------