Skip to content

Commit a004e70

Browse files
committed
Merge branch 'release/1.21.0'
2 parents a54fc0f + b8f2095 commit a004e70

File tree

6 files changed

+245
-40
lines changed

6 files changed

+245
-40
lines changed

DESCRIPTION

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: redcapcustodian
22
Type: Package
33
Title: Data automation for R-centric workflows with a nod towards REDCap
4-
Version: 1.20.0
4+
Version: 1.21.0
55
Authors@R: c(
66
person("Philip", "Chase",
77
email = "pbc@ufl.edu",
@@ -55,7 +55,8 @@ Imports:
5555
tibble,
5656
tidyr,
5757
vctrs,
58-
jsonlite
58+
jsonlite,
59+
writexl
5960
Suggests:
6061
RSQLite,
6162
digest,

NAMESPACE

+1
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,4 @@ export(write_to_sql_db)
6363
importFrom(magrittr,"%>%")
6464
importFrom(rlang,.data)
6565
importFrom(sendmailR,"sendmail")
66+
importFrom(writexl,write_xlsx)

NEWS.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# redcapcustodian 1.21.0 (released 2024-03-15)
2+
- Add attachment management to send_email() allowing lists of files or dataframes to be attached to an email (@ljwoodley, #152, #153)
3+
14
# redcapcustodian 1.20.0 (released 2024-02-28)
25
- Add get_redcap_credentials() (@ljwoodley, #149, #151)
36
- Revert "add redcap wrapper functions" (@ljwoodley, #149, #150)

R/logging.R

+157-32
Original file line numberDiff line numberDiff line change
@@ -566,53 +566,178 @@ write_info_log_entry <- function(conn, target_db_name, table_written = NULL, df,
566566

567567
#' A wrapper function that sends an email (via sendmailR) reporting the outcome of another function
568568
#'
569+
#' This function sends an email via `sendmailR`, optionally including a dataframe(s) or zip files(s) as attachments.
570+
#'
569571
#' @param email_body The contents of the email
570572
#' @param email_subject The subject line of the email
571573
#' @param email_to The email addresses of the primary recipient(s), separate recipient addresses with spaces
572574
#' @param email_cc The email addresses of cc'd recipient(s), separate recipient addresses with spaces
573575
#' @param email_from The email addresses of the sender
574-
#' @return No returned value
576+
#' @param df_to_email (Optional) A dataframe or a list of dataframes to be included as file attachment(s). If this parameter is used, `file_name` must also be specified.
577+
#' Each dataframe in the list must have a corresponding file name in the `file_name` parameter to ensure a one-to-one match between dataframes and file names.
578+
#' @param file_name (Optional) A character vector specifying the file name(s) of the attachment(s). Valid file extensions are `.csv`, `.xlsx`, and `.zip`. Each file name must be unique.
579+
#' @param ... Additional arguments passed directly to the file writing functions: `write.csv` for CSV files, and `writexl::write_xlsx` for XLSX files.
580+
#'
581+
#' @return No returned value. It performs an action by sending an email.
575582
#' @examples
576583
#'
577584
#' \dontrun{
578-
#' message <- paste("Failed REDCap data import to", project_title,
585+
#' email_body <- paste("Failed REDCap data import to", project_title,
579586
#' "\nThe reason given was:", error_message)
580587
#'
581588
#' email_subject <- paste("FAILED |", script_name, "|",
582589
#' Sys.getenv("INSTANCE"), "|", script_run_time)
583590
#'
584-
#' send_email(message, email_subject)
591+
#' # email without attachemnts
592+
#' send_email(email_body, email_subject)
593+
#'
594+
#' email_to <- c("email1@example.com email2@example.com")
595+
#' dfs_to_email <- list(head(cars), tail(cars))
596+
#' file_names <- c("file1.csv", "file2.xlsx")
597+
#'
598+
#' # single attachment and at least one email address
599+
#' send_email(
600+
#' email_subject = email_subject,
601+
#' email_body = email_body,
602+
#' email_from = email_from,
603+
#' email_to = email_to,
604+
#' df_to_email = head(cars),
605+
#' file_name = "file1.csv"
606+
#' )
607+
#'
608+
#' # multiple attachments and at least one email address
609+
#' send_email(
610+
#' email_subject = email_subject,
611+
#' email_body = email_body,
612+
#' email_from = email_from,
613+
#' email_to = email_to,
614+
#' df_to_email = dfs_to_email,
615+
#' file_name = file_names
616+
#' )
617+
#'
618+
#' send_email(
619+
#' email_subject = email_subject,
620+
#' email_body = email_body,
621+
#' email_from = email_from,
622+
#' email_to = email_to,
623+
#' file_name = c("file1.zip", "<path_to_file>file2.zip")
624+
#' )
625+
#'
626+
#' # single attachment for each email group
627+
#' email_to <- c("email1@example.com", c("email2@example.com email3@example.com"))
628+
#'
629+
#' args_list <- list(
630+
#' email_subject = email_subject,
631+
#' email_body = email_body,
632+
#' email_to = email_to,
633+
#' email_from = email_from,
634+
#' df_to_email = dfs_to_email,
635+
#' file_name = file_names
636+
#' )
637+
#'
638+
#' purrr::pmap(args_list, send_email)
639+
#'
640+
#' # multiple attachments for each email group
641+
#' email_to <- c(
642+
#' c("email1@example.com email2@example.com"),
643+
#' c("email3@example.com email4@example.com")
644+
#' )
645+
#'
646+
#' args_list <- list(
647+
#' email_subject = email_subject,
648+
#' email_body = email_body,
649+
#' email_to = email_to,
650+
#' email_from = email_from,
651+
#' df_to_email = list(dfs_to_email, dfs_to_email),
652+
#' file_name = list(file_names, file_names)
653+
#' )
654+
#'
655+
#' purrr::pmap(args_list, send_email)
656+
#'
585657
#' }
586658
#' @importFrom sendmailR "sendmail"
659+
#' @importFrom writexl write_xlsx
587660
#' @export
588-
send_email <- function(email_body, email_subject = "", email_to = "", email_cc = "", email_from = "") {
589-
# email credentials
590-
email_server <- list(smtpServer = Sys.getenv("SMTP_SERVER"))
591-
if (email_from == "") {
592-
email_from <- Sys.getenv("EMAIL_FROM")
593-
}
594-
if (email_cc == "") {
595-
email_cc <- unlist(strsplit(Sys.getenv("EMAIL_CC"), " "))
596-
} else {
597-
email_cc <- unlist(strsplit(email_cc, " "))
598-
}
599-
if (email_subject == "") {
600-
email_subject <- paste(Sys.getenv("EMAIL_SUBJECT"), get_script_run_time())
601-
}
661+
send_email <-
662+
function(email_body,
663+
email_subject = "",
664+
email_to = "",
665+
email_cc = "",
666+
email_from = "",
667+
df_to_email = NULL,
668+
file_name = NULL,
669+
...
670+
) {
671+
672+
email_server <- list(smtpServer = Sys.getenv("SMTP_SERVER"))
673+
if (email_from == "") {
674+
email_from <- Sys.getenv("EMAIL_FROM")
675+
}
676+
if (email_cc == "") {
677+
email_cc <- unlist(strsplit(Sys.getenv("EMAIL_CC"), " "))
678+
} else {
679+
email_cc <- unlist(strsplit(email_cc, " "))
680+
}
681+
if (email_subject == "") {
682+
email_subject <-
683+
paste(Sys.getenv("EMAIL_SUBJECT"), get_script_run_time())
684+
}
602685

603-
if (email_to == "") {
604-
email_to <- unlist(strsplit(Sys.getenv("EMAIL_TO"), " "))
605-
} else {
606-
email_to <- unlist(strsplit(email_to, " "))
607-
}
686+
if (email_to == "") {
687+
email_to <- unlist(strsplit(Sys.getenv("EMAIL_TO"), " "))
688+
} else {
689+
email_to <- unlist(strsplit(email_to, " "))
690+
}
608691

609-
## TODO: consider toggling bypass of printing if interactive and local env detected
610-
## if (interactive()) {
611-
## print(email_body)
612-
## return(email_body)
613-
## }
614-
# TODO: consider replacing sendmailR with mRpostman
615-
sendmailR::sendmail(from = email_from, to = email_to, cc = email_cc,
616-
subject = email_subject, msg = email_body,
617-
control = email_server)
618-
}
692+
if (!is.null(file_name)) {
693+
output_dir <- tempdir()
694+
email_content <- list()
695+
696+
if (!is.null(df_to_email) && is.data.frame(df_to_email)) {
697+
df_to_email <- list(df_to_email)
698+
}
699+
700+
if (!is.null(df_to_email) &&
701+
length(df_to_email) != length(file_name)) {
702+
stop("The number of dataframes and file names must match.")
703+
}
704+
705+
for (i in seq_along(file_name)) {
706+
file_extension <- tolower(sub(".*\\.(.*)$", "\\1", file_name[[i]]))
707+
file_fullpath <- file.path(output_dir, basename(file_name[[i]]))
708+
709+
if (!is.null(df_to_email)) {
710+
if (file_extension == "csv") {
711+
readr::write_csv(df_to_email[[i]], file_fullpath, ...)
712+
} else if (file_extension == "xlsx") {
713+
writexl::write_xlsx(df_to_email[[i]], file_fullpath, ...)
714+
} else {
715+
stop("Unsupported file format. Use 'csv' or 'xlsx'.")
716+
}
717+
}
718+
719+
if (file_extension == "zip" &&
720+
!file.copy(file_name[[i]], output_dir, overwrite = TRUE)) {
721+
stop(paste("Failed to move", file_name[[i]]))
722+
}
723+
724+
attachment_object <- sendmailR::mime_part(file_fullpath, basename(file_fullpath))
725+
email_content <- c(email_content, attachment_object)
726+
}
727+
}
728+
729+
## TODO: consider toggling bypass of printing if interactive and local env detected
730+
## if (interactive()) {
731+
## print(email_body)
732+
## return(email_body)
733+
## }
734+
# TODO: consider replacing sendmailR with mRpostman
735+
sendmailR::sendmail(
736+
from = email_from,
737+
to = email_to,
738+
cc = email_cc,
739+
subject = email_subject,
740+
msg = email_content,
741+
control = email_server
742+
)
743+
}

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.20.0
1+
1.21.0

man/send_email.Rd

+80-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)