Skip to content

Commit

Permalink
Recent updates to fix bugs and improve the flexibility of returning o…
Browse files Browse the repository at this point in the history
…r writing data
  • Loading branch information
brianhelsel committed Apr 21, 2023
1 parent 4f81556 commit 3cac084
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 68 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Imports:
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.2.2
RoxygenNote: 7.2.3
Suggests:
knitr
VignetteBuilder: knitr
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# iFitbit 0.1.3
* Add a method to `get_fitbit_activities` that separates Fitbit API calls for time periods greater than 100 days.
* Removed a merge call in `get_fitbit_heart_intraday` that didn't work for an incomplete day of heart rate data.
* Add a start and end date argument to `get_fitbit_report` to allow filtering of the data when `toCSV` or `returnData` is TRUE

# iFitbit 0.1.2

* Added distance, log type, activity level, and heart zones to exercise log
Expand Down
143 changes: 81 additions & 62 deletions R/activity.R
Original file line number Diff line number Diff line change
Expand Up @@ -20,82 +20,99 @@ get_fitbit_activities <- function(token.pathname, resource = "All Resources", st
token <- token[[2]]
user <- token$credentials$user_id
url_activity <- paste0("https://api.fitbit.com/1/", "user/", user, "/", "activities/")
date <- seq.Date(as.Date(start.date), as.Date(end.date), "day")
data <- data.frame(date)

# Check to see if data is greater than 100 days
dateDiff <- difftime(end.date, start.date)

if(dateDiff > 100){
mid.date <- as.Date(start.date) + floor(dateDiff / 2)
start.date <- c(start.date, as.character(mid.date))
end.date <- c(as.character(mid.date + 1), end.date)
}

if("All Resources" %in% resource){
resource <- c("activityCalories", "calories", "distance", "elevation", "floors", "minutesSedentary",
"minutesLightlyActive", "minutesFairlyActive", "minutesVeryActive", "steps")
}

if("activityCalories" %in% resource){
activityCalories.url <- paste0(url_activity, "activityCalories", sprintf("/date/%s/%s.json", start.date, end.date))
activityCalories <- jsonlite::fromJSON(httr::content(httr::GET(activityCalories.url, token), as = "text"))[[1]][2]
colnames(activityCalories) <- "activityCalories"
data <- cbind(data, activityCalories)
}
data <- data.frame(matrix(nrow = 0, ncol = length(resource) + 1))
colnames(data) <- c("date", resource)

if("calories" %in% resource){
calories.url <- paste0(url_activity, "calories", sprintf("/date/%s/%s.json", start.date, end.date))
calories <- jsonlite::fromJSON(httr::content(httr::GET(calories.url, token), as = "text"))[[1]][2]
colnames(calories) <- "calories"
data <- cbind(data, calories)
}
for(i in 1:length(start.date)){
temp_dates <- seq.Date(as.Date(start.date[i]), as.Date(end.date[i]), "day")
temp_data <- data.frame(date = as.character(temp_dates))

if("distance" %in% resource){
distance.url <- paste0(url_activity, "distance", sprintf("/date/%s/%s.json", start.date, end.date))
distance <- jsonlite::fromJSON(httr::content(httr::GET(distance.url, token), as = "text"))[[1]][2]
colnames(distance) <- "distance"
data <- cbind(data, distance)
}
if("activityCalories" %in% resource){
activityCalories.url <- paste0(url_activity, "activityCalories", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
activityCalories <- jsonlite::fromJSON(httr::content(httr::GET(activityCalories.url, token), as = "text"))[[1]][2]
colnames(activityCalories) <- "activityCalories"
temp_data <- cbind(temp_data, activityCalories)
}

if("elevation" %in% resource){
elevation.url <- paste0(url_activity, "elevation", sprintf("/date/%s/%s.json", start.date, end.date))
elevation <- jsonlite::fromJSON(httr::content(httr::GET(elevation.url, token), as = "text"))[[1]][2]
colnames(elevation) <- "elevation"
data <- cbind(data, elevation)
}
if("calories" %in% resource){
calories.url <- paste0(url_activity, "calories", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
calories <- jsonlite::fromJSON(httr::content(httr::GET(calories.url, token), as = "text"))[[1]][2]
colnames(calories) <- "calories"
temp_data <- cbind(temp_data, calories)
}

if("floors" %in% resource){
floors.url <- paste0(url_activity, "floors", sprintf("/date/%s/%s.json", start.date, end.date))
floors <- jsonlite::fromJSON(httr::content(httr::GET(floors.url, token), as = "text"))[[1]][2]
colnames(floors) <- "floors"
data <- cbind(data, floors)
}
if("distance" %in% resource){
distance.url <- paste0(url_activity, "distance", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
distance <- jsonlite::fromJSON(httr::content(httr::GET(distance.url, token), as = "text"))[[1]][2]
colnames(distance) <- "distance"
temp_data <- cbind(temp_data, distance)
}

if("minutesSedentary" %in% resource){
minutesSedentary.url <- paste0(url_activity, "minutesSedentary", sprintf("/date/%s/%s.json", start.date, end.date))
minutesSedentary <- jsonlite::fromJSON(httr::content(httr::GET(minutesSedentary.url, token), as = "text"))[[1]][2]
colnames(minutesSedentary) <- "minutesSedentary"
data <- cbind(data, minutesSedentary)
}
if("elevation" %in% resource){
elevation.url <- paste0(url_activity, "elevation", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
elevation <- jsonlite::fromJSON(httr::content(httr::GET(elevation.url, token), as = "text"))[[1]][2]
colnames(elevation) <- "elevation"
temp_data <- cbind(temp_data, elevation)
}

if("minutesLightlyActive" %in% resource){
minutesLightlyActive.url <- paste0(url_activity, "minutesLightlyActive", sprintf("/date/%s/%s.json", start.date, end.date))
minutesLightlyActive <- jsonlite::fromJSON(httr::content(httr::GET(minutesLightlyActive.url, token), as = "text"))[[1]][2]
colnames(minutesLightlyActive) <- "minutesLightlyActive"
data <- cbind(data, minutesLightlyActive)
}
if("floors" %in% resource){
floors.url <- paste0(url_activity, "floors", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
floors <- jsonlite::fromJSON(httr::content(httr::GET(floors.url, token), as = "text"))[[1]][2]
colnames(floors) <- "floors"
temp_data <- cbind(temp_data, floors)
}

if("minutesFairlyActive" %in% resource){
minutesFairlyActive.url <- paste0(url_activity, "minutesFairlyActive", sprintf("/date/%s/%s.json", start.date, end.date))
minutesFairlyActive <- jsonlite::fromJSON(httr::content(httr::GET(minutesFairlyActive.url, token), as = "text"))[[1]][2]
colnames(minutesFairlyActive) <- "minutesFairlyActive"
data <- cbind(data, minutesFairlyActive)
}
if("minutesSedentary" %in% resource){
minutesSedentary.url <- paste0(url_activity, "minutesSedentary", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
minutesSedentary <- jsonlite::fromJSON(httr::content(httr::GET(minutesSedentary.url, token), as = "text"))[[1]][2]
colnames(minutesSedentary) <- "minutesSedentary"
temp_data <- cbind(temp_data, minutesSedentary)
}

if("minutesVeryActive" %in% resource){
minutesVeryActive.url <- paste0(url_activity, "minutesVeryActive", sprintf("/date/%s/%s.json", start.date, end.date))
minutesVeryActive <- jsonlite::fromJSON(httr::content(httr::GET(minutesVeryActive.url, token), as = "text"))[[1]][2]
colnames(minutesVeryActive) <- "minutesVeryActive"
data <- cbind(data, minutesVeryActive)
}
if("minutesLightlyActive" %in% resource){
minutesLightlyActive.url <- paste0(url_activity, "minutesLightlyActive", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
minutesLightlyActive <- jsonlite::fromJSON(httr::content(httr::GET(minutesLightlyActive.url, token), as = "text"))[[1]][2]
colnames(minutesLightlyActive) <- "minutesLightlyActive"
temp_data <- cbind(temp_data, minutesLightlyActive)
}

if("minutesFairlyActive" %in% resource){
minutesFairlyActive.url <- paste0(url_activity, "minutesFairlyActive", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
minutesFairlyActive <- jsonlite::fromJSON(httr::content(httr::GET(minutesFairlyActive.url, token), as = "text"))[[1]][2]
colnames(minutesFairlyActive) <- "minutesFairlyActive"
temp_data <- cbind(temp_data, minutesFairlyActive)
}

if("steps" %in% resource){
steps.url <- paste0(url_activity, "steps", sprintf("/date/%s/%s.json", start.date, end.date))
steps <- jsonlite::fromJSON(httr::content(httr::GET(steps.url, token), as = "text"))[[1]][2]
colnames(steps) <- "steps"
data <- cbind(data, steps)
if("minutesVeryActive" %in% resource){
minutesVeryActive.url <- paste0(url_activity, "minutesVeryActive", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
minutesVeryActive <- jsonlite::fromJSON(httr::content(httr::GET(minutesVeryActive.url, token), as = "text"))[[1]][2]
colnames(minutesVeryActive) <- "minutesVeryActive"
temp_data <- cbind(temp_data, minutesVeryActive)
}

if("steps" %in% resource){
steps.url <- paste0(url_activity, "steps", sprintf("/date/%s/%s.json", start.date[i], end.date[i]))
steps <- jsonlite::fromJSON(httr::content(httr::GET(steps.url, token), as = "text"))[[1]][2]
colnames(steps) <- "steps"
temp_data <- cbind(temp_data, steps)
}

data <- rbind(data, temp_data)
}

database <- grep(user, list.files(paste0(directory, "/data"), full.names = TRUE), value = TRUE)
Expand Down Expand Up @@ -140,7 +157,8 @@ get_fitbit_exercise_log <- function(token.pathname, limit = 25){
duration <- round(activities[[1]]$activeDuration/60000, 2)
steps <- activities[[1]]$steps
calories <- activities[[1]]$calories
hr <- activities[[1]]$averageHeartRate

hr <- ifelse(!is.null(activities[[1]]$averageHeartRate), activities[[1]]$averageHeartRate, NA)
distance <- ifelse(!is.null(activities[[1]]$distance), activities[[1]]$distance, NA)
distanceUnit <- ifelse(!is.null(activities[[1]]$distanceUnit), activities[[1]]$distanceUnit, NA)
logType <- activities[[1]]$logType
Expand Down Expand Up @@ -195,6 +213,7 @@ get_fitbit_exercise_log <- function(token.pathname, limit = 25){
date = time = type = duration = steps = calories = hr = distance = distanceUnit = logType = activityLevel = heartZones = NA
}


data <- data.frame(cbind(date, time, type, duration, steps, calories, hr, distance, distanceUnit, logType, activityLevel, heartZones))
data <- data[data$duration >= 1 & !is.na(data$duration), ]
database <- grep(user, list.files(paste0(directory, "/data"), full.names = TRUE), value = TRUE)
Expand Down
18 changes: 14 additions & 4 deletions R/heart.R
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ get_fitbit_heart_intraday <- function(token.pathname, start.date = Sys.Date(),
for(date in dates){
print(paste0("Extracting Fitbit Heart Rate Intraday Data for ", user, " on ", date))
heart.url <- paste0("https://api.fitbit.com/1/", "user/", user, "/", "activities/heart", sprintf("/date/%s/1d/%s.json", date, detail.level))
datetime <- seq.POSIXt(as.POSIXct(paste(date, "00:00:00"), format = "%Y-%m-%d %H:%M:%S"), as.POSIXct(paste(date, "23:59:59"), format = "%Y-%m-%d %H:%M:%S"), "min")
datetime <- format(datetime, "%Y-%m-%d %H:%M:%S")
data <- data.frame(datetime)
heart <- jsonlite::fromJSON(httr::content(httr::GET(heart.url, token), as = "text"))

# Calculate intensity by heart rate reserve: ACSM Guidelines for Exercise Testing and Prescription, 11 Edition (page 148, table 5.2)
Expand All @@ -86,7 +83,20 @@ get_fitbit_heart_intraday <- function(token.pathname, start.date = Sys.Date(),
hrr90 <- calculateHRR(max.hr, rest.hr, 0.90)

if(length(heart$`activities-heart-intraday`$dataset)!=0){
data <- merge(x = data, y = data.frame(datetime = paste(date, heart$`activities-heart-intraday`$dataset$time), hr.bpm = heart$`activities-heart-intraday`$dataset$value), by = "datetime", all.x = TRUE)

data = data.frame(datetime = paste(date, heart$`activities-heart-intraday`$dataset$time),
hr.bpm = heart$`activities-heart-intraday`$dataset$value)

data <-
seq(as.POSIXct(date), as.POSIXct(date)+86399, 60) %>%
as.character() %>%
setdiff(as.character(data$datetime)) %>%
{data.frame(
datetime = .,
hr.bpm = rep(NA, length(.))
)} %>%
rbind(data) %>%
.[order(.$datetime), ]

# Add sleep
if(nrow(sleep)!=0){
Expand Down
7 changes: 6 additions & 1 deletion R/report.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#' @param study_name A customized study name for the iFitbit report, Default: 'study'
#' @param report_author A customized report author for the iFitbit report, Default: 'iFitbit'
#' @param reportName PARAM_DESCRIPTION, Default: 'FitbitReport'
#' @param start.date Add a start date to allow filtering of data when returning the data or writing it to a CSV
#' @param end.date Add an end date to allow filtering of data when returning the data or writing it to a CSV
#' @param ... Additional arguments passed to \code{\link[data.table]{fwrite}} or \code{\link[rmarkdown]{render}}
#' @return Returns data set or generates a HTML or CSV file
#' @details Generates an HTML report, returns data, or writes to a CSV file
Expand All @@ -28,7 +30,9 @@ get_fitbit_report <- function(
reports_pathname = NULL,
returnData = TRUE, toHTML = FALSE, toCSV = FALSE,
study_name = "study", report_author = "iFitbit",
reportName = "FitbitReport", ...){
reportName = "FitbitReport",
start.date,
end.date, ...){

# Create a directory for the report if a directory doesn't exist
if(!dir.exists(reports_pathname)) dir.create(reports_pathname)
Expand All @@ -54,6 +58,7 @@ get_fitbit_report <- function(
if(toCSV | returnData){
names(exercise_log)[2:ncol(exercise_log)] <- paste0("exercise_", names(exercise_log)[2:ncol(exercise_log)])
data <- merge(activities, merge(exercise_log, heart, by = "date", all = TRUE), by = "date", all = TRUE)
data %<>% dplyr::filter(date >= start.date & date <= end.date)
attr(data, "device") <- device
}

Expand Down
6 changes: 6 additions & 0 deletions man/get_fitbit_report.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3cac084

Please sign in to comment.