I work as an exercise physiologist, and I use the rStrava package for creating an API between R and my Strava account for doing session analyses. The API works fine on its own (outside a function), but inside a function it does not run. In the post, there is an example of the code outside and inside a function.

Expected behavior from the function is that it returns a data frame called by what I give to df_name, with a resolution of either "low", "medium" or "high" on the session ID, id, that I get from my Strava account.

Beneath is the standalone code (not in a function) that works. Note that I have edited out my personal information using #'s.

library(rStrava)
    
# Data from my personal Strava API
    
app_name <- "#"
app_client_id <- "#"
app_secret <- "#"
    
# The token that makes the connection to my Strava data
    
strava_token <- httr::config(token = strava_oauth(app_name,
                                                      app_client_id,
                                                      app_secret,
                                                      app_scope = "activity:read_all"))
    
# Accessing the data
    
my_acts <- get_activity_list(strava_token)
id <- {#} # This is the ID from Strava that is unique to a session
strava_data <- get_activity_streams(my_acts,
                                        strava_token,
                                        id = id,
                                        resolution = "high")

The problem is that if I wrap this into a function, the expected behaviour is not achieved. This is the code inside a function:

generate_strava_session_data <- function(id, 
                                         df_name,
                                         resolution){
                                         
  
  library(rStrava)
  library(tidyverse)
  
  # Data from my personal Strava API
  
  app_name <- "#"
  app_client_id <- "#"
  app_secret <- "#"
  
  # The token that makes the connection to my Strava data
  
  strava_token <- httr::config(token = strava_oauth(app_name,
                                                    app_client_id,
                                                    app_secret,
                                                    app_scope = "activity:read_all"))

# These five print lines were added to debug

  print(strava_token)
  print(id)
  print(resolution)
  print(.Last.error)
  print(.Last.warning)

  # Accessing the data  
  
  my_acts <- get_activity_list(strava_token)
  df_name <- get_activity_streams(my_acts,
                             strava_token,
                             id = id,
                             resolution = resolution)
  
}

The code makes the connection to Strava and I'm able to authorize the connection, but it then stops. The five print lines added for debugging all, and I was given the error that "Object "high" not found".

Any help would be greatly appreciated.

1

There are 1 best solutions below

1
On BEST ANSWER

As noted in the comments, assignments in function are only visible in local environment, i.e. in the function itself. Same applies to objects you pass as function arguments -- altering those inside your function does not change object values in global environment. The return value of the function is the value of the last evaluated expression or explicit return() call, in other words your function should end with something like

  ...
  get_activity_streams(my_acts,
                       strava_token,
                       id = id,
                       resolution = resolution)
}

(no assignment) or

  ...
  return(df_name)
}

There's also <<- operator that would allow you to alter global environment objects from functions, but generally, one should try to avoid such side effects as much as possible, this also applies to loading libraries inside a function.

Without knowing what happened next in your code, it's difficult to guess if you attempted to use functions return value or assumed that the object passed as a df_name argument would hold the dataset. A working example might look something like this:

library(dplyr)
library(rStrava)

generate_strava_session_data <- function(resolution = "medium", id = NULL){
  if (file.exists('.httr-oauth')){
    # use cached token, if exists
    stoken <- httr::config(token = readRDS('.httr-oauth')[[1]])
  } else {
    # load secrets from environment variables, 
    # .Renviron file of current project is one convenient option to set those up
    stoken <- httr::config(
      token = strava_oauth(Sys.getenv("STRAVA_APP_NAME"), 
                           Sys.getenv("STRAVA_CLIENT_ID"), 
                           Sys.getenv("STRAVA_APP_SECRET"), 
                           app_scope="activity:read_all",
                           cache = TRUE))
  }
  # limit request to past 10 days
  my_acts <- get_activity_list(stoken, 
                               after = Sys.Date() - as.difftime(10, units = "days"))
  streams_df <- get_activity_streams(my_acts,
                                     stoken = stoken,
                                     id = id,
                                     resolution = resolution)
  # function's return value is return() argument or
  # the value of the last evaluated expression; 
  # otherwise it just returns NULL
  return(streams_df)
}

generate_strava_session_data() %>% 
  summarise(dist = max(distance), 
            avg_hr = mean(heartrate), 
            move_ratio = sum(moving) / n(), 
            duration = max(time) %>% lubridate::seconds_to_period(),
            .by = id) %>% 
  select(-id)
#>      dist  avg_hr move_ratio   duration
#> 1  4.6950 124.610      0.999    29M 19S
#> 2 37.2897 122.367      0.942 7H 12M 24S
#> 3  3.5909 134.767      0.996    28M 27S

Created on 2023-10-15 with reprex v2.0.2