# firstapiR_http.R version 2.0.1===============================================
# Contains functions that retrieve data from the FIRST API server and format
# the data as either XML, JSON, or as R data frames.
# Server URLs and other parameters
.staging_url <- "https://frc-staging-api.firstinspires.org"
.production_url <- "https://frc-api.firstinspires.org"
.first_http_api_version <- "v2.0"
.package_version <- "2.0.1"
.user_agent_name <- paste("firstapiR: Version", .package_version)
.default_season <- as.integer(format(Sys.Date(), "%Y"))
# GetSession() ================================================================
#' Create a firstapiR session
#'
#' Every firstapiR function requires a Session object as its first parameter.
#'
#' The Session object is an R list that contains the FIRST API username and
#' authorization key, season, format, and a boolean value that specifies whether
#' to use the staging server instead of the production server.
#'
#' The \code{key} argument may be set to the value "key". If this is done, the
#' firstapiR functions will skip the HTTP request and will extract example data
#' from the R/sysdata.rda file, which is included with the firstapiR package.
#' This function is for testing and demonstrations when no internet connection
#' or valid authorization key is available. Example data frames returned by
#' firstapiR functions will have their \emph{local_test_data} attribute set to
#' \code{TRUE} and the \emph{time_downloaded} attribute will be set to the date
#' and time that the example data was downloaded from the server and stored in
#' the R/sysdata.rda file.
#'
#' Throws an error if \code{season}, \code{format}, or \code{staging} arguments
#' are incorrect.
#'
#' @param username A character vector containing the username assigned by FIRST.
#' @param key A character vector containing the authorization key assigned by
#' FIRST, or the value "key".
#' @param season An integer vector containing the 4-digit year. Must be equal to
#' or less than the current season and greater than or equal to 2015.
#' Optional: defaults to the current year.
#' @param format A character vector that specifies the data format that will be
#' returned by firstapiR functions. Can be "json", "data.frame", or "xml"
#' (case insensitive). Optional: defaults to "data.frame".
#' @param staging A logical vector. If set to \code{TRUE}, firstapiR uses the
#' staging URL. Optional: defaults to \code{FALSE}.
#'
#' @return A Session object containing all GetSession parameters.
#' The class attribute is set to c("list", "Session")
#'
#' @export
#'
#' @examples
#' sn <- GetSession("myUserName", "myAuthorizationKey")
#' sn <- GetSession("myUserName", "myAuthorizationKey", season = 2015)
#' sn$format <- "xml"
GetSession <- function(username, key,
season = .default_season,
format = "data.frame",
staging = FALSE){
# Check for invalid arguments
if((season < 2015) || (season > .default_season + 1))
stop("season must be an integer between 2015 and next year")
if(!(tolower(format) %in% c("data.frame", "xml", "json")))
stop("format must be 'data.frame', 'xml', or 'json'")
if(!is.logical(staging))
stop("staging must be either TRUE or FALSE")
# Build Session
session <- list(username = username,
key = key,
staging = staging,
season = season,
format = format)
class(session) <- append(class(session), "Session")
return(session)
}
# GetServerStatus() ===========================================================
#' Get the status of the FIRST API server
#'
#' See the \emph{API Index} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0}
#'
#' @param session A Session object created with \code{GetSession()}.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to
#' c("data.frame, "Status").
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{name}: character
#' \item \emph{version}: double
#' \item \emph{status}: character}
#'
#' @section Attributes:
#' \itemize{
#' \item \emph{url}: Character vector containing URL submitted to FIRST API
#' server.
#' \item \emph{local_test_data}: \code{TRUE} if data was extracted from
#' R/sysdata.rda file.
#' \item \emph{local_url}: Character vector containing URL used to download
#' local data.
#' \item \emph{time_downloaded}: Character vector containing the local
#' system time that the object was downladed from the FIRST API server.
#' Formatted an an http date and time string.
#' \item \emph{last_modified}: Character vector containing the date and time
#' that the data was last modified by the FIRST API server.}
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key")
#' status <- GetServerStatus(sn)
GetServerStatus <- function(session) {
status <- .GetHTTP(session, "status")
# Skip rest of function for XML or JSON results
if(session$format != "data.frame") return(status)
class(status) <- append(class(status), "Status")
return(status)
}
# GetSeason() =================================================================
#' Get high-level information for an FRC season
#'
#' Returns information for the season specified in the session list (see
#' documentation for the GetSession function for additional details.)
#'
#' See the \emph{Season Summary} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to
#' c("data.frame, "Season"). Returns a logical vector of length one with value
#' \code{NA} if data is unchanged since date and time passed in arguments
#' \code{mod_since} or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{eventCount}: integer
#' \item \emph{gameName}: factor
#' \item \emph{kickoff}: factor
#' \item \emph{rookieStart}: integer
#' \item \emph{teamCount}: integer
#' \item \emph{FRCChampionships.name}: character
#' \item \emph{FRCChampionships.startDate}: character
#' \item \emph{FRCChampionships.location}: character}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#' @seealso Refer to \code{\link{GetSession}} for information on specifying the
#' FRC season.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2015, staging = TRUE)
#' summary <- GetSeason(sn)
GetSeason <- function(session, mod_since = NULL, only_mod_since = NULL) {
season <- .GetHTTP(session, "", mod_since, only_mod_since)
# Skip rest of function for empty, XML, or JSON results
if(is.na(season) || session$format != "data.frame") return(season)
class(season) <- append(class(season), "Season")
return(season)
}
# GetDistricts() ==============================================================
#' Get a list of FIRST districts
#'
#' This function returns a list of all current districs, including their titles
#' and codes. District codes are used as parameters for several other FIRST API
#' functions.
#'
#' See the \emph{District Listings} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/districts}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "Districts"). Returns a logical vector of length one with value \code{NA}
#' if data is unchanged since date and time passed in arguments
#' \code{mod_since} or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{code}: character
#' \item \emph{name}: character
#' \item \emph{districtCount}: integer}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key")
#' districts <- GetDistricts(sn)
GetDistricts <- function(session, mod_since = NULL, only_mod_since = NULL) {
url <- "districts"
districts <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip rest of function for empty, XML, or JSON results
if(is.na(districts) || session$format != "data.frame") return(districts)
# Shorten the column names to reduce amount of typing required.
names(districts) <- .TrimColNames(names(districts))
class(districts) <- append(class(districts), "Districts")
return(districts)
}
# GetEvents() =================================================================
#' Get information about FRC events
#'
#' See the \emph{Event Listings} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' \code{GetEvents} will accept either the \code{team} or \code{district}
#' parameters, neither parameter, or both parameters. If neither \code{team} nor
#' \code{district} are specified, \code{GetEvents} returns all FRC events for
#' the competition season. If \code{team} is specified, the results are filtered
#' to only the events in which the FRC team participated. Similarly, if
#' \code{district} is specified, the results are filtered to only the events
#' that occurred within the specified district. If \code{exclude_district} is
#' set to TRUE, then only non-district events are returned. The \code{district}
#' and \code{exclude_district} events may not be specified at the same time.
#'
#' Throws an error if \code{team} is specified and any other arguments are
#' specified, or if both the \code{district} and \code{exclude_district}
#' arguments are specified.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/events?
#' teamNumber=team&districtCode=district&excludeDistrict=district}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param event A character vector containing a FIRST API event code. If event
#' is specified, \code{GetEvents()} will return results only for the specified
#' event. Optional.
#' @param team An integer vector containing a team number. Optional
#' @param district A character vector containing the FIRST API district code
#' (see \code{GetDistricts()}). If \code{district} is specified,
#' \code{GetTeams()} will filter results to only the events in the specified
#' district. Optional.
#' @param exclude_district A logical vector. If set to \code{TRUE}, district
#' events are excluded from results. Optional.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "Events"). Returns a logical vector of length one with value \code{NA} if
#' data is unchanged since date and time passed in arguments \code{mod_since}
#' or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{code}: character
#' \item \emph{divisionCode}: character
#' \item \emph{name}: character
#' \item \emph{type}: factor ('Regional', 'DistrictEvent',
#' 'DistrictChampionship', 'ChampionshipSubdivision',
#' 'ChampionshipDivision', 'Championship', 'Offseason')
#' \item \emph{districtCode}: factor ('CHM', 'FIM', 'IN', 'MAR', 'NC',
#' 'PCH', 'PNW')
#' \item \emph{venue}: character
#' \item \emph{city}: character
#' \item \emph{stateprov}: factor
#' \item \emph{country}: factor
#' \item \emph{timezone}: factor
#' \item \emph{dateStart}: character
#' \item \emph{dateEnd}: character
#' \item \emph{eventCount}: integer}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' team5803_events <- GetEvents(sn, team = 5803)
#' pacificNW_events <- GetEvents(sn, district = 'PNW')
#' team360_nondist_events <- GetEvents(sn, team = 360, exclude_district = TRUE)
GetEvents <- function(session, event = NULL, team = NULL,
district = NULL, exclude_district = NULL,
mod_since = NULL, only_mod_since = NULL) {
# Check for unallowed combinations of arguments.
if(!is.null(event) && (!is.null(team) || !is.null(district) ||
!is.null(exclude_district)))
stop("If you specify an event, you cannot specify any other arguments.")
if(!is.null(district) && !is.null(exclude_district))
stop("You cannot specify both the district and exclude_district arguments.")
event_args <- list(eventCode = event, teamNumber = team,
districtCode = district, excludeDistrict = exclude_district)
url <- .AddHTTPArgs("events", event_args)
# Send HTTP request
events <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip rest of function for empty, XML, or JSON results
if(is.na(events) || session$format != "data.frame") return(events)
# Shorten the column names to reduce amount of typing required.
names(events) <- .TrimColNames(names(events))
# Convert categorical coluns to factor data types.
events <- .FactorColumns(events, c("type", "districtCode", "stateprov",
"country", "timezone"))
class(events) <- append(class(events), "Events")
return(events)
}
# GetTeams() ==================================================================
#' Get details on FRC teams
#'
#' Provides lists of FRC teams for specified events, districts, and states. With
#' no parameters (except for \code{session}), \code{GetTeams} will provide a
#' list of all FRC teams.
#'
#' Because the length of the \code{GetTeams} response can be several thousand
#' lines long, the FIRST API server will break up its response into several
#' pages when the number of teams in the response exceeds 65. For the data frame
#' format, \code{GetTeams} will send a request to the FIRST API server and
#' determine from the first response whether additional HTTP requests are
#' necessary to retrieve all requested data. \code{GetTeams} will then merge all
#' responses into a single data frame. For XML and JSON formats, the user will
#' have to call \code{GetTeams} for each page of data, specifying the page with
#' the \code{page} argument.
#'
#' See the \emph{Team Listings} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/teams&eventCode=event
#' ?districtCode=district?state=state?page=2}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param team An integer vector containing a team number. Optional.
#' @param event Character A FIRST API event code (see \code{GetEvents()}). If
#' event is specified, \code{GetTeams()} will filter results to all teams
#' particpating in the specified event. Optional.
#' @param district A character vector containing a FIRST API district code
#' (see \code{GetDistricts()}). If specified, the FIRST API server will filter
#' the response to only the teams in the specified district. Optional.
#' @param state A character vector containing a state name, spelled out entirely
#' (i.e., 'Idaho', \emph{not} 'ID'). If state is specified, \code{GetTeams()}
#' will filter results to all teams in the specified state. Optional.
#' @param page An integer vector that specifyies which page of results should be
#' returned. Optional. Use only for XML or JSON formats.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "Teams"). Returns a logical vector of length one with value \code{NA} if
#' data is unchanged since date and time passed in arguments \code{mod_since}
#' or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{team}: character
#' \item \emph{nameFull}: character
#' \item \emph{nameShort}: character
#' \item \emph{city}: character
#' \item \emph{stateProv}: factor
#' \item \emph{country}: factor
#' \item \emph{website}: character
#' \item \emph{rookieYear}: integer
#' \item \emph{robotName}: character
#' \item \emph{districtCode}: factor
#' \item \emph{teamCountTotal}: integer
#' \item \emph{teamCountPage}: integer
#' \item \emph{pageCurrent}: integer
#' \item \emph{pageTotal}: integer}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' GetTeams(sn, state = "California")
#' GetTeams(sn, district = "FIM")
#' GetTeams(sn, event = "CMP-CARVER")
GetTeams <- function (session, team = NULL, event = NULL, district = NULL,
state = NULL, page = NULL, mod_since = NULL,
only_mod_since = NULL) {
# Check for unallowed combinations of arguments.
if(!is.null(team) && (!is.null(event) || !is.null(district) ||
!is.null(state)))
stop("If you specify a team, you cannot specify any other arguments")
if(session$format == "data.frame" && !is.null(page)) {
page <- NULL
warning("Do not specify GetTeams page argument for data frame format")
}
# Assemble URL
team_args <- list(teamNumber = team, eventCode = event,
districtCode = district, state = state, page = page)
url <- .AddHTTPArgs("teams", team_args)
# FIRST teams API can return multiple pages, and each page requires a separate
# HTTP request, so results will be stored in a list containing one list item
# for each page.
teams <- list()
# Send HTTP request and get first page of data.
teams[[1]] <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty, XML, or JSON formats.
if(is.na(teams) || session$format != "data.frame") return(teams[[1]])
# Get total number of pages
pages <- teams[[1]]$pageTotal[1]
# If results consist of more than one page, send an HTTP request to get each
# page, storing each page of results in the list.
if(pages > 1) {
team_attr <- attributes(teams[[1]])
for(page in 2:pages) {
team_args$page <- page
url <- .AddHTTPArgs("teams", team_args)
teams[[page]] = .GetHTTP(session, url)
}
# For data frames, merge all pages into one data frame.
if(session$format == "data.frame") {
teams_df <- teams[[1]]
for(page in 2:pages) {
teams_df <- merge(teams_df, teams[[page]], all = TRUE)
}
teams <- teams_df
# Replace attributes that were stripped due to merging
attr(teams, "url") <- team_attr$url
attr(teams, "local_test_data") <- team_attr$local_test_data
attr(teams, "local_url") <- team_attr$local_url
attr(teams, "time_downloaded") <- team_attr$time_downloaded
attr(teams, "last_modified") <- team_attr$last_modified
attr(teams, "mod_since") <- team_attr$mod_since
attr(teams, "only_mod_since") <- team_attr$only_mod_since
}
} else
teams <- teams[[1]]
# Shorten the column names to reduce amount of typing required.
names(teams) <- .TrimColNames(names(teams))
names(teams)[names(teams) == "teamNumber"] <- "team"
# Convert categorical coluns to factor data types.
teams <- .FactorColumns(teams, c("districtCode", "stateProv", "country"))
class(teams) <- append(class(teams), "Teams")
return(teams)
}
# GetSchedule() ===============================================================
#' Get the match schedule for a specific event
#'
#' Returns either the qualification schedule or the playoff schedule, based on
#' the value of the \code{level} argument. The \code{start} and \code{end}
#' arguments allow filtering of results to specific matches.
#'
#' The data frame returned by \code{GetSchedule()} is in team shape, i.e., each
#' row contains data for a single team and there are six rows per match. Use
#' \code{ToAllianceShape()} or \code{ToMatchShape} to convert the data frame to
#' a three-teams-per-row shape or a six-teams-per-row shape.
#'
#' See the \emph{Event Schedule} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/schedule/event?
#' tournamentLevel=level&teamNumber=team&start=start&end=end}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param event A character vector containing a FIRST API event code
#' (see \code{GetEvents}).
#' @param level A character vector containing either \emph{"qual"} or
#' \emph{"playoff"}. Defaults to \emph{"qual"}. Optional.
#' @param team An integer vector containing a team number. Optional.
#' @param start An integer vector containing the earliest match to return.
#' Optional.
#' @param end An integer vector containing the latest match to return. Optional.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "Schedule"). Returns a logical vector of length one with value \code{NA} if
#' data is unchanged since date and time passed in arguments \code{mod_since}
#' or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{match}: integer
#' \item \emph{description}: character
#' \item \emph{level}: factor
#' \item \emph{field}: character
#' \item \emph{start}: character
#' \item \emph{team}: factor
#' \item \emph{alliance}: factor (Blue, Red)
#' \item \emph{station}: factor (Red1, Red2, Red3, Blue1, Blue2, Blue3)
#' \item \emph{surrogate}: logical}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' PNW_champs_qual_sched <- GetSchedule(sn, "PNCMP")
#' qual_matches_5_to_10 <- GetSchedule(sn, "PNCMP", start=5, end=10)
#' CWU_playoff_sched <- GetSchedule(sn, "WAAMV", level='playoff')
#' frc4911_matches_thru_25 <- GetSchedule(sn, "PNCMP", team=4911, end=25)
GetSchedule <- function (session, event, level = "qual", team = NULL,
start = NULL, end = NULL, mod_since = NULL,
only_mod_since = NULL) {
# Build URL
sched_args <- list(tournamentLevel = level, teamNumber = team, start = start,
end = end)
url <- .AddHTTPArgs(paste("schedule", event, sep = "/"), sched_args)
# Send HTTP request
sched <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty results or XML, and JSON formats.
if(is.na(sched) || session$format != "data.frame") return(sched)
# Delete 'Schedule.' from the beginning of column names.
names(sched) <- .TrimColNames(names(sched))
# The FIRST API returns nested schedule data nested data. The remainder of
# this function is necessary to either extract the nested data into new
# columns or to add rows so that the scheule data can be saved as csv data.
# Add columns for team number, station, and surrogate
sched["team"] <- vector(mode = "integer", length = nrow(sched))
sched["alliance"] <- vector(mode = "character", length = nrow(sched))
sched["station"] <- vector(mode = "character", length = nrow(sched))
sched["surrogate"] <- vector(mode = "logical", length = nrow(sched))
# Extract teams and delete nested teams column.
teams <- sched$teams
sched$teams <- NULL
# Expand the matches data frame so there are six rows per match.
sched <- sched[sort(rep(1:nrow(sched), 6)), ]
# Fill in team and station data.
for(mtch in 1:length(teams)) {
for(tm in 1:6) {
mrow <- (mtch-1)*6 + tm
sched$team[[mrow]] <- teams[[mtch]][["teamNumber"]][[tm]]
sched$station[[mrow]] <- teams[[mtch]][["station"]][[tm]]
sched$surrogate[[mrow]] <- teams[[mtch]][["surrogate"]][[tm]]
#Vrow.names(sched)[mrow] <- paste(mtch, sched$station[[mrow]], sep = ".")
}
}
# Set column names to shorter, easier to type values
names(sched)[names(sched) == "tournamentLevel"] <- "level"
names(sched)[names(sched) == "startTime"] <- "start"
names(sched)[names(sched) == "matchNumber"] <- "match"
# Fill in alliance data
sched$alliance <- substr(sched$station, 1, nchar(sched$station) - 1)
# Transform categorical columns into factors.
sched <- .FactorColumns(sched, c("team", "station", "field",
"level", "alliance"))
# Set row and column order
sched <- sched[order(sched$match, sched$station), ]
cols.order <- c("match", "description", "level", "field", "start",
"team", "alliance", "station", "surrogate")
sched <- .SetColumnOrder(sched, cols.order)
row.names(sched) <- tolower(paste(substr(level, 1, 1), sched$match,
sched$station, sep = "."))
attr(sched, "shape") <- "team"
class(sched) <- append(class(sched), "Schedule")
return(sched)
}
# GetHybridSchedule() =========================================================
#' Get the match schedule and results
#'
#' For matches that have been played, \code{GetHybridSchedule} returns the teams
#' assigned to the match and the match results. If the mtach has not yet been
#' played, the assigned teams and schedule data are returned, but the result
#' fields are blank.
#'
#' The data frame returned by \code{GetHybridSchedule()} is in team shape, i.e.,
#' each row contains data for a single team and there are six rows per match.
#' Use \code{ToAllianceShape()} or \code{ToMatchShape()} to convert the data
#' frame to a three-teams-per-row shape or a six-teams-per-row shape.
#'
#' See the \emph{Hybrid Schedule} section of the FIRST API documentation for
#' more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/schedule/event/level/
#' hybrid?start=start&end=end}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param event A character vector containing a FIRST API event code
#' (see \code{GetEvents}).
#' @param level A character vector containing either \emph{"qual"} or
#' \emph{"playoff"}. Optional: defaults to \emph{"qual"}.
#' @param start An integer vector containing the earliest match to return.
#' Optional.
#' @param end An integer vector containing the latest match to return. Optional.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "HybridSchedule"). Returns a logical vector of length one with value
#' \code{NA} if data is unchanged since date and time passed in arguments
#' \code{mod_since} or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{match}: integer
#' \item \emph{description}: character
#' \item \emph{level}: factor
#' \item \emph{start}: character
#' \item \emph{actualStart}: character
#' \item \emph{team}: factor
#' \item \emph{alliance}: factor (Blue, Red)
#' \item \emph{station}: factor (Red1, Red2, Red3, Blue1, Blue2, Blue3)
#' \item \emph{surrogate}: logical
#' \item \emph{disqualified}: logical
#' \item \emph{scoreFinal, scoreAuto, scoreFoul}: integer}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' Philometh_qual_sched <- GetHybridSchedule(sn, event = "ORPHI")
#' CWU_playoffs <- GetHybridSchedule(sn, event = "WAELL", level = "playoff",
#' start = 3, end = 6)
GetHybridSchedule <- function(session, event, level = "qual", start = NULL,
end = NULL, mod_since = NULL,
only_mod_since = NULL) {
# Check for prohibited combinations of arguments
# Not required because GetSchedule has no prohibited combinations.
# Build URL
sched_args <- list(start = start, end = end)
url <- .AddHTTPArgs(paste("schedule", event, level, "hybrid", sep = "/"),
sched_args)
# Send HTTP request
sched <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty, XML, or JSON formats.
if(is.na(sched) || session$format != "data.frame") return(sched)
# Delete 'Schedule.' from the beginning of column names.
names(sched) <- .TrimColNames(names(sched))
# The FIRST API returns nested schedule data nested data. The remainder of
# this function is necessary to either extract the nested data into new
# columns or to add rows so that the scheule data can be saved as csv data.
# Add columns for team number, station, and surrogate
sched["teamNumber"] <- vector(mode = "integer", length = nrow(sched))
sched["station"] <- vector(mode = "character", length = nrow(sched))
sched["surrogate"] <- vector(mode = "logical", length = nrow(sched))
sched["disqualified"] <- vector(mode = "logical", length = nrow(sched))
# Add combined scores columns
sched["scoreFinal"] <- vector(mode = "integer", length = nrow(sched))
sched["scoreFoul"] <- vector(mode = "integer", length = nrow(sched))
sched["scoreAuto"] <- vector(mode = "integer", length = nrow(sched))
# Extract teams and delete nested teams column.
teams <- sched$teams
sched$teams <- NULL
# Expand the matches data frame so there are six rows per match.
sched <- sched[sort(rep(1:nrow(sched), 6)), ]
# Fill in team and station data.
for(mtch in 1:length(teams)) {
for(tm in 1:6) {
mrow <- (mtch-1)*6 + tm
sched$teamNumber[[mrow]] <- teams[[mtch]][["teamNumber"]][[tm]]
sched$station[[mrow]] <- teams[[mtch]][["station"]][[tm]]
sched$surrogate[[mrow]] <- teams[[mtch]][["surrogate"]][[tm]]
sched$surrogate[[mrow]] <- teams[[mtch]][["dq"]][[tm]]
# Extract red and blue scores into combined scoreing columns
if(substr(sched$station[mrow], 1, 1) == 'R')
score <- "scoreRed"
else
score <- "scoreBlue"
sched$scoreFinal[[mrow]] <- sched[[paste(score, "Final", sep="")]][[mrow]]
sched$scoreFoul[[mrow]] <- sched[[paste(score, "Foul", sep = "")]][[mrow]]
sched$scoreAuto[[mrow]] <- sched[[paste(score, "Auto", sep = "")]][[mrow]]
}
}
# Remove redundent score columns
sched$scoreRedFinal <- NULL
sched$scoreBlueFinal <- NULL
sched$scoreRedFoul <- NULL
sched$scoreBlueFoul <- NULL
sched$scoreRedAuto <- NULL
sched$scoreBlueAuto <- NULL
# Set column names
names(sched)[names(sched) == "tournamentLevel"] = "level"
names(sched)[names(sched) == "matchNumber"] <- "match"
names(sched)[names(sched) == "teamNumber"] <- "team"
names(sched)[names(sched) == "startTime"] <- "start"
names(sched)[names(sched) == "actualStartTime"] <- "actualStart"
# Fill in alliance data
sched$alliance <- substr(sched$station, 1, nchar(sched$station) - 1)
# Transform categorical columns into factors.
sched <- .FactorColumns(sched, c("team", "alliance", "station", "level"))
# Set row names
row.names(sched)<- tolower(paste(substr(level, 1, 1), sched$match,
sched$station, sep = "."))
# Set column order
cols.order <- c("match", "description", "level", "start", "actualStart",
"team", "alliance", "station", "surrogate", "disqualified",
"scoreFinal", "scoreAuto", "scoreFoul")
sched <- .SetColumnOrder(sched, cols.order)
attr(sched, "shape") <- "team"
sched <- sched[order(sched$match, sched$station), ]
class(sched) <- append(class(sched), "HybridSchedule")
return(sched)
}
# GetMatchResults() ===========================================================
#' Get match scores and participating teams
#'
#' The data frame returned by \code{GetMatchResults()} is in team shape, i.e.,
#' each row contains data for a single team and there are six rows per match.
#' Use \code{ToAllianceShape()} or \code{ToMatchShape()} to convert the data
#' frame to a three-teams-per-row shape or a six-teams-per-row shape.
#'
#' See the \emph{Match Results} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/matches/event
#' ?tournamentLevel=level&teamNumber=team&matchNumber=match&start=start&end=end}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param event A character vector containing a FIRST API event code
#' (see \code{GetEvents}).
#' @param level A character vector containing either \emph{"qual"} or
#' \emph{"playoff"}. Defaults to \emph{"qual"}. Optional.
#' @param team An integer vector containing a team number. Optional. Cannot
#' specify \code{match} when \code{team} is specified.
#' @param match An integer vector containing a match number. Optional. If
#' specified, \code{GetMatchResults} returns results for only the specified
#' match. If \code{level} is not specified, returns the results for the
#' qualification match. To get playoff match results, set \code{level} to
#' \emph{"playoff"}. Cannot specify \code{team} when \code{match} is
#' specified.
#' @param start An integer vector containing the earliest match to return.
#' Optional.
#' @param end An integer vector containing the latest match to return. Optional.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "MatchResults"). Returns a logical vector of length one with value
#' \code{NA} if data is unchanged since date and time passed in arguments
#' \code{mod_since} or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{match}: integer
#' \item \emph{description}: character
#' \item \emph{level}: factor
#' \item \emph{actualStart}: character
#' \item \emph{postResult}: character
#' \item \emph{team}: factor
#' \item \emph{alliance}: factor (Blue, Red)
#' \item \emph{station}: factor (Red1, Red2, Red3, Blue1, Blue2, Blue3)
#' \item \emph{disqualified}: logical
#' \item \emph{scoreFinal, scoreAuto, scoreFoul}: integer}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' GetMatchResults(sn, "PNCMP", level="qual")
#' GetMatchResults(sn, "PNCMP", team="2990")
#' GetMatchResults(sn, "WAAMV", match=2, level="playoff")
#' GetMatchResults(sn, "CMP-ARCHIMEDES", level="qual", start=10, end=20)
GetMatchResults <- function(session, event, level = "qual", team = NULL,
match = NULL, start = NULL, end = NULL,
mod_since = NULL, only_mod_since = NULL) {
# Check for unallowed combinations of arguments.
if((!is.null(match) || !is.null(start) || !is.null(end)) && is.null(level))
stop("You must specify the level when you specify match, start, or end.")
if(!is.null(team) && !is.null(match))
stop("You cannot specify both a team and match number.")
if(!is.null(match) && (!is.null(start) || !is.null(end)))
stop("You cannot specify start or end if you specify match.")
# Assemble URL
result_args <- list(tournamentLevel = level, teamNumber = team,
matchNumber = match, start = start, end = end)
url <- .AddHTTPArgs(paste("matches", event, sep = "/"), result_args)
# Send HTTP request and get data.
matches <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty, XML, or JSON formats.
if(is.na(matches) || session$format != "data.frame") return(matches)
# Delete 'Matches.' from the beginning of column names.
names(matches) <- substr(names(matches), 9, 100)
# Convert first character of column names to lower case
substr(names(matches), 1, 1) <- tolower(substr(names(matches), 1, 1))
# The FIRST API returns nested schedule data. The remainder of this function
# is necessary to extract the nested data into new rows so that the scheule
# data can be saved as csv data.
# Add columns for each operating station
matches["teamNumber"] <- vector(mode = "integer", length = nrow(matches))
matches["station"] <- vector(mode = "character", length = nrow(matches))
matches["disqualified"] <- vector(mode = "logical", length = nrow(matches))
# Add combined scores columns
matches["scoreFinal"] <- vector(mode = "integer", length = nrow(matches))
matches["scoreFoul"] <- vector(mode = "integer", length = nrow(matches))
matches["scoreAuto"] <- vector(mode = "integer", length = nrow(matches))
# Extract teams and delete nested teams column.
teams <- matches$teams
matches$teams <- NULL
# Expand the matches data frame so there are six rows per match.
xMatches <- matches[sort(rep(1:nrow(matches), 6)), ]
# Fill in team and station data.
for(mtch in 1:length(teams)) {
for(tm in 1:6) {
mrow <- (mtch-1)*6 + tm
xMatches$teamNumber[[mrow]] <- teams[[mtch]][["teamNumber"]][[tm]]
xMatches$station[[mrow]] <- teams[[mtch]][["station"]][[tm]]
xMatches$disqualified[[mrow]] <- teams[[mtch]][["dq"]][[tm]]
# Extract red and blue scores into combined scoreing columns
if(substr(xMatches$station[mrow], 1, 1) == 'R')
score <- "scoreRed"
else
score <- "scoreBlue"
xMatches$scoreFinal[[mrow]] <- xMatches[[paste(score, 'Final', sep="")]][[mrow]]
xMatches$scoreFoul[[mrow]] <- xMatches[[paste(score, 'Foul', sep = "")]][[mrow]]
xMatches$scoreAuto[[mrow]] <- xMatches[[paste(score, 'Auto', sep = "")]][[mrow]]
}
}
# Remove redundent score columns
xMatches$scoreRedFinal <- NULL
xMatches$scoreBlueFinal <- NULL
xMatches$scoreRedFoul <- NULL
xMatches$scoreBlueFoul <- NULL
xMatches$scoreRedAuto <- NULL
xMatches$scoreBlueAuto <- NULL
matches <- xMatches
# Set column names to shorter, easier to type values.
names(matches)[names(matches) == "tournamentLevel"] <- "level"
names(matches)[names(matches) == "matchNumber"] <- "match"
names(matches)[names(matches) == "teamNumber"] <- "team"
names(matches)[names(matches) == "actualStartTime"] <- "actualStart"
names(matches)[names(matches) == "postResultTime"] <- "postResult"
# Fill in alliance data
matches$alliance <- substr(matches$station, 1, nchar(matches$station) - 1)
# Convert categorical data into factors
matches <- .FactorColumns(matches, c("team", "alliance", "station", "level"))
# Set row names to match.station
row.names(matches) <- tolower(paste(substr(level, 1, 1), matches$match,
matches$station, sep = "."))
# Set column order
cols.order <- c("match", "description", "level", "actualStart", "postResult",
"team", "alliance", "station", "disqualified",
"scoreFinal", "scoreAuto", "scoreFoul")
matches <- .SetColumnOrder(matches, cols.order)
matches <- matches[order(matches$match, matches$station), ]
attr(matches, "shape") <- "team"
class(matches) <- append(class(matches), "MatchResults")
return(matches)
}
# GetScores ====================================================================
#' Get detailed match scores
#'
#' The results vary depending on the season requested. The 2016 data fields are
#' listed here. See the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for data fields for prior seasons.
#' The data frame contains two rows for each match, one for blue and the other
#' for red.
#'
#' \code{GetScores()} contains both the blue and red alliance scores for each
#' match, but it does not list the teams assigned to each alliance. Use
#' firstapiR \code{MergeResults()} function to merge the data frames returned by
#' \code{GetHybridSchedule} and \code{GetScores()} to create a data frame that
#' contains both team numbers and detailed scores:
#' \code{MergeResults(hybrid_df, scores_df)} should do the trick.
#'
#' See the \emph{Detailed Scores} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#'The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/matches/event/level?
#' teamNumber=team&matchNumber=match&start=start&end=end}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param event A character vector containing a FIRST API event code
#' (see \code{GetEvents}).
#' @param level A character vector containing either \emph{"qual"} or
#' \emph{"playoff"}. Defaults to \emph{"qual"}. Optional.
#' @param team An integer vector containing a team number. Optional. Cannot
#' specify \code{match} when \code{team} is specified.
#' @param match An integer vector containing a match number. Optional. If
#' specified, \code{GetMatchResults} returns results for only the specified
#' match. If \code{level} is not specified, returns the results for the
#' qualification match. To get playoff match results, set \code{level} to
#' \emph{"playoff"}. Cannot specify \code{team} when \code{match} is
#' specified.
#' @param start An integer vector containing the earliest match to return.
#' Optional.
#' @param end An integer vector containing the latest match to return. Optional.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "Scores"). Returns a logical vector of length one with value \code{NA} if
#' data is unchanged since date and time passed in arguments \code{mod_since}
#' or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{level}: character
#' \item \emph{match}: integer
#' \item \emph{audienceGroup}: factor
#' \item \emph{alliance}: factor
#' \item \emph{robot1Auto, robot2Auto, robot3Auto}: factor
#' \item \emph{autoBouldersLow, autoBouldersHigh}: integer
#' \item \emph{teleopBouldersLow, teleopBouldersHigh}: integer
#' \item \emph{towerFaceA, towerFaceB, towerFaceC}: factor
#' \item \emph{towerEndStrength}: integer
#' \item \emph{teleopTowerCaptured, teleopDefensesBreached}: logical
#' \item \emph{position1, position2, position3, position4,
#' position5}: factor
#' \item \emph{position1Crossings, position2Crossings, position3Crossings,
#' position4Crossings, position5Crossings}: integer
#' \item \emph{foulCount, techFoulCount}: integer
#' \item \emph{autoPoints, autoReachPoints, autoCrossingPoints,
#' autoBoulderPoints}: integer
#' \item \emph{teleopPoints, teleopCrossingPoints, teleopBoulderPoints,
#' teleopChallengePoints, teleopScalePoints}: integer
#' \item \emph{breachPoints, capturePoints}: integer
#' \item \emph{adustPoints, foulPoints, totalPoints}: integer}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data attributes
#' returned by this function.
#' @seealso Refer to \code{\link{MergeResults}} for guidance on how to merge the
#' \emph{Scores} and \emph{HybridSchedule} data frames.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' archimedes_qual_scores <- GetScores(sn, event = "ARCHIMEDES")
#' first_10_qual_matches <- GetScores(sn, event = "WAELL", start = 1, end = 10)
#' qual_match_10_scores <- GetScores(sn, event = "WAELL", match = 15)
GetScores <- function(session, event, level = "qual", team = NULL,
match = NULL, start = NULL, end = NULL,
mod_since = NULL, only_mod_since = NULL) {
# Check for unallowed combinations of arguments.
if(!is.null(team) && !is.null(match))
stop("You cannot specify both a team and match number")
if(!is.null(match) && (!is.null(start) || !is.null(end)))
stop("You cannot specify start or end if you specify match")
# Assemble URL
score_args <- list(teamNumber = team, matchNumber = match, start = start,
end = end)
url <- .AddHTTPArgs(paste("scores", event, level, sep = "/"), score_args)
# Send HTTP request and get data.
scores <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty, XML, or JSON formats.
if(is.na(scores) || session$format != "data.frame") return(scores)
# Delete 'MatcheScores.' from the beginning of column names.
names(scores) <- .TrimColNames(names(scores))
# Convert first character of column names to lower case
substr(names(scores), 1, 1) <- tolower(substr(names(scores), 1, 1))
# Extract nested alliances column from data frame.
alliances <- scores$alliances
scores$alliances <- NULL
# Expand data frame to include six rows for each match.
scores <- scores[sort(rep(1:nrow(scores), 2)), ]
# Get names of nested Alliance columns.
alliance_col_names <- names(alliances[[1]])
for(col_name in alliance_col_names) {
col_type <- if(is.integer(alliances[[1]][[col_name]]))
"integer"
else
"character"
scores[col_name] <- vector(mode = col_type, length = nrow(scores))
}
# Extract nested data into new columns
for(mtch in 1:length(alliances)){
for(col_name in alliance_col_names){
df_row <- (mtch - 1)*2
scores[[col_name]][[df_row + 1]] <- alliances[[mtch]][[col_name]][[1]]
scores[[col_name]][[df_row + 2]] <- alliances[[mtch]][[col_name]][[2]]
}
}
# Transform categorical columns into factors or logical vectors
for(col_name in names(scores)){
if(is.character(scores[[col_name]])) {
lvls <- tolower(unique(scores[[col_name]]))
if(length(lvls) <= 2) {
if(("true" %in% lvls) || ("false" %in% lvls))
scores[[col_name]] <- as.logical(scores[[col_name]])
} else
scores[[col_name]] <- factor(scores[[col_name]])
}
}
# Set column names to shorter, easier to type values.
names(scores)[names(scores) == "matchLevel"] <- "level"
names(scores)[names(scores) == "matchNumber"] <- "match"
# Set row names to be integers.
scores$alliance <- as.character(scores$alliance)
row.names(scores) <- tolower(paste(scores$match, scores$alliance, sep = "."))
scores$alliance <- factor(scores$alliance)
class(scores) <- append(class(scores), "Scores")
return(scores)
}
# GetAlliances() ==============================================================
#' Get teams assigned to playoff alliances
#'
#' Returns a list of playoff alliances, including the alliance captains and
#' teams that were selected for each alliance.
#'
#' See the \emph{Event Alliances} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/alliances/event}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param event A character vector containing a FIRST API event code
#' (see \code{GetEvents}).
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "Alliances"). Returns a logical vector of length one with value \code{NA}
#' if data is unchanged since date and time passed in arguments
#' \code{mod_since} or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{number}: integer
#' \item \emph{name}: character
#' \item \emph{captain, round1, round2, round3}: integer
#' \item \emph{backup, backupReplaced}: integer
#' \item \emph{count}: integer}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' GetAlliances(sn, "WAAMV")
GetAlliances <- function (session, event, mod_since = NULL,
only_mod_since = NULL) {
# Assemble URL
url <- paste("alliances/", event, sep="")
# Send HTTP request
alliances <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty, XML, or JSON formats.
if(is.na(alliances) || session$format != "data.frame") return(alliances)
# Remove prefix from column names.
names(alliances) <- .TrimColNames(names(alliances))
class(alliances) <- append(class(alliances), "Alliances")
return(alliances)
}
# GetRankings() ===============================================================
#' Get team rankings
#'
#' The results vary depending on the season requested. The 2016 data fields are
#' listed here. See the FIRST API documentation for data fields for prior
#' seasons.
#'
#' See the \emph{Event Rankings} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/rankings/event?teamNumber=team&top=top}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param event A character vector containing a FIRST API event code
#' (see \code{GetEvents}).
#' @param team An integer vector containing a team number. Optional.
#' @param top An integer vector specifying the number of teams to return,
#' starting with the top ranked team.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "Rankings"). Returns a logical vector of length one with value \code{NA} if
#' data is unchanged since date and time passed in arguments \code{mod_since}
#' or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{rank}: integer
#' \item \emph{team}: integer
#' \item \emph{sortOrder1, sortOrder2, sortOrder3, sortOrder4, sortOrder5,
#' sortOrder6}: integer or numeric.
#' \item \emph{wins, losses, ties}: integer
#' \item \emph{qualAverage}: numeric
#' \item \emph{dq}: integer
#' \item \emph{matchesPlayed}: integer}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' CWU_rankings <- GetRankings(sn, "WAAMV")
#' frc1938_rank_PNWChamps <- GetRankings(sn, "PNCMP", team = 1983)
#' archimedes_top5_teams <- GetRankings(sn, "ARCHIMEDES", top = 5)
GetRankings <- function (session, event, team = NULL, top = NULL,
mod_since = NULL, only_mod_since = NULL) {
# Check for unallowed combinations of arguments.
if(!is.null(team) && !is.null(top))
stop("You cannot specify both the team and top argument")
# Assemble URL
rank_args <- list(teamNumber = team, top = top)
url <- .AddHTTPArgs(paste("rankings", event, sep = "/"), rank_args)
# Send HTTP request and get data.
rankings <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty, XML, or JSON formats.
if(is.na(rankings) || session$format != "data.frame") return(rankings)
# Delete 'Rankings.' from the beginning of column names.
names(rankings) <- .TrimColNames(names(rankings))
# Standardize column names across different firstapiR functions
names(rankings)[names(rankings) == "teamNumber"] <- "team"
class(rankings) <- append(class(rankings), "Rankings")
return(rankings)
}
# GetAwards() =================================================================
#' Get the awards that were presented at an event
#'
#' See the \emph{Event Awards} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#' \code{https://frc-api.firstinspires.org/v2.0/season/awards/event/team}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param event A character vector containing a FIRST API event code
#' (see \code{GetEvents}). Must specify the \code{event} argument, the
#' \code{team} argument, or both.
#' @param team An integer vector containing a team number. Optional. Must
#' specify the \code{event} argument, the \code{team} argument, or both.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object or a data.frame with class set to c("data.frame,
#' "Awards"). Returns a logical vector of length one with value \code{NA} if
#' data is unchanged since date and time passed in arguments \code{mod_since}
#' or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{awardId}: integer
#' \item \emph{teamId}: integer
#' \item \emph{eventId}: integer
#' \item \emph{eventDivisionId}: logical
#' \item \emph{eventCode}: character
#' \item \emph{name}: character
#' \item \emph{series}: integer
#' \item \emph{team}: integer
#' \item \emph{fullTeamName}: character
#' \item \emph{person}: character}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' GetAwards(sn, "PNCMP")
#' GetAwards(sn, team = 360)
#' GetAwards(sn, "PNCMP", 360)
GetAwards <- function(session, event = NULL, team = NULL,
mod_since = NULL, only_mod_since = NULL) {
# Check for incorrect combinations of arguments.
if(is.null(event) && is.null(team))
stop("You must specify either a team number or event code")
# Assemble URL -- GetAwards URL format is different from other functions.
url <- "awards"
if(!is.null(event))
url <- paste(url, event, sep = "/")
if(!is.null(team))
url <- paste(url, team, sep = "/")
# Send HTTP request
awards <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty, XML, or JSON formats.
if(is.na(awards) || session$format != "data.frame") return(awards)
# Remove column name prefix
names(awards) <- .TrimColNames(names(awards))
# Standardize column names across different firstapiR functions
names(awards)[names(awards) == "teamNumber"] <- "team"
class(awards) <- append(class(awards), "Awards")
return(awards)
}
# GetAwardsList() =============================================================
#' Get a list of all available awards for a season
#'
#' See the \emph{Awards Listing} section of the FIRST API documentation at
#' \url{http://docs.frcevents2.apiary.io/#} for more details.
#'
#' The FIRST API URL format is:
#'
#' \code{https://frc-api.firstinspires.org/v2.0/season/awards/list}
#'
#' @param session A Session object created with \code{GetSession()}.
#' @param mod_since A character vector containing an HTTP formatted date and
#' time. Returns \code{NA} if no changes have been made to the requested data
#' since the date and time provided. Optional.
#' @param only_mod_since A character vector containing an HTTP formatted date
#' and time. This function only returns data that has changed since the date
#' and time provided. Optional.
#'
#' @return Depending on the \code{session$format} value, returns JSON text, an
#' XML::XMLDocument object, or a data.frame with class set to c("data.frame,
#' "AwardsList"). Returns a logical vector of length one with value \code{NA}
#' if data is unchanged since date and time passed in arguments
#' \code{mod_since} or \code{only_mod_since}.
#'
#' @section Columns:
#' \enumerate{
#' \item \emph{awardId}: integer
#' \item \emph{eventType}: character
#' \item \emph{description}: character
#' \item \emph{forPerson}: logical}
#'
#' @seealso Refer to \code{\link{Standard_attributes}} for data
#' attributes returned by this function.
#'
#' @export
#'
#' @examples
#' sn <- GetSession("username", "key", season = 2016)
#' GetAwardsList(sn)
GetAwardsList <- function(session, mod_since = NULL, only_mod_since = NULL) {
# Assemble URL
url <- "awards/list"
# Send HTTP request
alist <- .GetHTTP(session, url, mod_since, only_mod_since)
# Skip remainder of function for empty, XML, or JSON formats.
if(is.na(alist) || session$format != "data.frame") return(alist)
# Remove column name prefix
names(alist) <- .TrimColNames(names(alist))
class(alist) <- append(class(alist), "AwardsList")
return(alist)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.