Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup_fastf1 + vignette #126

Merged
merged 19 commits into from
Jul 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/check-standard.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ jobs:
fail-fast: false
matrix:
config:
- {os: macos-latest, r: 'release', py: "3.x" }
- {os: windows-latest, r: 'release', py: "3.x" }
- {os: ubuntu-latest, r: 'devel', py: "3.x", http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'oldrel-1', py: "3.x" }
- {os: ubuntu-latest, r: 'release', py: "3.8" }
- {os: ubuntu-latest, r: 'release', py: "3.9" }
- {os: ubuntu-latest, r: 'release', py: "3.10"}
- {os: ubuntu-latest, r: 'release', py: "3.11"}
- {os: macos-latest, r: "release", py: "3.x" }
- {os: windows-latest, r: "release", py: "3.x" }
- {os: ubuntu-latest, r: "devel", py: "3.x", http-user-agent: "release"}
- {os: ubuntu-latest, r: "oldrel-1", py: "3.x" }
- {os: ubuntu-latest, r: "release", py: "3.8" }
- {os: ubuntu-latest, r: "release", py: "3.9" }
- {os: ubuntu-latest, r: "release", py: "3.10"}
- {os: ubuntu-latest, r: "release", py: "3.11"}

env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: f1dataR
Title: F1 Data in R
Version: 1.1.0
Version: 1.2.0
Authors@R:
c(person(given = "Santiago",
family = "Casanova",
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export(load_session_laps)
export(load_sprint)
export(load_standings)
export(plot_fastest)
export(setup_fastf1)
export(theme_dark_f1)
import(reticulate)
importFrom(magrittr,"%>%")
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# f1dataR 1.2.0

* Added a helper function for setting up `fastf1` connection.
* Improved testing coverage
* Code style and format cleanups

# f1dataR 1.1.0

* Updated load_driver_telemetry to use `laps` parameter, allowing for a choice of 'fastest', 'all', or a numbered lap. Note a numbered lap requires `fastf1` version 3.0 or greater (#78)
Expand Down
17 changes: 11 additions & 6 deletions R/load_driver_telemetry.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
#' @description Receives season, race number, driver code, and an optional fastest lap only
#' argument to output car telemetry for the selected situation.
#' Example usage of this code can be seen in the Introduction vignette (run
#' `vignette('introduction', 'f1dataR')` to read). Multiple drivers' telemetry can be appended
#' \code{vignette('introduction', 'f1dataR')} to read). Multiple drivers' telemetry can be appended
#' to one data frame by the user.
#'
#' If you have trouble with errors mentioning 'fastf1' or 'get_fastf1_version()' read the
#' "Setup FastF1 Connection" vignette (run \code{vignette('setup_fastf1', 'f1dataR')}).
#'
#' @param season number from 2018 to current season (defaults to current season).
#' @param round number from 1 to 23 (depending on season selected). Also accepts race name.
#' @param session the code for the session to load Options are `'FP1'`, `'FP2'`, `'FP3'`,
Expand Down Expand Up @@ -127,9 +130,11 @@ get_driver_telemetry <-
log_level = "WARNING",
fastest_only = lifecycle::deprecated(),
race = lifecycle::deprecated()) {
lifecycle::deprecate_warn("1.0.0",
"get_driver_telemetry()",
"load_driver_telemetry()")
lifecycle::deprecate_warn(
"1.0.0",
"get_driver_telemetry()",
"load_driver_telemetry()"
)

load_driver_telemetry(
season = season,
Expand All @@ -139,5 +144,5 @@ get_driver_telemetry <-
laps = ifelse(fastest_only, "fastest", "all"),
log_level = log_level,
race = race
)
}
)
}
3 changes: 3 additions & 0 deletions R/load_race_session.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#' session data streams. See the \href{https://theoehrly.github.io/Fast-F1/}{fastf1 documentation}
#' for more details on the data returned by the python API.
#'
#' If you have trouble with errors mentioning 'fastf1' or 'get_fastf1_version()' read the
#' 'Setup FastF1 Connection vignette (run \code{vignette('setup_fastf1', 'f1dataR')}).
#'
#' @param obj_name name assigned to the loaded session to be referenced later.
#' Leave as `'session'` unless otherwise required.
#' @param season number from 2018 to current season. Defaults to current season.
Expand Down
3 changes: 3 additions & 0 deletions R/load_session_laps.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#' Cache directory can be set by setting `option(f1dataR.cache = [cache dir])`,
#' default is the current working directory.
#'
#' If you have trouble with errors mentioning 'fastf1' or 'get_fastf1_version()' read the
#' 'Setup FastF1 Connection vignette (run \code{vignette('setup_fastf1', 'f1dataR')}).
#'
#' @param season number from 2018 to current season. Defaults to current season.
#' @param round number from 1 to 23 (depending on season selected). Also accepts race name.
#' @param session the code for the session to load Options are `'FP1'`, `'FP2'`, `'FP3'`,
Expand Down
62 changes: 58 additions & 4 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ get_ergast_content <- function(url) {
#' @keywords internal
#' @return Year (four digit number) representation of current season, as numeric.


.get_current_season <- function() {
current_season <- ifelse(as.numeric(strftime(Sys.Date(), "%m")) < 3,
as.numeric(strftime(Sys.Date(), "%Y")) - 1,
Expand Down Expand Up @@ -87,11 +86,11 @@ time_to_sec <- function(time) {
} else if (is.numeric(x)) {
x
} else {
split <- as.numeric(strsplit(x, ":",fixed=TRUE)[[1]])
split <- as.numeric(strsplit(x, ":", fixed = TRUE)[[1]])
if (length(split) == 3) {
split[1]*3600 + split[2]*60 + split[3]
split[1] * 3600 + split[2] * 60 + split[3]
} else if (length(split) == 2) {
split[1]*60 + split[2]
split[1] * 60 + split[2]
} else if (length(split) == 1) {
split
}
Expand Down Expand Up @@ -132,3 +131,58 @@ time_to_sec <- function(time) {
#' @inherit .get_fastf1_version title description return
#' @keywords internal
get_fastf1_version <- memoise::memoise(.get_fastf1_version)


#' Setup fastf1 connection
#'
#' @description Set up reticulate using some options from user (or defaults). Helps
#' solve `fastf1` issues - see the Setup FastF1 Connection vignette for more info
#' (run \code{vignette('setup_fastf1', 'f1dataR')}).
#'
#' @param envname a name for the virtualenv or conda environment.
#'
#' For virtualenv, if a name is passed, `reticulate` will use/create the environment in the
#' default location. Alternatively, if providing a full path, `reticulate` will use the specified location.
#' @param conda whether to use conda environments or virtualenvs. Default FALSE (i.e. virtualenv)
#'
#' @export
#'
#' @examples
#' \dontrun{
#' # setup fastf1 connection with all defaults
#' setup_fastf1()
#'
#' # setup with a preexisting conda environment, with a specified name
#' setup_fastf1("example_conda_env", conda = TRUE)
#' }
#'
setup_fastf1 <- function(envname = "f1dataRenv", conda = FALSE) {
if (conda == FALSE) {
if (envname %in% reticulate::virtualenv_list()) {
reticulate::use_virtualenv(envname)
} else if (reticulate:::conda_installed() && envname %in% reticulate::conda_list()$name) {
cli::cli_abort("{.val envname} found in list of conda environments. Did you mean to use that?",
x = "Run the function again with {.param conda} = `TRUE`"
)
} else {
reticulate::virtualenv_create(envname = envname, packages = c("numpy", "fastf1"))
reticulate::use_virtualenv(envname)
}
} else {
if (!reticulate:::conda_installed()) {
cli::cli_abort("Conda is not installed on your system.",
i = "If you wish to use conda please run {.code reticulate::install_miniconda}."
)
}
if (envname %in% reticulate::conda_list()$name) {
reticulate::use_condaenv(envname)
} else if (envname %in% reticulate::virtualenv_list()) {
cli::cli_abort("{.val {envname}} found in list of virtualenv environments. Did you mean to use that?",
x = "Run the function again with {.param conda} = `FALSE`"
)
} else {
reticulate::conda_create(envname = envname, packages = c("numpy", "fastf1"))
reticulate::use_condaenv(envname)
}
}
}
2 changes: 1 addition & 1 deletion README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ When the parameters for season (four digit year), round (number or GP name), ses
```{r load_telemetries}
load_driver_telemetry(2022, round = 4, driver = "PER")

load_driver_telemetry(2018, round = 7,'Q', 'HAM', laps = "fastest")
load_driver_telemetry(2018, round = 7, "Q", "HAM", laps = "fastest")
```

### Lap-by-Lap information
Expand Down
3 changes: 3 additions & 0 deletions man/load_driver_telemetry.Rd

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

3 changes: 3 additions & 0 deletions man/load_race_session.Rd

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

3 changes: 3 additions & 0 deletions man/load_session_laps.Rd

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

31 changes: 31 additions & 0 deletions man/setup_fastf1.Rd

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

1 change: 1 addition & 0 deletions pkgdown/_pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ reference:
- time_to_sec
- clear_f1_cache
- constructor_data
- setup_fastf1

authors:
Santiago Casanova:
Expand Down
7 changes: 5 additions & 2 deletions tests/testthat/test-load_driver_telemetry.R
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ test_that("driver telemetry", {
"`get_driver_telemetry\\(\\)` was deprecated in f1dataR 1.0.0.*"
)
expect_warning(
get_driver_telemetry(season = 2022, race = "Brazil", session = "S", driver = "HAM", fastest_only = TRUE),
"`get_driver_telemetry\\(\\)` was deprecated in f1dataR 1.0.0.*"
expect_warning(
get_driver_telemetry(season = 2022, race = "Brazil", session = "S", driver = "HAM", fastest_only = TRUE),
"`get_driver_telemetry\\(\\)` was deprecated in f1dataR 1.0.0.*"
),
"The `race` argument of `load_driver_telemetry\\(\\)` is deprecated as of f1dataR 1.0.0.*"
)
})
10 changes: 5 additions & 5 deletions tests/testthat/test-load_session_laps.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ test_that("load session laps works", {
# Set testing specific parameters - this disposes after the test finishes
# Note: The test suite can't delete the old fastf1_http_cache.sqlite file
# because python's process has it locked.
if (dir.exists(file.path(getwd(), "tst_session"))) {
unlink(file.path(getwd(), "tst_session"), recursive = TRUE, force = TRUE)
if (dir.exists(file.path(getwd(), "tst_session_laps"))) {
unlink(file.path(getwd(), "tst_session_laps"), recursive = TRUE, force = TRUE)
}
withr::local_file(file.path(getwd(), "tst_session"))
dir.create(file.path(getwd(), "tst_session"), recursive = TRUE)
withr::local_options(f1dataR.cache = file.path(getwd(), "tst_session"))
withr::local_file(file.path(getwd(), "tst_session_laps"))
dir.create(file.path(getwd(), "tst_session_laps"), recursive = TRUE)
withr::local_options(f1dataR.cache = file.path(getwd(), "tst_session_laps"))


laps <- load_session_laps(season = 2022, round = "bahrain")
Expand Down
63 changes: 61 additions & 2 deletions tests/testthat/test-utils.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# helper function to skip tests if we don't have the fastf1 module
skip_if_no_ff1 <- function() {
have_ff1 <- "fastf1" %in% reticulate::py_list_packages()$package
if (!have_ff1) {
skip("fastf1 not available for testing")
}
}

test_that("utility functions work", {
# current season function - also naturally tested in some load_x functions
expect_true(is.numeric(get_current_season()))
Expand All @@ -17,7 +25,58 @@ test_that("utility functions work", {
expect_equal(time_to_sec("12.3456"), 12.3456)
expect_equal(time_to_sec(12.345), 12.345)

expect_equal(time_to_sec(c("12.345", "1:23.456", "12:34:56.789", "12.3456")),
c(12.345, 83.456, 45296.789, 12.3456))
expect_equal(
time_to_sec(c("12.345", "1:23.456", "12:34:56.789", "12.3456")),
c(12.345, 83.456, 45296.789, 12.3456)
)
})

test_that("setup-fastf1 works", {
skip_if_no_ff1()

# Set testing specific parameters - this disposes after the test finishes
# Note: The test suite can't delete the old fastf1_http_cache.sqlite file
# because python's process has it locked.
withr::local_file(file.path(getwd(), "tst_setup"))
if (dir.exists(file.path(getwd(), "tst_setup"))) {
unlink(file.path(getwd(), "tst_setup"), recursive = TRUE, force = TRUE)
}
dir.create(file.path(getwd(), "tst_setup"), recursive = TRUE)
withr::local_options(f1dataR.cache = file.path(getwd(), "tst_setup"))
withr::local_envvar(.new = list(
"WORKON_HOME" = file.path(getwd(), "tst_setup"),
"RETICULATE_PYTHON" = NA
))
withr::defer(reticulate::virtualenv_remove(file.path(getwd(), "tst_setup", "setup_venv"), confirm = FALSE))

expect_false("setup_venv" %in% reticulate::virtualenv_list())

# Different testing environments may or may not have preexisting selected/activated python venv or condaenv.
# This try(suppressWarnings()) set makes sure that the code is run, and we'll test for that which we care about
# (namely the creation of the venv) afterwards
try(suppressWarnings(setup_fastf1(file.path(getwd(), "tst_setup", "setup_venv"), conda = FALSE)))

expect_true("setup_venv" %in% reticulate::virtualenv_list())

if (reticulate:::conda_installed()) {
# Workflow runners or CRAN tests might not have conda

withr::defer(reticulate::conda_remove("setup_conda"))

expect_error(
setup_fastf1("setup_venv", conda = TRUE),
"* found in list of virtualenv environments. Did you mean to use that?"
)
expect_false("setup_conda" %in% reticulate::conda_list()$name)

# Because we set the venv earlier, reticulate won't let you set a second active env without restarting R.
# Alternatively, we can wrap this in 'try' also.
expect_error(setup_fastf1("setup_conda", conda = TRUE), "*failed to initialize requested version of Python")
expect_true("setup_conda" %in% reticulate::conda_list()$name)

expect_error(
setup_fastf1("setup_conda", conda = FALSE),
"* found in list of conda environments. Did you mean to use that?"
)
}
})
Loading
Loading