Created
August 14, 2020 00:13
-
-
Save sergiopvilar/3e849a4b4c3ddc9c71ba65ee48b96545 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Instruções em ingles para rodar este script: https://elifk.us/en/retrieving-your-strava-data-with-google-app-scripts/ | |
*/ | |
var CLIENT_ID = ''; // Client ID do Strava | |
var CLIENT_SECRET = ''; // Client Secret do Strava | |
var SPREADSHEET_NAME = "Registro de Exercícios"; // Nome da Planilha | |
var SPREADSHEET_ID = ""; // ID da Planilha | |
var SHEET_NAME = "Dados"; // Nome da página de dados | |
var DEBUG = false; | |
// Se você quiser detalhes como 'description' | |
var RETRIEVE_DETAILS = false; | |
var ONLY_WITH_HEARTRATE = false; | |
// Se você quiser buscar apenas um tipo de atividade. Ex: var FILTER_BY_TYPE = "Ride"; | |
var FILTER_BY_TYPE = ""; | |
// Lista de campos em http://developers.strava.com/docs/reference/#api-Activities-getActivityById | |
var DATA_FIELDS = ['start_date_local', 'distance', 'moving_time', 'average_speed', 'type','calories', 'elapsed_time']; | |
// Cabeçalhos da planilha | |
var HEADING_FOR_DATA_FIELDS = ['Timestamp', 'Data Formatada', 'Distância', 'Tempo de Movimento', 'Velocidade Média', 'Tipo Atividade', 'Calorias', 'Tempo Total']; | |
var STRAVA_API_SCOPE = "activity:read_all"; | |
var ALL = false; | |
function onOpen() { | |
var ui = SpreadsheetApp.getUi(); | |
ui.createMenu('Sincronizar') | |
.addItem('Obter dados do Strava', 'retrieveData') | |
.addToUi(); | |
} | |
/** | |
* Configura o serviço | |
*/ | |
function getService() { | |
return OAuth2.createService('Strava') | |
// URLs do Strava | |
.setAuthorizationBaseUrl('https://www.strava.com/oauth/authorize') | |
.setTokenUrl('https://www.strava.com/oauth/token') | |
// ClientID e ClientSecret | |
.setClientId(CLIENT_ID) | |
.setClientSecret(CLIENT_SECRET) | |
// Nome da função que vai lidar com o callback | |
.setCallbackFunction('authCallback') | |
// Persiste a autorização | |
.setPropertyStore(PropertiesService.getUserProperties()) | |
// Incluir atividades privadas | |
.setScope(STRAVA_API_SCOPE) | |
} | |
/** | |
* OAuth callback. | |
*/ | |
function authCallback(request) { | |
var service = getService(); | |
var authorized = service.handleCallback(request); | |
if (authorized) { | |
return HtmlService.createHtmlOutput('Success!'); | |
} else { | |
return HtmlService.createHtmlOutput('Denied'); | |
} | |
} | |
function logAccessToken() { | |
var service = getService(); | |
Logger.log("Access token: "+ service.getAccessToken()); | |
} | |
/** | |
* Reseta o estado da autorização | |
*/ | |
function reset() { | |
var service = getService(); | |
service.reset(); | |
} | |
/** | |
* Autoriza e faz o request à API | |
*/ | |
function run() { | |
var service = getService(); | |
if (service.hasAccess()) { | |
var url = 'https://www.strava.com/api/v3/athlete'; | |
var response = UrlFetchApp.fetch(url, { | |
headers: { | |
Authorization: 'Bearer ' + service.getAccessToken() | |
} | |
}); | |
var result = JSON.parse(response.getContentText()); | |
Logger.log(JSON.stringify(result, null, 2)); | |
} else { | |
var authorizationUrl = service.getAuthorizationUrl(); | |
Logger.log('Open the following URL and re-run the script: %s', | |
authorizationUrl); | |
} | |
} | |
function retrieveData() { | |
// Valida as variáveis | |
validateConfig() | |
// Se a planilha tá vazia, busca todos os dados | |
var service = getService(); | |
if (service.hasAccess()) { | |
var sheet = getStravaSheet(); | |
var unixTime = retrieveLastDate(sheet); | |
// Ou se ALL é setado como true | |
var url = ALL ? 'https://www.strava.com/api/v3/athlete/activities?per_page=100' : 'https://www.strava.com/api/v3/athlete/activities?per_page=100&after=' + unixTime; | |
var response = UrlFetchApp.fetch(url, { | |
headers: { | |
Authorization: 'Bearer ' + service.getAccessToken() | |
} | |
}); | |
var result = JSON.parse(response.getContentText()); | |
if (result.length == 0) { | |
Logger.log("No new data"); | |
return; | |
} | |
if (RETRIEVE_DETAILS) { | |
retrieveAndInsertActivityDetailsForActivityList(result); | |
} | |
var data = convertData(result); | |
if (data.length == 0) { | |
Logger.log("No new data with the used filters (ONLY_WITH_HEARTRATE, FILTER_BY_TYPE)"); | |
return; | |
} | |
insertData(sheet, data); | |
} else { | |
var authorizationUrl = service.getAuthorizationUrl(); | |
Logger.log('Open the following URL and re-run the script: %s', | |
authorizationUrl); | |
} | |
} | |
function validateConfig() { | |
if (DATA_FIELDS.length != HEADING_FOR_DATA_FIELDS.length) { | |
Logger.log("The length of DATA_FIELDS does not match with the length of HEADING_FOR_DATA_FIELDS. "); | |
Logger.log(DATA_FIELDS.length.toString()+" != "+HEADING_FOR_DATA_FIELDS.length.toString() + " Please fix."); | |
} | |
} | |
function retrieveAndInsertActivityDetailsForActivityList(activityListResult) { | |
var service = getService(); | |
if (service.hasAccess()) { | |
var url = 'https://www.strava.com/api/v3/activities/'; | |
var query = '?include_all_efforts=false'; | |
for (var i = 0; i < activityListResult.length; i++) { | |
var activityURL = url + activityListResult[i]['id'] + query; | |
var response = UrlFetchApp.fetch(activityURL, { | |
headers: { | |
Authorization: 'Bearer ' + service.getAccessToken() | |
} | |
}); | |
var result = JSON.parse(response.getContentText()); | |
extendObj(activityListResult[i], result); | |
} | |
} else { | |
var authorizationUrl = service.getAuthorizationUrl(); | |
Logger.log('Open the following URL and re-run the script: %s', | |
authorizationUrl); | |
} | |
} | |
function extendObj(obj1, obj2){ | |
for (var key in obj2){ | |
if(!obj1.hasOwnProperty(key)){ | |
obj1[key] = obj2[key]; | |
} | |
} | |
} | |
function retrieveLastDate(sheet) { | |
var lastRow = sheet.getLastRow(); | |
var unixTime = 0; | |
if (lastRow > 0) { | |
var dateCell = sheet.getRange(lastRow, 1); | |
var dateString = dateCell.getValue(); | |
var date = new Date((dateString || "").replace(/-/g,"/").replace(/[TZ]/g," ")); | |
unixTime = date/1000; | |
} | |
return unixTime; | |
} | |
function convertData(result) { | |
var data = []; | |
for (var i = 0; i < result.length; i++) { | |
if ((!ONLY_WITH_HEARTRATE || result[i]["has_heartrate"]) && (FILTER_BY_TYPE.length == 0 || result[i]["type"] == FILTER_BY_TYPE)) { | |
var item = []; | |
for (var j = 0; j < DATA_FIELDS.length; j++) { | |
var field_name = DATA_FIELDS[j]; | |
var single_data = result[i][field_name]; | |
// Distância é retornada em metros, dividimos por 1000 para converter para kms | |
if (field_name == 'distance') { | |
single_data = single_data/1000; | |
} | |
// Tempo é dado em segundos, dividimos por 60 para termos minutos | |
if(field_name == 'moving_time') { | |
single_data = single_data/60; | |
} | |
// Convertemos a data para o formato Brasileiro, assim tempos também um cabeçalho adicional | |
if(field_name == 'start_date_local') { | |
item.push(single_data); | |
single_data = new Date(single_data).toLocaleDateString("en-GB", {timeZone: "GMT"}) + " " + new Date(single_data).toLocaleTimeString("en-GB", {timeZone: "GMT"}); | |
} | |
// Se o dado não existe, vira "-" | |
if (single_data == undefined) { | |
single_data = '-' | |
} | |
item.push(single_data); | |
} | |
data.push(item); | |
} | |
} | |
return data; | |
} | |
function getStravaSheet() { | |
var spreadsheet = SpreadsheetApp.openById(SPREADSHEET_ID); | |
var sheet = getOrCreateSheet(spreadsheet, SHEET_NAME); | |
return sheet; | |
} | |
function insertData(sheet, data) { | |
var header = HEADING_FOR_DATA_FIELDS; | |
ensureHeader(header, sheet); | |
Logger.log(header); | |
var lastRow = sheet.getLastRow(); | |
var range = sheet.getRange(lastRow+1,1,data.length,DATA_FIELDS.length +1); | |
range.setValues(data); | |
} | |
function ensureHeader(header, sheet) { | |
// Só adiciona o cabeçalho se a planilha estiver vazia | |
if (sheet.getLastRow() == 0) { | |
if (DEBUG) { | |
Logger.log('Sheet is empty, adding header.'); | |
} | |
sheet.appendRow(header); | |
return true; | |
} else { | |
if (DEBUG) { | |
Logger.log('Sheet is not empty, not adding header.') | |
} | |
return false; | |
} | |
} | |
function getOrCreateSheet(spreadsheet, sheetName) { | |
var sheet = spreadsheet.getSheetByName(sheetName); | |
if (!sheet) { | |
if (DEBUG) Logger.log('Sheet "%s" does not exist, adding new one.', sheetName); | |
sheet = spreadsheet.insertSheet(sheetName) | |
} | |
return sheet; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment