Skip to content

Instantly share code, notes, and snippets.

@bstancil
Last active January 7, 2023 17:29
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bstancil/442c885c4e73eb5012ab016ee3d5f7ca to your computer and use it in GitHub Desktop.
Save bstancil/442c885c4e73eb5012ab016ee3d5f7ca to your computer and use it in GitHub Desktop.

Importing Mode results into a Google Sheet

A simple way to do this

Usage

In a Google sheet where this is enabled (see below for getting it set up), add the following formula to a cell, with the URL to a Mode query (note that you must include the query; you can get this URL by going to a report, clicking "View Details," and clicking on the query on the left).

=importModeResult("https://app.mode.com/organization/reports/abcdabcdabcd/queries/012301230123")

The cells below and to the right of this cell will be populated with the CSV results from this query. It will automatically use the last successful run of the query.

Setup

  1. From a Google Sheet, click “Extensions -> Apps Script” from the top menu bar.
  2. Paste the importModeResults function below into the code editor and save it.
  3. In the code editor, click the “gear icon -> Project Settings” in the left-side menu, scroll down to the bottom and click the “Add Script Property” tab.
  4. Add a username and password property. The value of the username property should be your Mode username, the email address associated with your Mode account, or a Mode API token. The value of the password property should be your Mode password or a Mode API secret. We strongly recommend using API tokens and secrets; you can create a token and secret in the API Tokens tab of your Mode settings page.
  5. Once you save these properties, you should be able to use the importModeResult function in your Google Sheet.
function importModeResult(url) {
  
  var USERNAME = PropertiesService.getScriptProperties().getProperty('username');
  var PASSWORD = PropertiesService.getScriptProperties().getProperty('password');
  
  var token = getTokenFromUrl(url);
  var account = getAccountFromUrl(url);
  var query = getQueryFromUrl(url);
  
  var options = {
    headers: { 'Authorization': 'Basic ' + Utilities.base64Encode(USERNAME + ':' + PASSWORD, Utilities.Charset.UTF_8) }
  };
    
  var reportUrl = 'https://app.mode.com/api/' + account + '/reports/' + token;
  var reportResponse = UrlFetchApp.fetch(reportUrl, options);
  var reportJSON = JSON.parse(reportResponse);
  
  var lastRunPath = reportJSON['_links']['last_successful_run']['href'];
  var queryRunUrl = 'https://app.mode.com' + lastRunPath + '/query_runs'
  var queryRunResponse = UrlFetchApp.fetch(queryRunUrl, options);
  var queryRunJSON = JSON.parse(queryRunResponse);
  
  var queryRuns = queryRunJSON['_embedded']['query_runs']
  
  queryRuns.forEach(function(q) {
    if (q['query_token'] == query) {
      resultPath = q['_links']['result']['href']
    }
  })
  
  if (resultPath) {
    var csvPath = 'https://app.mode.com' + resultPath + '/content.csv'
    var csvResponse = UrlFetchApp.fetch(csvPath, options);
    var csvTextRaw = csvResponse.getContentText();
    var csvText = csvTextRaw.replace(/(["'])(?:(?=(\\?))\2[\s\S])*?\1/g, function(e){return e.replace(/\r?|\r/g, '') });
    
    return Utilities.parseCsv(csvText)
    
  } else {
    return 'Invalid URL!'
  }
  
  
  function getTokenFromUrl(url) {
    return url.split('/reports/')[1].slice(0,12)
  }
  
  function getAccountFromUrl(url) {
    return url.split('.com/')[1].split('/')[0]
  }
  
  function getQueryFromUrl(url) {
    return url.split('/queries/')[1].slice(0,12)
  }
}
@isanuk
Copy link

isanuk commented Nov 26, 2018

This works, but it doesn't Refresh after report updates. Any tips/tricks for getting the formula to update after report refresh?

@scottgeller
Copy link

In the code in the gist, if you replace

return Utilities.parseCsv(csvText)

with

return Utilities.parseCsv(csvText).map(function(row){return row.map(function(col){return isNaN(col) ? col : Number(col)})})

the import function will support native numeric columns. otherwise, it will import integer columns as string in google sheets. this makes it so it will respect native integer columns.

@akltree
Copy link

akltree commented Oct 17, 2019

I keep getting this error when the script runs through a trigger:

TypeError: Cannot find function split in object {year=2019, month=10, day-of-month=17, day-of-week=4, week-of-year=42, hour=14, minute=42, second=44, timezone=UTC, authMode=FULL, triggerUid=2359458}. (line 45, file "Code")

Anyone know what this is / how to fix it?

@akltree
Copy link

akltree commented Oct 21, 2019

To resolve the above error, we defined the URL directly in line 2:

var url = 'https://app.mode.com/lemontreefoods/reports/xxxx/queries/yyyyyyyy'

@andrewjdmccann
Copy link

Thanks a ton, @bstancil. In order to get refreshing reports I've forked this, but you did all the heavy lifting :D

https://gist.github.com/andrewjdmccann/0ebc89d6f1108d4934d1faf8e6ddb068

@broach312
Copy link

Is it possible to adjust this to accommodate for fields that have addresses with commas? I have one field with an address that is being separated into multiple fields at each comma.

@SheaJackman
Copy link

hi all. Im a total amateur at this so forgive me. I think Google recently updated a lot of features in Google Sheets and there is no "Tools > Script editor" or "File > Project properties" and "Script properties" option anymore. It seems to have been replaced with Apps Scripts https://screenshot.click/18-36-ymebl-52ktg.png

Does anyone know what the new steps would be?
Shea

@julietteandrea
Copy link

julietteandrea commented Jan 3, 2023

This script has been moved to the Mode Help site. Going forward, this GH gist will no longer be maintained and may be outdated. Please use the version on the help site for any future reference.

If you have any questions regarding the script, please don't hesitate to reach out to Mode through their official help center.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment