# If you'd like to omit non-matching lines from the result; add ';d' to the end of the expression.
sed -E 's/^(\/\*)(\n)(\*)(.*?)(\n)(\*\/$)|(?<!:)(\/\/)(.*?)$/\2'\4\5\8/gm;t' <<< "// intraday.gs -- Supports downloading intraday (minute-by-minute) step data
// Will not work without special permission from Fitbit
// Simon Bromberg (http://sbromberg.com)
// You are free to use, modify, copy any of the code in this script for your own purposes, as long as it's not for evil
// If you do anything cool with it, let me know!
// Note: there are minor improvements/cleanups still to be made in this file, but it should work as is if everything is setup properly
// See readme on github repo for more information
// Script based on post here http://quantifiedself.com/2014/09/download-minute-fitbit-data/ by Ernesto Ramirez
/*
* Do not change these key names. These are just keys to access these properties once you set them up by running the Setup function from the Fitbit menu
*/
// Key of ScriptProperty for Firtbit consumer key.
var CONSUMER_KEY_PROPERTY_NAME = \"fitbitConsumerKey\";
// Key of ScriptProperty for Fitbit consumer secret.
var CONSUMER_SECRET_PROPERTY_NAME = \"fitbitConsumerSecret\";
var SERVICE_IDENTIFIER = 'fitbit';
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{
name: \"Setup\",
functionName: \"setup\"
},
{
name: \"Authorize\",
functionName: \"showSidebar\"
},
{
name: \"Reset\",
functionName: \"clearService\"
},
{
name: \"Download data\",
functionName: \"refreshTimeSeries\"
}];
ss.addMenu(\"Fitbit\", menuEntries);
}
function isConfigured() {
return getConsumerKey() != \"\" && getConsumerSecret() != \"\";
}
function setConsumerKey(key) {
ScriptProperties.setProperty(CONSUMER_KEY_PROPERTY_NAME, key);
}
function getConsumerKey() {
var key = ScriptProperties.getProperty(CONSUMER_KEY_PROPERTY_NAME);
if (key == null) {
key = \"\";
}
return key;
}
function setLoggables(loggable) {
ScriptProperties.setProperty(\"loggables\", loggable);
}
function getLoggables() {
var loggable = ScriptProperties.getProperty(\"loggables\");
if (loggable == null) {
loggable = LOGGABLES;
} else {
loggable = loggable.split(',');
}
return loggable;
}
function setConsumerSecret(secret) {
ScriptProperties.setProperty(CONSUMER_SECRET_PROPERTY_NAME, secret);
}
function getConsumerSecret() {
var secret = ScriptProperties.getProperty(CONSUMER_SECRET_PROPERTY_NAME);
if (secret == null) {
secret = \"\";
}
return secret;
}
// function saveSetup saves the setup params from the UI
function saveSetup(e) {
setConsumerKey(e.parameter.consumerKey);
setConsumerSecret(e.parameter.consumerSecret);
setLoggables(e.parameter.loggables);
setFirstDate(e.parameter.firstDate);
setLastDate(e.parameter.lastDate);
var app = UiApp.getActiveApplication();
app.close();
return app;
}
function setFirstDate(firstDate) {
ScriptProperties.setProperty(\"firstDate\", firstDate);
}
function getFirstDate() {
var firstDate = ScriptProperties.getProperty(\"firstDate\");
if (firstDate == null) {
firstDate = \"2012-01-01\";
}
return firstDate;
}
function setLastDate(lastDate) {
ScriptProperties.setProperty(\"lastDate\", lastDate);
}
function getLastDate() {
var lastDate = ScriptProperties.getProperty(\"lastDate\");
if (lastDate == null) {
var today = new Date();
lastDate = Utilities.formatDate(new Date(), SpreadsheetApp.getActive().getSpreadsheetTimeZone(),\"yyyy-mm-dd\");
}
return lastDate;
}
// function setup accepts and stores the Consumer Key, Consumer Secret, Project Key, firstDate, and list of Data Elements
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setTitle(\"Setup Fitbit Download\");
app.setStyleAttribute(\"padding\", \"10px\");
var consumerKeyLabel = app.createLabel(\"Fitbit OAuth 2.0 Client ID:*\");
var consumerKey = app.createTextBox();
consumerKey.setName(\"consumerKey\");
consumerKey.setWidth(\"100%\");
consumerKey.setText(getConsumerKey());
var consumerSecretLabel = app.createLabel(\"Fitbit OAuth Consumer Secret:*\");
var consumerSecret = app.createTextBox();
consumerSecret.setName(\"consumerSecret\");
consumerSecret.setWidth(\"100%\");
consumerSecret.setText(getConsumerSecret());
var projectKeyTitleLabel = app.createLabel(\"Project key: \");
var projectKeyLabel = app.createLabel(ScriptApp.getProjectKey());
var firstDate = app.createTextBox().setId(\"firstDate\").setName(\"firstDate\");
firstDate.setName(\"firstDate\");
firstDate.setWidth(\"100%\");
firstDate.setText(getFirstDate());
var lastDate = app.createTextBox().setId(\"lastDate\").setName(\"lastDate\");
lastDate.setName(\"lastDate\");
lastDate.setWidth(\"100%\");
lastDate.setText(getLastDate());
// create the save handler and button
var saveHandler = app.createServerClickHandler(\"saveSetup\");
var saveButton = app.createButton(\"Save Setup\", saveHandler);
// put the controls in a grid
var listPanel = app.createGrid(8, 3);
listPanel.setWidget(1, 0, consumerKeyLabel);
listPanel.setWidget(1, 1, consumerKey);
listPanel.setWidget(2, 0, consumerSecretLabel);
listPanel.setWidget(2, 1, consumerSecret);
listPanel.setWidget(3, 0, app.createLabel(\" * (obtain these at dev.fitbit.com, use OAuth2.0)\"));
listPanel.setWidget(4, 0, projectKeyTitleLabel);
listPanel.setWidget(4, 1, projectKeyLabel);
listPanel.setWidget(5, 0, app.createLabel(\"Start Date for download (yyyy-mm-dd)\"));
listPanel.setWidget(5, 1, firstDate);
listPanel.setWidget(6, 0, app.createLabel(\"End date for download (yyyy-mm-dd)\"));
listPanel.setWidget(6, 1, lastDate);
listPanel.setWidget(7, 0, app.createLabel(\"Very long intervals will not work; exceed Fitbit rate limit and/or function will timeout\"));
// Ensure that all controls in the grid are handled
saveHandler.addCallbackElement(listPanel);
// Build a FlowPanel, adding the grid and the save button
var dialogPanel = app.createFlowPanel();
dialogPanel.add(listPanel);
dialogPanel.add(saveButton);
app.add(dialogPanel);
doc.show(app);
}
function getFitbitService() {
// 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
Logger.log(PropertiesService.getUserProperties());
return OAuth2.createService(SERVICE_IDENTIFIER)
// Set the endpoint URLs, which are the same for all Google services.
.setAuthorizationBaseUrl('https://www.fitbit.com/oauth2/authorize')
.setTokenUrl('https://api.fitbit.com/oauth2/token')
// Set the client ID and secret, from the Google Developers Console.
.setClientId(getConsumerKey())
.setClientSecret(getConsumerSecret())
// 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())
.setScope('activity profile')
.setTokenHeaders({
'Authorization': 'Basic ' + Utilities.base64Encode(getConsumerKey() + ':' + getConsumerSecret())
});
}
function clearService(){
OAuth2.createService(SERVICE_IDENTIFIER)
.setPropertyStore(PropertiesService.getUserProperties())
.reset();
}
function showSidebar() {
var service = getFitbitService();
if (!service.hasAccess()) {
var authorizationUrl = service.getAuthorizationUrl();
var template = HtmlService.createTemplate(
'<a href=\"<?= authorizationUrl ?>\" target=\"_blank\">Authorize</a>. ' +
'Reopen the sidebar when the authorization is complete.');
template.authorizationUrl = authorizationUrl;
var page = template.evaluate();
SpreadsheetApp.getUi().showSidebar(page);
} else {
Logger.log(\"Has access!!!!\");
}
}
function authCallback(request) {
Logger.log(\"authcallback\");
var service = getFitbitService();
var isAuthorized = service.handleCallback(request);
if (isAuthorized) {
Logger.log(\"success\");
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
Logger.log(\"denied\");
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
function getUser() {
var service = getFitbitService();
var options = {
headers: {
\"Authorization\": \"Bearer \" + service.getAccessToken(),
\"method\": \"GET\"
}};
var response = UrlFetchApp.fetch(\"https://api.fitbit.com/1/user/-/profile.json\",options);
var o = JSON.parse(response.getContentText());
return o.user;
}
function refreshTimeSeries() {
if (!isConfigured()) {
setup();
return;
}
var user = getUser();
var doc = SpreadsheetApp.getActiveSpreadsheet();
doc.setFrozenRows(2);
// two header rows
doc.getRange(\"a1\").setValue(user.fullName);
doc.getRange(\"a1\").setComment(\"DOB:\" + user.dateOfBirth)
doc.getRange(\"b1\").setValue(user.country + \"/\" + user.state + \"/\" + user.city);
var options =
{headers:{
\"Authorization\": 'Bearer ' + getFitbitService().getAccessToken(),
\"method\": \"GET\"
}};
var activities = [\"activities/log/steps\"];
var intradays = [\"activities-log-steps-intraday\"];
var lastIndex = 0;
for (var activity in activities) {
var index = 0;
var dateString = getFirstDate();
date = parseDate(dateString);
var table = new Array();
while (1) {
var currentActivity = activities[activity];
try {
var result = UrlFetchApp.fetch(\"https://api.fitbit.com/1/user/-/\" + currentActivity + \"/date/\" + dateString+ \"/\" + dateString + \".json\", options);
} catch(exception) {
Logger.log(exception);
}
var o = JSON.parse(result.getContentText());
var cell = doc.getRange('a3');
var titleCell = doc.getRange(\"a2\");
titleCell.setValue(\"Date\");
var title = currentActivity.split(\"/\");
title = title[title.length - 1];
titleCell.offset(0, 1 + activity * 1.0).setValue(title);
var row = o[intradays[activity]][\"dataset\"];
for (var j in row) {
var val = row[j];
var arr = new Array(2);
arr[0] = dateString + ' ' + val[\"time\"];
arr[1] = val[\"value\"];
table.push(arr);
// set the value index index
index++;
}
date.setDate(date.getDate()+1);
dateString = Utilities.formatDate(date, \"GMT\", \"yyyy-MM-dd\");
if (dateString > getLastDate()) {
break;
}
}
// Batch set values of table, much faster than doing each time per loop run, this wouldn't work as is if there were multiple activities being listed
doc.getRange(\"A3:B\"+(table.length+2)).setValues(table);
}
}
// parse a date in yyyy-mm-dd format
function parseDate(input) {
var parts = input.match(/(\d+)/g);
// new Date(year, month [, date [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[0], parts[1]-1, parts[2]); // months are 0-based
}
// parse a date in 2011-10-25T23:57:00.000 format
function parseDate2(input) {
var parts = input.match(/(\d+)/g);
return new Date(parts[0], parts[1]-1, parts[2], parts[3], parts[4]);
}"
Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for SED, please visit: https://www.gnu.org/software/sed/manual/html_node/The-_0022s_0022-Command.html