R/task.r

Defines functions filter_task_info handle_multiple_tasks rtm_date quickdf rbinddf rtm_task_method lookup_task_method_args task_method_args rtm_time

Documented in filter_task_info handle_multiple_tasks lookup_task_method_args quickdf rbinddf rtm_date rtm_task_method rtm_time task_method_args

##' Process task info from rtm_search.
##'
##' @param df a data frame of tasks returned by rtm_search
##' @export
filter_task_info <- function(df) {
  ## when the task series list is 1 entry long, it's not a data frame but
  ## a named list so we need to do some trickery to get it into a data frame
  ## just quickdf and as.data.frame won't do b/c of the potentially
  ## nested nature of its contents
  if (!is.data.frame(df)) {
    list_elems <- c("notes", "participants", "tags", "task")
    atomic_df <- quickdf(df[setdiff(names(df), list_elems)])

    ## add the lists elements as columns one-by-one
    col_or_blank <- function (col) if(length(df[[col]]) > 0) df[[col]] else ""
    atomic_df[["notes"]] <- col_or_blank("notes")
    atomic_df[["participants"]] <- col_or_blank("participants")
    atomic_df[["tags"]] <- col_or_blank("tags")

    ## the task list is a special case, as it's a data frame in its own right
    task <- quickdf(df[["task"]])
    names(task) <- gsub("^", "task\\.", names(task))
    df <- cbind(atomic_df, task)
  }

  ## if tasks are repeating it may be the case that it has multiple
  ## entries per row, we need to test if df[["task"]] is a list
  if (exists("task", where = df) && !is.data.frame(df[["task"]])) {
    df <- handle_multiple_tasks(df)
  }

  ## convert times for all
  time_vars <- c("created", "modified", "task.due",
                 "task.added", "task.completed")
  df[, time_vars] <- plyr::colwise(rtm_date, time_vars)(df)
  df
}

##' Handle the case where a task is repeating and has multiple entries
##' per row.
##'
##' In this case df[["task"]] is a list and needs to be converted to a
##' data frame and merged back into the larger data frame.
##'
##' @param df the list
##' @return the data frame
handle_multiple_tasks <- function(df) {
  for (i in seq_along(df[["task"]])) {
    df[["task"]][[i]][["series_id"]] <- i
  }
  tasks <- Reduce(rbinddf, df[["task"]])
  ## rename tasks according to the convention
  names(tasks) <- gsub("^", "task\\.", names(tasks))
  df[["task"]] <- NULL
  df[["task.series_id"]] <- seq_len(nrow(df))
  df <- merge(df, tasks)
  df[["task.series_id"]] <- NULL
  df
}

##' Convert date stored in RTM server format to R POSIXlt format.
##'
##' @param date date string in RTM server format
##' @return a POSIXlt value
rtm_date <- function(date) {
  strptime(date, "%Y-%m-%dT%H:%M:%SZ", tz = "GMT")
}

##' Convert a list to a data frame
##'
##' this idea and code (quickdf) is attributed to Hadley Wickham
##' http://adv-r.had.co.nz/Profiling.html
##' it's much faster than using as.data.frame, and in our use case
##' we seem to be guaranteed well-formed lists
##' @param l list suitable to be a data frame with quick conversion
##' @return a data frame
quickdf <- function(l) {
  class(l) <- "data.frame"
  attr(l, "row.names") <- .set_row_names(length(l[[1]]))
  l
}

##' Create a data frame out of two lists or data frames.
##'
##' @param x a list or data frame
##' @param y a list or data frame
##' @return a data frame
rbinddf <- function(x, y) {
  rbind(quickdf(x), quickdf(y))
}

##' Call rtm_req with a "rtm.tasks.*" method on the given task.
##'
##' @param method one of the RTM API "rtm.tasks.*" methods
##' @param task a task
##' @param ... additional arguments
##' @return response from the server
##' @export
rtm_task_method <- function(method, task, ...) {
  rtm_method <- paste0("rtm.tasks.", method)
  args <- lookup_task_method_args(method)
  dots <- list(...)

  if (length(args) < 1L)
    stop("Invalid rtm.tasks method: ", method)
  if (length(dots) != length(args)) {
    stop("The number of arguments to rtm_task_method() ",
         "do not match parameters to ", method)
  }

  ## note: not implemented yet, if we only get part of the way through
  ## and lose a connection might want to revert the timeline somehow
  timeline <- rtm_timeline()
  for (i in seq_len(nrow(task))) {
    base_call <- c(list(rtm_method, timeline = timeline,
                        list_id = task[["list_id"]][i],
                        taskseries_id = task[["id"]][i],
                        task_id = task[["task.id"]][i]))
  
    base_call <- c(base_call, setNames(dots, args))
    rsp <- do.call("rtm_req", base_call)
  }
  cat(sprintf("Method \"%s\" completed successfully!\n", method))
  invisible(rsp)
}

##' Look up the argument that a method takes
##'
##' @param method the method as a character string
##' @return the argument as a character string
lookup_task_method_args <- function(method) {
  task_method_args()[[method]]
}

##' A list of the arguments to RTM task method calls
##'
##' @return the list
##' @export
task_method_args <- function() {
  list(addTags = "tags",
       movePriority = "direction",
       removeTags = "tags", setDueDate = "due",
       setEstimate = "estimate", setLocation = "location_id",
       setName = "name", setPriority = "priority",
       setRecurrence = "repeat", setTags = "tags",
       setURL = "url", notes.add = c("note_title", "note_text"))
}
  
##' Convert the time from text to UTC via the RTM service.
##'
##' @param text a character string representing text
##' @return a time
rtm_time <- function(text) {
  rsp <- rtm_req("rtm.time.parse", FALSE, text = text)
  if (exists("time", rsp) && exists("$t", rsp[["time"]]))
    return(rsp[["time"]][["$t"]])
  else
    stop("problem getting time from RTM")
}
jowalski/rtmr documentation built on May 19, 2019, 9:43 p.m.