Skip to content

Commit

Permalink
Merge pull request #31 from laijasmine/master
Browse files Browse the repository at this point in the history
Fixes to awards-bot
- updates Readme for more detailed. instructions on testing to prevent accidents
-  Deals with changes in the RT package, resolves #25
- Fixes issue from: Error in UseMethod("months") : no applicable method for 'months' applied to an object of class "character"
  • Loading branch information
Jasmine authored Feb 19, 2021
2 parents 990f3bc + cfff8fb commit 6163c08
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 72 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ Suggests:
testthat
Remotes:
NCEAS/rt
RoxygenNote: 6.1.0
RoxygenNote: 7.1.1
64 changes: 24 additions & 40 deletions R/RT_functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,14 @@ create_ticket <- function(award, requestor) {
subject = subject,
rt_base = 'https://support.nceas.ucsb.edu/rt')

if (!grepl('created', httr::content(ticket))) {
out <- sprintf('I failed to create a ticket for award: %s, from requestor: %s', award, requestor)
slackr::slackr_bot(out)
return('rt_ticket_create_error')
#check to see if the object ticket is created successfully
if(!exists("ticket")) {
out <- sprintf('I failed to create a ticket for award: %s, from requestor: %s', award, requestor)
slackr::slackr_bot(out)
return('rt_ticket_create_error')
}

# get ticket_id
ticket_id <- rawToChar(ticket$content) %>%
gsub('(.*Ticket )([[:digit:]]+)( created.*)', '\\2', .)

return(ticket_id)

return(ticket)
}


Expand Down Expand Up @@ -78,10 +75,7 @@ create_ticket_and_send_initial_correspondence <- function(awards_db) {
db$id[i],
db$title[i])

reply <- rt::rt_ticket_history_reply(ticket_id = db$rt_ticket[i],
text = email_text,
rt_base = 'https://support.nceas.ucsb.edu/rt')
check_rt_reply(reply, db$rt_ticket[i])
reply <- check_rt_reply(db$rt_ticket[i], email_text)

db$contact_initial[i] <- as.character(Sys.Date())
}
Expand All @@ -105,11 +99,8 @@ send_annual_report_correspondence <- function(awards_db) {
email_text <- sprintf(template,
db$pi_first_name[i])

reply <- rt::rt_ticket_history_reply(ticket_id = db$rt_ticket[i],
text = email_text,
rt_base = 'https://support.nceas.ucsb.edu/rt')
check_rt_reply(reply, db$rt_ticket[i])

reply <- check_rt_reply(db$rt_ticket[i], email_text)

# Update last contact date
db$contact_annual_report_previous[i] <- db$contact_annual_report_next[i]
}
Expand All @@ -135,10 +126,7 @@ send_aon_correspondence <- function(awards_db){
email_text <- sprintf(template,
db$pi_first_name[i])

reply <- rt::rt_ticket_history_reply(ticket_id = db$rt_ticket[i],
text = email_text,
rt_base = 'https://support.nceas.ucsb.edu/rt')
check_rt_reply(reply, db$rt_ticket[i])
reply <- check_rt_reply(db$rt_ticket[i], email_text)

# Update last contact date
db$contact_aon_previous[i] <- db$contact_aon_next[i]
Expand All @@ -163,10 +151,7 @@ send_one_month_remaining_correspondence <- function(awards_db) {
db$id[i],
db$title[i])

reply <- rt::rt_ticket_history_reply(ticket_id = db$rt_ticket[i],
text = email_text,
rt_base = 'https://support.nceas.ucsb.edu/rt')
check_rt_reply(reply, db$rt_ticket[i])
reply <- check_rt_reply(db$rt_ticket[i], email_text)

# Update last contact date
db$contact_1mo[i] <- as.character(Sys.Date())
Expand Down Expand Up @@ -207,24 +192,23 @@ send_correspondence_at_time_x <- function(awards_db,

}


## helper function to check RT replies
check_rt_reply <- function(reply, rt_ticket_number) {
if (reply$status_code != 200) {
out <- sprintf('I failed to reply on: %s, with status code: %s', rt_ticket_number, reply$status_code)
slackr::slackr_bot(out)
}
content <- httr::content(reply)
if (!grepl('Correspondence added', content)) {
out <- paste0('I failed to send a correspondence on ticket: ', rt_ticket_number)
check_rt_reply <- function(ticket_number, email) {
tryCatch({
ticket <- rt::rt_ticket_history_reply(ticket_id = ticket_number,
text = email,
rt_base = 'https://support.nceas.ucsb.edu/rt')
},
error = function(e) {
out <- sprintf('I failed to reply on: %s', ticket)
slackr::slackr_bot(out)
}
})

return(ticket)
}


## helper function to read in email templates
read_file <- function(path) {
suppressWarnings(paste0(readLines(path), collapse = '\n'))
suppressWarnings(paste0(readLines(path), collapse = '\n '))
}

## helper function read in general, AON, or SS initial template
Expand Down
11 changes: 11 additions & 0 deletions R/main.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ main <- function(database_path = Sys.getenv('DATABASE_PATH'),
annual_report_time = Sys.getenv('ANNUAL_REPORT_TIME'),
initial_aon_offset = Sys.getenv('INITIAL_AON_OFFSET'),
aon_recurring_interval = Sys.getenv('AON_RECURRING_INTERVAL')) {

annual_report_time <- as.numeric(annual_report_time)
initial_aon_offset <- as.numeric(initial_aon_offset)
aon_recurring_interval <- as.numeric(aon_recurring_interval)


## Import awards database
db <- import_awards_db(database_path)

Expand All @@ -31,6 +37,11 @@ test_main <- function(database_path = Sys.getenv('DATABASE_PATH'),
initial_aon_offset = Sys.getenv('INITIAL_AON_OFFSET'),
aon_recurring_interval = Sys.getenv('AON_RECURRING_INTERVAL'),
email) {

annual_report_time <- as.numeric(annual_report_time)
initial_aon_offset <- as.numeric(initial_aon_offset)
aon_recurring_interval <- as.numeric(aon_recurring_interval)

# Change email to testing email
db <- import_awards_db(database_path)
db$pi_email <- email
Expand Down
3 changes: 2 additions & 1 deletion R/testing_functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ write_inst_database <- function() {
create_dummy_database <- function() {
db <- create_blank_database()

db$pi_email <- 'mullen@nceas.ucsb.edu'
db$pi_email <- 'jasminelai@nceas.ucsb.edu'
db$pi_first_name <- 'Dominic'
db$pi_last_name <- 'Mullen'
db$title <- '**Test** AwardBot Title'
Expand All @@ -71,3 +71,4 @@ with_dir <- function(directory, expr) {
setwd(directory)
evalq(expr)
}

34 changes: 30 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ The NSF awards bot regularly contacts principal investigators with reminders on
## How the bot works
Every 24 hours the bot queries NSF's award [API](https://www.research.gov/common/webapi/awardapisearch-v1.htm) for newly awarded grants and stores this information in a pre-existing database. When it finds a new award it creates a new ticket in [Request Tracker](https://bestpractical.com/request-tracker/) and sends an initial correspondence that outlines project-specific expectations and deadlines. It sends reminders to submit annual reports, submit data for Arctic Observing Network (AON) projects, and that the award is expiring soon. The bot sends error messages to a slack channel.

## Setup
## Setup
- Copy the bot [cron script](https://github.com/NCEAS/awards-bot/blob/master/inst/main_cron_script.R) to a directory

- Create a file called `.Renviron` in the same directory as the script
- Create a file called `.Renviron` in the same directory as the script.
Include the following variables:
```text
DATABASE_PATH # Path to the database of awards and correspondences
Expand All @@ -24,6 +24,8 @@ Every 24 hours the bot queries NSF's award [API](https://www.research.gov/common
AON_RECURRING_INTERVAL=6 # Number of months to send recurring emails for AON data due
```

**Note** - please contact Chris for set up in Linux

## Running
Run the bot [cron script](https://github.com/NCEAS/awards-bot/blob/master/inst/main_cron_script.R) every 24 hours.
Example crontab: `0 15 * * * Rscript ~/home/awardsBot/main_cron_script.R`
Expand All @@ -42,11 +44,35 @@ In this example we will assume the submission policies changed to require an ini
- Unit tests!!

## Testing the bot
The awards-bot package contains modular unit tests, however, many of these don't run, by default, unless the R session is connected to [RT](https://bestpractical.com/request-tracker/) and [Slack](https://slack.com/). If you need to test the bot for any reason run the [test_main unit test](https://github.com/NCEAS/awards-bot/blob/master/tests/testthat/test_main.R) locally, ideally line by line. Be aware that this will create two test tickets in RT. A thorough test of the bot would involve signing in to RT and Slack, and running `devtools::check()`; although, if the [test_main unit test](https://github.com/NCEAS/awards-bot/blob/master/tests/testthat/test_main.R) passes it's generally safe to assume the more modular tests will pass as well.
The awards-bot package contains modular unit tests, however, many of these don't run, by default, unless the R session is connected to [RT](https://bestpractical.com/request-tracker/) and [Slack](https://slack.com/). A thorough test of the bot would involve signing in to RT and Slack, and running `devtools::check()`; although, if the [test_main unit test](https://github.com/NCEAS/awards-bot/blob/master/tests/testthat/test_main.R) passes it's generally safe to assume the more modular tests will pass as well.

General notes:
- only use `awardsBot:::test_main()` in testing. This a wrapper for `awardsBot::main()` except with an additional `email` argument
- set `email = your test email address` in the unit test script
- modify `test_main()` calls in the unit test script accordingly, including any additional arguments


### Testing Locally
Please follow this closely so no emails are sent to researchers by mistake while testing.

1. Add the following lines to the `.Renviron` (can be accessed by `usethis::edit_r_environ()`)
```
SLACK_WEBHOOK_URL="{URL}" # Your Slack webhook URL
RT_URL="https://example.com/rt" # The URL of your RT install
RT_USER="your_rt_user" # Your RT username
RT_PASS="your_rt_password" # Your RT password
```

2. Set up slackbot: `slackr::slackr_setup(channel = "#awardbot",incoming_webhook_url = Sys.getenv("SLACK_WEBHOOK_URL"))`

3. Login to RT: `rt::rt_login()`

4. Run `devtools::load_all()` to source the scripts for testing so you can run the test file line by line

5. Run the [test_main unit test](https://github.com/NCEAS/awards-bot/blob/master/tests/testthat/test_main.R) locally, ideally line by line.

6. Two test tickets should be created in RT.

## Style
Code generally follows the [tidyverse style conventions](http://style.tidyverse.org/), with the following specific style preferences:
- underscore for all variable names unless referring to an NSF awards API return field (i.e. expDate, startDate, etc.)
Expand All @@ -58,4 +84,4 @@ Work on this package was supported by:

Additional support was provided by the National Center for Ecological Analysis and Synthesis, a Center funded by the University of California, Santa Barbara, and the State of California.

[![nceas_footer](https://www.nceas.ucsb.edu/files/newLogo_0.png)](http://www.nceas.ucsb.edu)
<img src="https://live-ncea-ucsb-edu-v01.pantheonsite.io/sites/default/files/2020-03/NCEAS-full%20logo-4C.png" width=50% height=50%>
3 changes: 3 additions & 0 deletions example_db.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"awardee","date","exp_date","fund_program_name","id","pi_email","pi_first_name","pi_last_name","pi_phone","po_name","start_date","title","rt_ticket","pi_orcid","contact_initial","contact_annual_report_previous","contact_annual_report_next","contact_aon_previous","contact_aon_next","contact_1mo","active_award_flag"
"REGENTS OF THE UNIVERSITY OF COLORADO, THE","2018-06-29","2020-06-30","AON IMPLEMENTATION","1833165","jasminelai@nceas.ucsb.edu","Bruce","Vaughn","3034927985","Jennifer Mercer","2018-07-01","NNA: AON: EAGER: Closing the Water Vapor Exchange Budget Between the Ice Sheets and Free Atmosphere",NA,NA,NA,NA,NA,NA,NA,NA,"no"
"UNIVERSITY OF CALIFORNIA SAN DIEGO","2018-07-03","2021-08-31","ARCTIC NATURAL SCIENCES","1822021","jasminelai@nceas.ucsb.edu","Rick","Reynolds","8588224407","Cynthia Suchman","2018-09-01","Assessing Alterations to Planktonic Community Composition in a Changing Arctic Ocean","20725",NA,"2020-08-19",NA,NA,NA,NA,"2021-07-31","yes"
6 changes: 3 additions & 3 deletions inst/example_db.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"awardee","date","exp_date","fund_program_name","id","pi_email","pi_first_name","pi_last_name","pi_phone","po_name","start_date","title","rt_ticket","pi_orcid","contact_initial","contact_annual_report_previous","contact_annual_report_next","contact_aon_previous","contact_aon_next","contact_1mo", "active_award_flag"
"REGENTS OF THE UNIVERSITY OF COLORADO, THE","2018-06-29","2020-06-30","AON IMPLEMENTATION","1833165","bruce.vaughn@colorado.edu","Bruce","Vaughn","3034927985","Jennifer Mercer","2018-07-01","NNA: AON: EAGER: Closing the Water Vapor Exchange Budget Between the Ice Sheets and Free Atmosphere",NA,NA,NA,NA,NA,NA,NA,NA,NA
"UNIVERSITY OF CALIFORNIA SAN DIEGO","2018-07-03","2021-08-31","ARCTIC NATURAL SCIENCES","1822021","rreynolds@ucsd.edu","Rick","Reynolds","8588224407","Cynthia Suchman","2018-09-01","Assessing Alterations to Planktonic Community Composition in a Changing Arctic Ocean",NA,NA,NA,NA,NA,NA,NA,NA,NA
"awardee","date","exp_date","fund_program_name","id","pi_email","pi_first_name","pi_last_name","pi_phone","po_name","start_date","title","rt_ticket","pi_orcid","contact_initial","contact_annual_report_previous","contact_annual_report_next","contact_aon_previous","contact_aon_next","contact_1mo","active_award_flag"
"REGENTS OF THE UNIVERSITY OF COLORADO, THE","2018-06-29","2021-06-30","AON-Arctic Observing Network","1833165","bruce.vaughn@colorado.edu","Bruce","Vaughn","3034927985","Jennifer Mercer","2018-07-01","NNA: AON: EAGER: Closing the Water Vapor Exchange Budget Between the Ice Sheets and Free Atmosphere",NA,NA,NA,NA,NA,NA,NA,NA,"yes"
"UNIVERSITY OF CALIFORNIA SAN DIEGO","2018-07-03","2021-08-31","ANS-Arctic Natural Sciences","1822021","rreynolds@ucsd.edu","Rick","Reynolds","8588224407","Colene Haffke","2018-09-01","Assessing Alterations to Planktonic Community Composition in a Changing Arctic Ocean",NA,NA,NA,NA,NA,NA,NA,NA,"yes"
3 changes: 1 addition & 2 deletions man/get_awards.Rd

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

8 changes: 6 additions & 2 deletions man/update_contact_dates.Rd

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

40 changes: 22 additions & 18 deletions tests/testthat/test_RT_functions.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
library(rt)
context('Send RT correspondences')

rt_url <- 'https://support.nceas.ucsb.edu/rt'
Expand All @@ -10,24 +11,11 @@ test_that('we can create an RT ticket', {
}

db <- create_dummy_database()
ticket <- create_ticket(db$id, db$pi_email)

expect_type(ticket, 'character')
})

test_that('create_ticket errors gracefully', {
if (check_rt_login(rt_url)) {
skip('Logged in to RT. Test will probably not pass')
}
if (Sys.getenv('SLACK_WEBHOOK_URL') == '') {
skip('Run slackr_setup() to run this test')
}

db <- create_dummy_database()
#does not return ticket but creates ticket
ticket <- create_ticket(db$id, db$pi_email)

# create_ticket ouputs the character 'error' when it fails
expect_equal(ticket, 'rt_ticket_create_error')
expect_type(ticket, 'double')
})

test_that('we can send an initial correspondence', {
Expand All @@ -38,8 +26,8 @@ test_that('we can send an initial correspondence', {
db <- create_dummy_database()
db <- create_ticket_and_send_initial_correspondence(db)

ticket <- rt::rt_ticket_properties(db$rt_ticket, rt_url)
expect_equal(ticket$content$Requestors, 'mullen@nceas.ucsb.edu')
ticket <- rt::rt_ticket_properties(db$rt_ticket)
expect_equal(ticket$Requestors, 'jasminelai@nceas.ucsb.edu')
})

test_that('we can send an annual report correspondence', {
Expand Down Expand Up @@ -86,6 +74,22 @@ test_that('one error in the database does not the initial contact for loop', {
# this is the test for 'rt_ticket_create_error' error handling
})

test_that('check_rt_reply catches both potential errors', {})
test_that('check_rt_reply catches both potential errors', {
if (!check_rt_login(rt_url)) {
skip('Not logged in to RT. Skipping Test.')
}

db <- create_dummy_database()
db <- create_ticket_and_send_initial_correspondence(db)

template <- read_initial_template(db$fund_program_name[1])
email_text <- sprintf(template,
db$pi_first_name[1],
db$id[1],
db$title[1])

reply <- check_rt_reply(db$rt_ticket[1], email_text)
expect_type(reply, "double")
})

test_that('send correspondences works', {})
2 changes: 1 addition & 1 deletion tests/testthat/test_main.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test_that('main sends correspondences and updates the database', {
annual_report_time <- 8
initial_aon_offset <- 11
aon_recurring_interval <- 6
email <- 'mullen@nceas.ucsb.edu' # set to the test email you're using.
email <- 'jasminelai@nceas.ucsb.edu' # set to the test email you're using.

## Initialize database and lastrun
db <- import_awards_db(file.path(system.file('example_db.csv', package = 'awardsBot')))
Expand Down

0 comments on commit 6163c08

Please sign in to comment.