├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ └── pkgdown.yaml ├── .gitignore ├── .lintr ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── actions.R ├── ae.R ├── ae_workload.R ├── article.R ├── articles.R ├── assignments.R ├── author-email.R ├── convert_landing.R ├── crossref.R ├── current.R ├── doi.R ├── email.R ├── git.R ├── id.R ├── issue.R ├── late.R ├── match.R ├── path.R ├── publish.R ├── report.R ├── reviewers-utils.R ├── reviewers.R ├── rfc822.R ├── rj.R ├── status.R ├── submissions.R ├── summary_plots.R ├── sysdata.rda └── utils.R ├── README.md ├── README.nm ├── _pkgdown.yml ├── data-raw └── keywords_list_concat.R ├── inst ├── RJwrapper_template.tex ├── associate-editors.csv ├── auth │ └── client_id.json ├── editors.csv ├── examples │ └── example.R ├── issue_template.Rmd ├── keywords-list.csv ├── pandoc-metadata │ └── RJournal.sty ├── sample │ ├── Accepted │ │ ├── 2012-01 │ │ │ └── DESCRIPTION │ │ └── 2012-02 │ │ │ └── DESCRIPTION │ ├── Rejected │ │ ├── 2012-03 │ │ │ └── DESCRIPTION │ │ └── 2012-04 │ │ │ └── DESCRIPTION │ └── Submitted │ │ └── 2012-05 │ │ └── DESCRIPTION └── templates │ ├── accept.txt │ ├── acknowledge.txt │ ├── acknowledge_revision.txt │ ├── ae-invitation.txt │ ├── gmail_acknowledge.txt │ ├── gmail_proofing.txt │ ├── issue.txt │ ├── online.txt │ ├── reject.txt │ ├── reject_format.txt │ ├── resubmission.txt │ ├── review-template.txt │ ├── review-updated.txt │ ├── review.txt │ ├── revision-major.txt │ ├── revision-minor.txt │ └── withdraw.txt ├── man ├── AEs.Rd ├── acknowledge_revision.Rd ├── acknowledge_submission.Rd ├── action.Rd ├── actionable_articles.Rd ├── active_articles.Rd ├── add_ae.Rd ├── add_review.Rd ├── add_reviewer.Rd ├── address.Rd ├── ae_workload.Rd ├── article.Rd ├── article_status_plot.Rd ├── assignments.Rd ├── author_emails.Rd ├── build_latex.Rd ├── corr_author.Rd ├── decline_reviewer.Rd ├── email_template.Rd ├── email_text.Rd ├── filter_status.Rd ├── future_ids.Rd ├── get_accepted_but_not_online.Rd ├── get_article_keywords.Rd ├── get_articles_path.Rd ├── get_md_from_pdf.Rd ├── get_reviewer_keywords.Rd ├── get_reviewers.Rd ├── get_submissions.Rd ├── git_user.Rd ├── invite_reviewers.Rd ├── late_aes.Rd ├── late_reviewers.Rd ├── make_proof.Rd ├── match_keywords.Rd ├── need_decision.Rd ├── need_reviewers.Rd ├── new_id.Rd ├── online_metadata.Rd ├── online_metadata_for_article.Rd ├── parse_address_list.Rd ├── proofing.Rd ├── publish_article.Rd ├── publish_issue.Rd ├── publish_news.Rd ├── reexports.Rd ├── report.Rd ├── reviewer_status.Rd ├── reviewer_summary.Rd ├── rj.Rd ├── set_articles_path.Rd ├── status.Rd ├── summarise_articles.Rd ├── tabulate_articles.Rd ├── time_to_accept_plot.Rd ├── update_status.Rd ├── valid_reviewer_status.Rd └── valid_status.Rd ├── rj.Rproj ├── tests ├── test-all.R └── testthat │ ├── test-actions.R │ ├── test-id.r │ ├── test-rfc822.r │ ├── test-status.r │ └── valid-article.dcf └── vignettes ├── .gitignore ├── associate_editors_guide.Rmd ├── conflict_of_interest.Rmd ├── editor_in_chief_guide.Rmd ├── editors_guide.Rmd ├── issue_build_guide.Rmd ├── roles_and_responsibilities.Rmd └── website_build_guide.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README.nm$ 4 | ^\.lintr$ 5 | ^\.github$ 6 | ^data-raw$ 7 | ^_pkgdown\.yml$ 8 | ^docs$ 9 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macOS-latest, r: 'release'} 22 | - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | 31 | steps: 32 | - uses: actions/checkout@v2 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - uses: r-lib/actions/setup-r@v2 37 | with: 38 | r-version: ${{ matrix.config.r }} 39 | http-user-agent: ${{ matrix.config.http-user-agent }} 40 | use-public-rspm: true 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | with: 44 | extra-packages: any::rcmdcheck 45 | needs: check 46 | 47 | - uses: r-lib/actions/check-r-package@v2 48 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | name: pkgdown 7 | 8 | jobs: 9 | pkgdown: 10 | runs-on: macOS-latest 11 | env: 12 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - uses: r-lib/actions/setup-r@v2 17 | 18 | - uses: r-lib/actions/setup-pandoc@v2 19 | 20 | - name: Query dependencies 21 | run: | 22 | install.packages('remotes') 23 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) 24 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") 25 | shell: Rscript {0} 26 | 27 | - name: Cache R packages 28 | uses: actions/cache@v3 29 | with: 30 | path: ${{ env.R_LIBS_USER }} 31 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} 32 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- 33 | 34 | - name: Install dependencies 35 | run: | 36 | remotes::install_deps(dependencies = TRUE) 37 | install.packages("pkgdown", type = "binary") 38 | shell: Rscript {0} 39 | 40 | - name: Install package 41 | run: R CMD INSTALL . 42 | 43 | - name: Deploy package 44 | run: | 45 | git config --local user.email "actions@github.com" 46 | git config --local user.name "GitHub Actions" 47 | Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # https://raw.githubusercontent.com/github/gitignore/master/R.gitignore 2 | # History files 3 | .Rhistory 4 | .Rapp.history 5 | # Session Data files 6 | .RData 7 | # User-specific files 8 | .Ruserdata 9 | # Example code in package build process 10 | *-Ex.R 11 | # Output files from R CMD build 12 | /*.tar.gz 13 | # Output files from R CMD check 14 | /*.Rcheck/ 15 | # RStudio files 16 | .Rproj.user/ 17 | # produced vignettes 18 | vignettes/*.html 19 | vignettes/*.pdf 20 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 21 | .httr-oauth 22 | # knitr and R markdown default cache directories 23 | *_cache/ 24 | /cache/ 25 | # Temporary files created by R markdown 26 | *.utf8.md 27 | *.knit.md 28 | # R Environment Variables 29 | .Renviron 30 | # pkgdown site 31 | docs/ 32 | # backups 33 | *~ 34 | .DS_Store 35 | inst/doc 36 | -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: with_defaults( 2 | assignment_linter = NULL, 3 | object_name_linter = NULL, 4 | line_length_linter(100), 5 | commented_code_linter = NULL 6 | ) 7 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: rj 3 | Title: Tools for managing the R journal 4 | Version: 0.3.0.9000 5 | Author: Editor-in-Chief 6 | Maintainer: Editor-in-Chief 7 | Description: This package provides useful functions for the 8 | editors of the R journal. 9 | License: MIT + file LICENSE 10 | Depends: 11 | R (>= 4.2.0) 12 | Imports: 13 | cli, 14 | dplyr, 15 | fs, 16 | glue, 17 | gmailr, 18 | googledrive, 19 | googlesheets4 (>= 0.2.0), 20 | pdftools, 21 | purrr, 22 | rlang, 23 | stringr, 24 | tibble, 25 | tth, 26 | whisker, 27 | yaml, 28 | ctv, 29 | tidyr, 30 | scales, 31 | texor, 32 | tinytex, 33 | xfun 34 | Suggests: 35 | DiagrammeR, 36 | knitr, 37 | testthat, 38 | rmarkdown, 39 | renv, 40 | rstudioapi, 41 | callr, 42 | ggplot2 43 | Encoding: UTF-8 44 | URL: https://rjournal.github.io/rj 45 | RoxygenNote: 7.3.2 46 | VignetteBuilder: knitr 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2020 2 | COPYRIGHT HOLDER: The R Journal 3 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(as.character,id) 4 | S3method(as.data.frame,status_list) 5 | S3method(c,status) 6 | S3method(empty,"NULL") 7 | S3method(empty,address_list) 8 | S3method(empty,character) 9 | S3method(format,address) 10 | S3method(format,address_list) 11 | S3method(format,article) 12 | S3method(format,id) 13 | S3method(format,status) 14 | S3method(format,status_list) 15 | S3method(format,supplfile) 16 | S3method(format,supplfile_list) 17 | S3method(format,unparsed) 18 | S3method(print,address) 19 | S3method(print,address_list) 20 | S3method(print,article) 21 | S3method(print,catout) 22 | S3method(print,id) 23 | S3method(print,report) 24 | S3method(print,status) 25 | S3method(print,status_list) 26 | export("%>%") 27 | export(AE) 28 | export(AEs) 29 | export(abandon_reviewer) 30 | export(accept) 31 | export(accepted_articles) 32 | export(acknowledge_revision) 33 | export(acknowledge_submission) 34 | export(actionable_articles) 35 | export(active_articles) 36 | export(add_ae) 37 | export(add_review) 38 | export(add_reviewer) 39 | export(address) 40 | export(ae_workload) 41 | export(agree_reviewer) 42 | export(article) 43 | export(article_status_plot) 44 | export(as.article) 45 | export(assignments) 46 | export(author_emails) 47 | export(build_latex) 48 | export(corr_author) 49 | export(decline_reviewer) 50 | export(draft_proofing) 51 | export(email_template) 52 | export(filter_status) 53 | export(find_articles) 54 | export(future_ids) 55 | export(get_AE) 56 | export(get_accepted_articles) 57 | export(get_accepted_but_not_online) 58 | export(get_articles_path) 59 | export(get_assignments) 60 | export(get_latest) 61 | export(get_md_from_pdf) 62 | export(get_reviewers) 63 | export(get_submissions) 64 | export(get_unassigned) 65 | export(invite_reviewer) 66 | export(invite_reviewers) 67 | export(late_aes) 68 | export(late_reviewers) 69 | export(list_reviewers) 70 | export(major_revision) 71 | export(make_proof) 72 | export(match_keywords) 73 | export(minor_revision) 74 | export(need_decision) 75 | export(need_reviewers) 76 | export(new_id) 77 | export(online_metadata) 78 | export(online_metadata_for_article) 79 | export(parse_address_list) 80 | export(proofing_article) 81 | export(proofing_article_text) 82 | export(publish_article) 83 | export(publish_issue) 84 | export(publish_news) 85 | export(reject) 86 | export(reject_format) 87 | export(report) 88 | export(resubmission) 89 | export(reviewer_status) 90 | export(reviewer_summary) 91 | export(set_articles_path) 92 | export(status) 93 | export(summarise_articles) 94 | export(tabulate_articles) 95 | export(time_to_accept_plot) 96 | export(update_status) 97 | export(valid_reviewer_status) 98 | export(valid_status) 99 | export(withdraw) 100 | import(fs) 101 | import(rlang) 102 | import(stringr) 103 | importFrom(cli,cli_alert_danger) 104 | importFrom(cli,cli_alert_info) 105 | importFrom(cli,cli_end) 106 | importFrom(cli,cli_h1) 107 | importFrom(cli,cli_li) 108 | importFrom(cli,cli_ul) 109 | importFrom(dplyr,bind_rows) 110 | importFrom(dplyr,count) 111 | importFrom(dplyr,distinct) 112 | importFrom(dplyr,filter) 113 | importFrom(dplyr,group_by) 114 | importFrom(dplyr,left_join) 115 | importFrom(dplyr,mutate) 116 | importFrom(dplyr,rename) 117 | importFrom(dplyr,right_join) 118 | importFrom(dplyr,select) 119 | importFrom(dplyr,ungroup) 120 | importFrom(gmailr,create_draft) 121 | importFrom(gmailr,gm_auth_configure) 122 | importFrom(gmailr,mime) 123 | importFrom(gmailr,send_draft) 124 | importFrom(googlesheets4,range_write) 125 | importFrom(googlesheets4,read_sheet) 126 | importFrom(purrr,map) 127 | importFrom(purrr,map_dfr) 128 | importFrom(purrr,pluck) 129 | importFrom(rlang,.data) 130 | importFrom(scales,label_percent) 131 | importFrom(stats,na.omit) 132 | importFrom(stats,setNames) 133 | importFrom(stringr,fixed) 134 | importFrom(stringr,str_detect) 135 | importFrom(stringr,str_remove) 136 | importFrom(stringr,word) 137 | importFrom(tibble,as_tibble) 138 | importFrom(tibble,tibble) 139 | importFrom(tidyr,pivot_wider) 140 | importFrom(tidyr,replace_na) 141 | importFrom(tidyr,separate_rows) 142 | importFrom(tidyr,unnest) 143 | importFrom(utils,URLencode) 144 | importFrom(utils,adist) 145 | importFrom(utils,as.person) 146 | importFrom(utils,browseURL) 147 | importFrom(utils,capture.output) 148 | importFrom(utils,edit) 149 | importFrom(utils,glob2rx) 150 | importFrom(utils,read.csv) 151 | importFrom(utils,str) 152 | importFrom(utils,type.convert) 153 | importFrom(utils,unzip) 154 | importFrom(utils,zip) 155 | importFrom(whisker,whisker.render) 156 | importFrom(yaml,as.yaml) 157 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # rj (0.3.1) 2 | 3 | # rj 0.3.0 4 | - Fix metadata for pandoc 5 | - Added texor support in article publishing workflow 6 | - Updated report() 7 | - Added resubmission() 8 | - Added assignments() 9 | - Added late_reviewers() 10 | - All emails now recorded in correspondence folder 11 | - Review template added to correspondence folder 12 | - Templates updated 13 | - Updated vignettes 14 | - Updated templates 15 | - Bug fixes 16 | 17 | # rj 0.2.4 18 | - Changed proofing template email to Simon's 19 | 20 | # rj 0.2.31 21 | - added vignettes on conflict of interest and roles and responsibilities 22 | 23 | # rj 0.2.29 24 | 25 | - added a line for AE: decisions for summarise_articles() 26 | 27 | # rj 0.2.28 28 | 29 | - Add `check_in_submission_folder()` to verify the article is currently active for actions (major_revision, minor_revision, accept, reject). 30 | 31 | # rj 0.2.27 32 | 33 | - Added two new functions to create summary plots `article_status_plot()` and `time_to_accept_plot()` 34 | 35 | # rj 0.2.26 36 | 37 | - Expand `get_assignments()` and `actionable_articles()` to search on AEs and 38 | print out articles in status "with AE". 39 | - `match_keywords()` now requires authentication everytime to avoid false positive PERMISSION_DENIED 40 | 41 | 42 | # rj 0.2.25 43 | 44 | - Updated the template for "accept" to reflect new format, check list, and four issues per year. 45 | 46 | # rj 0.2.24 47 | 48 | - Changes to `summarise_articles()`: 49 | - include the unassigned articles in the beginning, if any 50 | - now use `tabulate_article()` to produce the output 51 | 52 | # rj 0.2.23 53 | 54 | - Changed the `ae_workload()` to 55 | - have a default period of 365 days 56 | - return all AEs where those without assignments as 0 57 | - `summarise_articles()` now first prints out article(s) not assigned to any editor 58 | 59 | 60 | # rj 0.2.22 61 | 62 | 2022-01-31 63 | - fixes to `ae_workload()`; now handles "" empty AE field without failing, 64 | and will show the AE initials, name, and email address alongside the 65 | assignment count. Now works as previously documented, and `day_back` 66 | argument works again (but note the meaning of this is not what users might 67 | expect.) 68 | 69 | # rj 0.2.21 70 | 71 | 2021-11-01 72 | - fixes to erroring in summarise_articles 73 | - make anonymous example for add_reviewer() 74 | - email_template also updates status 75 | 76 | # rj 0.2.20 77 | 78 | 2021-10-04 79 | - revised add_ae function 80 | 81 | 2021-09-06 82 | - Added a function and template to acknowledge revisions 83 | 84 | 2021-08-25 85 | - add_ae function added 86 | - fixes to get_submissions 87 | - new function to tabulate_articles which helps to count keywords, reviewer loads, ... 88 | - updates to vignettes on operations 89 | 90 | 2021-08-10 91 | - fix to match_keywords 92 | 93 | # rj 0.2.19 94 | 95 | 2021-06-21 96 | - new function to list articles that are accepted but not online 97 | 2021-06-17 98 | - added `git(..)` helper function to call `git` properly. 99 | It honors the `GIT` environment variable. 100 | - fixed git actions to work when executed in a subdirectory 101 | of "articles" by using `get_articles_path()` for the target. 102 | - add `as.characer.id()` so simple things like 103 | `warning("Article ", article$id, " has issues")` works. 104 | - `update_status` will update the last entry (with a warning) 105 | instead of appending if it is the same status. 106 | 2021-06-07 107 | - removed check on RJournal.sty file being in article directory 108 | - updated template for minor revision to say "tentatively" accepted 109 | - Only build article pdf if it doesn't exist already 110 | 111 | 2021-04-19 112 | - added a new function to handle format reject 113 | 114 | 2021-04-07 115 | - fix file path quoting in `git mv` commands (spaces in names 116 | would break it) 117 | 118 | 2021-04-05 119 | - fix `get_articles_path()` to use the repostiroy root if not 120 | defined by `set_articles_path()`. Also check the validity of 121 | the path set by `set_articles_path()` and ignore it (with a 122 | warning) if it does not exist. 123 | 124 | - re-factor printing in `summarise_articles()`. This also fixes 125 | the duplication of entries with "revision received" status. 126 | 127 | - added `summarise_articles(..., other=TRUE)` option which if 128 | set will show articles which have a status not covered by any of 129 | the other sections (typically entries like accepted, proofed, 130 | online) - possibly useful to check for status errors. 131 | 132 | - added _experimental_ `actionable_articles()` function which 133 | lists articles with potential actions such as "needs reviewers", 134 | "review overdue" or "invite overdue". It requires reviewer 135 | entries to use well-defined comments such as 136 | `[Invited 2020-12-18; Agreed 2020-12-21; Minor 2020-12-29]` 137 | Currently only looks at articles with status "out for review", 138 | "acknowledged" and "submitted". The supported review states are: 139 | - Invited 140 | - Agreed, Declined, Abandoned 141 | - Revision (subsequent rounds of reviews after Major) 142 | - Minor, Major, Reject (final) 143 | 144 | 2021-03-22 145 | - `RJ_EMAIL_TOOL` environment variable can be set to change the tool 146 | used to handle generated e-mails. It can have one of the following 147 | values: 148 | - "mailto" or "browser" 149 | opens the system browser with _mailto:_ URL, on most 150 | systems this will open the e-mail client as defined 151 | in the system (default) 152 | - "show" uses `file.show()` function on the generated text file 153 | - "edit" uses `file.edit()` function on the generated text file 154 | - "open" calls `open` system command on the generated text file 155 | (on macOS this opens your system text editor) 156 | - "clip" copies the text of the e-mail into the system 157 | clipboard. Note that on systems other that macOS or Windows this 158 | requires you to have either `xsel` or `xclip` helper tool 159 | installed. 160 | 161 | - Warn about the "browser" option (typically adulterated by 162 | RStudio which tends to prevent _mailto:_ URLs from working) only if 163 | it is a function. 164 | 165 | 2021-03-15 166 | - modified the `email_template()` function to use the same text as 167 | `valid_status()` for major and minor revision 168 | 169 | 170 | # rj 0.2.18 171 | 172 | 2021-02-22 173 | - Exported `get_articles_path()` 174 | 175 | 2021-02-22 176 | - getting report function to work, almost there 177 | -------------------------------------------------------------------------------- /R/ae.R: -------------------------------------------------------------------------------- 1 | #' Detect user name and e-mail from GIT 2 | #' @return named string, e-mail of the user 3 | git_user <- function() { 4 | name <- system("git config --get user.name", intern = TRUE) 5 | email <- system("git config --get user.email", intern = TRUE) 6 | names(email) <- name 7 | email 8 | } 9 | 10 | #' Associate editor (AE) functions 11 | #' 12 | #' Functions to determine if the user is an AE and retrieve relevant AE information 13 | #' 14 | #' @param path string, path to the git repository to use 15 | #' for detection. 16 | #' @param require logical, if \code{TRUE} then failing to detect 17 | #' the AE is considered an error 18 | #' 19 | #' @details 20 | #' \itemize{ 21 | #' \item{\code{AEs()}: read the associate-editors.csv in the rj package as a data frame} 22 | #' \item{\code{detect_AE()}: determine AE from the remote of the repository in 23 | #' \code{path} or from the git config e-mail.this only work for AE and will fail for editors} 24 | #' \item{\code{AE()}: returns the corresponding row from \code{AEs()} if called 25 | #' by an associate editor or in an AE repository, otherwise 26 | #' \code{NULL}. It relies on either \code{RJ_EDITOR} environment 27 | #' variable to contain the name of the editor or detection from the 28 | #' git repository pointed to by \code{path} (see 29 | #' \code{\link{detect_AE}}).} 30 | #' \item{\code{is_AE()}: determine if the user is an AE or the repository is an AE repository} 31 | #' } 32 | #' @return 33 | #' \itemize{ 34 | #' \item{\code{AEs()}: a data frame with all associate editors} 35 | #' \item{\code{detect_AE()}: \code{NULL} if not found or the 36 | #' row from \code{AEs()} corresponding to the AE} 37 | #' \item{ \code{AE}: \code{NULL} if not an AE or a row from \code{AEs()}} 38 | #' \item{\code{is_AE}: \code{TRUE} if the user if an AE or 39 | #' the repository is an AE repository, \code{FALSE} otherwise} 40 | #' } 41 | #' 42 | #' @export 43 | AEs <- function() { 44 | d <- read.csv(system.file("associate-editors.csv", package = "rj"), 45 | stringsAsFactors = FALSE 46 | ) 47 | valid <- nzchar(d$github) 48 | if (any(!valid)) warning("associate-editors.csv contains invalid entries!") 49 | d[valid, ] 50 | } 51 | 52 | #' @rdname AEs 53 | detect_AE <- function(path = ".", require = FALSE) { 54 | ae <- AEs() 55 | rem <- suppressWarnings(system(paste("git -C ", shQuote(path.expand(path)), " remote -v 2>&1"), intern = TRUE)) 56 | ## if we can't detect it from the repo, try git config 57 | m <- if (identical(attr(rem, "status"), 128L)) { 58 | user <- git_user() 59 | m <- na.omit(match(tolower(user), tolower(ae$email))) 60 | seq.int(nrow(ae)) %in% m 61 | } else { 62 | sapply(ae$github, function(o) isTRUE(any(grepl(paste0(o, "\\.git"), rem, TRUE)))) 63 | } 64 | if (require && !any(m)) { 65 | stop("Could not detect any AE") 66 | } 67 | if (any(m)) ae[m, , drop = FALSE] else NULL 68 | } 69 | 70 | #' @rdname AEs 71 | #' @export 72 | AE <- function(path = ".") { 73 | name <- Sys.getenv("RJ_EDITOR") 74 | if (!nzchar(name)) { 75 | detect_AE(path, FALSE) 76 | } else { 77 | ae <- AEs() 78 | m <- match(tolower(name), tolower(ae$name)) 79 | if (!is.na(m)) ae[m, , drop = FALSE] else NULL 80 | } 81 | } 82 | 83 | #' @rdname AEs 84 | is_AE <- function(path = ".") length(AE(path)) > 0 85 | -------------------------------------------------------------------------------- /R/ae_workload.R: -------------------------------------------------------------------------------- 1 | #' Summarise the reviewer's agree/decline ratio based on past invites 2 | #' 3 | #' The function pulls out the agree/decline incidence of all the reviewers 4 | #' based on past invite and calculate the agree percentage of each reviewer. 5 | #' Use \code{tabulate_articles} first to tabulate all the articles in a particular directory 6 | #' and then apply this function. 7 | #' 8 | #' @param articles a tibble summary of articles in the accepted and submissions folder. Output of \code{tabulate_articles()} 9 | #' @param push whether the reviewer number of review completed by the reviewer should be pushed to the reviewer sheet 10 | #' @importFrom tidyr separate_rows pivot_wider 11 | #' @importFrom stringr str_detect word 12 | #' @importFrom scales label_percent 13 | #' @examples 14 | #' \dontrun{ 15 | #' articles <- tabulate_articles() 16 | #' reviewer_summary(articles) 17 | #' } 18 | #' @export 19 | reviewer_summary <- function(articles, push = FALSE) { 20 | res <- articles %>% 21 | dplyr::select(.data$id, .data$reviewers) %>% 22 | tidyr::unnest(.data$reviewers) %>% 23 | tidyr::separate_rows(comment, sep = "; ") %>% 24 | dplyr::filter(stringr::str_detect(comment, "Agreed|Declined")) %>% 25 | dplyr::mutate(comment = tolower(stringr::word(comment))) %>% 26 | dplyr::group_by(.data$name, comment) %>% 27 | dplyr::count() %>% 28 | dplyr::ungroup() %>% 29 | tidyr::pivot_wider(names_from = comment, values_from = .data$n, values_fill = 0) %>% 30 | dplyr::relocate(.data$name, .data$agreed, .data$declined) %>% 31 | dplyr::mutate( 32 | ratio = .data$agreed / (.data$agreed + .data$declined), 33 | ratio = scales::label_percent()(.data$ratio) 34 | ) 35 | 36 | if (push) { 37 | sheet_raw <- read_reviewer_sheet() 38 | out <- sheet_raw %>% 39 | dplyr::left_join(res %>% dplyr::select(.data$name, .data$agreed), by = c("fname" = "name")) %>% 40 | dplyr::select(.data$agreed) 41 | range <- paste0("I1:I", nrow(sheet_raw)) 42 | googlesheets4::range_write(reviewer_sheet_url, out, range = range) 43 | } 44 | 45 | res 46 | } 47 | 48 | #' Check the number of articles an AE is currently working on 49 | #' 50 | #' This will examine the DESCRIPTION files for articles in 51 | #' the Submissions folder, check articles that have status 52 | #' "with AE". 53 | #' 54 | #' @param articles a tibble summary of articles in the accepted and submissions 55 | #' folder. Output of \code{tabulate_articles()} 56 | #' @param day_back numeric; positive number of day to go back for calculating AE 57 | #' workload. Retains any article where any status entry for an article is 58 | #' newer than `day_back` days ago. 59 | #' @param active_only Toggle to show only active AEs (filtered by end year and comment field). 60 | #' 61 | #' @importFrom dplyr select count left_join right_join filter distinct rename bind_rows 62 | #' @importFrom tidyr unnest replace_na 63 | #' @importFrom tibble as_tibble 64 | #' @examples 65 | #' \dontrun{ 66 | #' ae_workload() 67 | #' } 68 | #' @export 69 | ae_workload <- function(articles = NULL, day_back = 365, active_only = FALSE) { 70 | # select only active AEs 71 | ae_rj <- read.csv(system.file("associate-editors.csv", package = "rj")) 72 | 73 | if (isTRUE(active_only)) { 74 | inactive <- ae_rj$end_year < as.integer(substr(Sys.Date(), 1, 4)) | 75 | grepl("Finished", ae_rj$comment, ignore.case = TRUE) 76 | ae_rj <- ae_rj[!inactive, ] 77 | } 78 | 79 | ae_rj <- ae_rj %>% 80 | select(.data$name, .data$initials, .data$email, .data$comment) %>% 81 | as_tibble() %>% 82 | rename(status = .data$comment) 83 | 84 | 85 | # if don't supply articles, use documented(!) source 86 | if (is.null(articles)) { 87 | articles <- tabulate_articles() 88 | } 89 | 90 | # throw away most of the columns & then unnest status 91 | articles <- articles %>% 92 | select(.data$id, .data$ae, .data$status) %>% 93 | unnest(status) 94 | 95 | # filter articles if day back is provided 96 | # allow this to be NULL but check if is a numeric if supplied 97 | articles <- articles %>% 98 | filter(date >= Sys.Date() - day_back) 99 | 100 | # take only those with "with_AE" status & return rows 101 | # after this we don't need comments or status 102 | assignments <- articles %>% 103 | filter(.data$status == "with AE", .data$ae != "") %>% 104 | select(-c(.data$comments, status)) %>% 105 | distinct(id, .keep_all = TRUE) 106 | 107 | # some people use names, other initials for AEs 108 | # this finds those with only initials and replace the ae with the full name 109 | tmp <- assignments %>% 110 | filter(str_length(.data$ae) < 4) %>% 111 | left_join(ae_rj, by = c("ae" = "initials")) %>% 112 | select(.data$id, .data$name, .data$date, .data$status) %>% 113 | rename(ae = .data$name) 114 | 115 | # ... which allows us to take all those with full names... 116 | assignments %>% 117 | filter(str_length(.data$ae) >= 4) %>% 118 | bind_rows(tmp) %>% # ... bind on those that had initials 119 | count(.data$ae, sort = TRUE) %>% # count the assignments by AE 120 | right_join(ae_rj, by = c("ae" = "name")) %>% # add some some useful info 121 | replace_na(list(n = 0)) 122 | } 123 | 124 | #' @rdname ae_workload 125 | #' @param x a single article, i.e. as.article("Submissions/2020-114") 126 | #' @examples 127 | #' \dontrun{ 128 | #' art <- as.article("Submissions/2020-114") 129 | #' get_AE(art) 130 | #' } 131 | #' @export 132 | get_AE <- function(x) { 133 | list(id = format(x$id), ae = x$ae) 134 | } 135 | 136 | 137 | #' Add AE to the DESCRIPTION 138 | #' 139 | #' Fuzzy match to find the initial of the AE to fill in the article DESCRIPTION. 140 | #' Checks that AE term has not ended. 141 | #' The status field is also updated with a new line of add AE. 142 | #' 143 | #' @param article article id 144 | #' @param name a name used to match AE, can be AE initials, name, github handle, or email 145 | #' @param date the date for updating status 146 | #' @export 147 | 148 | 149 | add_ae <- function(article, name, date = Sys.Date()) { 150 | article <- as.article(article) 151 | ae_list <- filter(read.csv(system.file("associate-editors.csv", 152 | package = "rj" 153 | )), .data$end_year >= as.numeric(substr( 154 | Sys.Date(), 155 | 1, 4 156 | ))) 157 | found <- NA 158 | found <- which(str_detect(ae_list$initials, name)) 159 | if (is.na(found)) { 160 | found <- which(str_detect(ae_list$name, name)) 161 | } 162 | if (is.na(found)) { 163 | found <- which(str_detect(ae_list$github, name)) 164 | } 165 | if (is.na(found)) { 166 | found <- which(str_detect(ae_list$email, name)) 167 | } 168 | if (!is.na(found)) { 169 | article$ae <- ae_list$initials[found] 170 | update_status(article, "with AE", 171 | comments = ae_list$name[found], 172 | date = date 173 | ) 174 | } else { 175 | cli::cli_alert_warning("No AE found. Input the name as the whole or part of the AE name, github handle, or email") 176 | } 177 | return(invisible(article)) 178 | } 179 | 180 | 181 | #' Extract corresponding author from an article 182 | #' @param article Article id, like \code{"2014-01"} 183 | #' @examples 184 | #' \dontrun{ 185 | #' # extract from a single article 186 | #' corr_author("Submissions/2020-114") 187 | #' 188 | #' # extract corresponding authors from the active articles 189 | #' all <- active_articles() 190 | #' purrr::map_dfr(all, corr_author) 191 | #' } 192 | #' @importFrom purrr pluck map 193 | #' @importFrom tibble tibble 194 | #' @export 195 | corr_author <- function(article) { 196 | article <- as.article(article) 197 | 198 | all_authors <- article$authors 199 | # find the index of the author that provide the email 200 | email <- purrr::map(1:length(all_authors), ~ purrr::pluck(all_authors, .x)$email) 201 | idx <- which(purrr::map_lgl(email, ~ !is_null(.x))) 202 | 203 | tibble::tibble( 204 | corr_author = purrr::pluck(all_authors, idx)$name, 205 | email = purrr::pluck(all_authors, idx)$email 206 | ) 207 | } 208 | -------------------------------------------------------------------------------- /R/article.R: -------------------------------------------------------------------------------- 1 | #' S3 class for article objects 2 | #' 3 | #' Create or convert input into an s3 article object 4 | #' 5 | #' @details 6 | #' if the article is not parsable, \code{article()} will 7 | #' print an error message and return a unparsed blob. This ensures that 8 | #' information is not lost even if some articles have parsing errors. 9 | #' 10 | #' Usually the best way to use \code{as_article()} is to have your working directory set to the 11 | #' admin repo, and refer to articles by their id. See the examples section. 12 | #' 13 | #' @param ... Named arguments giving the components of an article: 14 | #' id, authors, title, editor, reviewers, status 15 | #' @param quiet if \code{TRUE} suppresses failure messages. 16 | #' @export 17 | #' @rdname article 18 | article <- function(..., quiet = FALSE) { 19 | tryCatch(make_article(...), 20 | error = function(e) { 21 | article <- unparsed(...) 22 | if (!quiet) { 23 | message("Failed to parse: ") 24 | print(article) 25 | message(e, "\n") 26 | } 27 | article 28 | } 29 | ) 30 | } 31 | 32 | #' Convert input into an article. 33 | #' 34 | #' @param id a path to a DESCRIPTION, a path to a directory containing 35 | #' DESCRIPTION, or a article name, found in a sub-directory rejected, 36 | #' accepted or submissions 37 | #' @export 38 | #' @examples 39 | #' \dontrun{ 40 | #' as.article("2012-01") 41 | #' as.article("2012-02") 42 | #' } 43 | #' @rdname article 44 | as.article <- function(id) { 45 | if (is.article(id)) { 46 | return(id) 47 | } 48 | if (length(id) != 1) stop("Invalid ID") 49 | 50 | # Check to see if it's an existing directory 51 | if (file.exists(id) && is.dir(id)) { 52 | id <- file.path(id, "DESCRIPTION") 53 | } 54 | if (file.exists(id)) { 55 | path <- id 56 | } else { 57 | # Otherwise we use the articles root 58 | base <- c( 59 | "Rejected", "Accepted", "Submissions", 60 | file.path("Proofs", dir(file.path(get_articles_path(), "Proofs"))) 61 | ) 62 | pos <- file.exists(file.path(get_articles_path(), base, id, "DESCRIPTION")) 63 | 64 | if (sum(pos) == 0) stop("Can't find ", id, call. = FALSE) 65 | if (sum(pos) > 1) { 66 | path <- file.path(get_articles_path(), base[pos], id) 67 | if (sum(pos) == 2 && "Rejected" %in% basename(dirname(path))) { 68 | warning(id, " found in multiple locations, ignoring the Rejected folder copy.") 69 | pos[1] <- FALSE 70 | } else { 71 | stop(id, " found in multiple locations: ", paste(path, collapse=", "), call. = FALSE) 72 | } 73 | } 74 | path <- file.path(get_articles_path(), base[pos], id, "DESCRIPTION") 75 | } 76 | 77 | load_article(path) 78 | } 79 | 80 | load_article <- function(path, quiet = FALSE) { 81 | fields <- c("ID", "Slug", "Authors", "Keywords", "OtherIDs", "Title", "Editor", "AE", "Reviewers", "Status", "Suppl") 82 | dcf <- read.dcf(path, fields = fields, keep.white = fields) 83 | if (nrow(dcf) != 1) stop("DCF parsing error: ", path, call. = FALSE) 84 | 85 | # Remove field names that keep.white incorrectly preserves 86 | for (field in fields) { 87 | dcf[, field] <- gsub(paste(field, ": ?", sep = ""), "", dcf[, field]) 88 | } 89 | # Convert missing values to empty strings 90 | dcf[is.na(dcf)] <- "" 91 | colnames(dcf) <- tolower(colnames(dcf)) 92 | 93 | dcf <- as.list(as.data.frame(dcf, stringsAsFactors = FALSE)) 94 | # Only should be manually set in tests 95 | if (is.null(dcf$id) || identical(dcf$id, "")) { 96 | dcf$id <- basename(dirname(path)) 97 | } 98 | dcf$path <- dirname(path) 99 | do.call(article, dcf) 100 | } 101 | 102 | is.article <- function(x) inherits(x, "article") 103 | 104 | make_article <- function(id, slug = "", authors = "", title = "", editor = "", ae = "", 105 | reviewers = "", status = "", path = "",type = "", suppl = "", 106 | keywords = "", otherids = "") { 107 | structure(list( 108 | id = parse_id(id), 109 | other_id = otherids, 110 | slug = slug, 111 | suppl = parse_supplementaries(suppl), 112 | path = path, 113 | type = type, 114 | authors = parse_address_list(authors), 115 | keywords = str_c(keywords, sep = ", "), 116 | title = str_trim(title), 117 | editor = str_trim(editor), 118 | ae = str_trim(ae), 119 | reviewers = parse_address_list(reviewers), 120 | status = parse_status_list(status) 121 | ), class = "article") 122 | } 123 | 124 | 125 | parse_supplementaries <- function(suppl) { 126 | x <- str_trim(str_split(suppl, ",\\s*\n?")[[1]]) 127 | x <- x[str_length(x) > 0] 128 | xs <- lapply(x, function(y) { 129 | class(y) <- "supplfile" 130 | y 131 | }) 132 | class(xs) <- "supplfile_list" 133 | xs 134 | } 135 | 136 | #' @export 137 | format.supplfile_list <- function(x, ...) { 138 | suppls <- lapply(x, format) 139 | paste(suppls, collapse = ",\n ") 140 | } 141 | 142 | #' @export 143 | format.supplfile <- function(x, ...) { 144 | paste0(x) 145 | } 146 | 147 | empty.supplfile_list <- function(x) length(x) == 0 148 | 149 | 150 | #' @method format article 151 | #' @export 152 | format.article <- function(x, ...) { 153 | authors <- format(x$authors) 154 | reviewers <- format(x$reviewers) 155 | status <- format(x$status) 156 | 157 | paste( 158 | "ID: ", format(x$id), "\n", 159 | if (!empty(x$other_id)) paste0("OtherIDs: ", x$other_id, "\n"), 160 | "Title: ", x$title, "\n", 161 | if (!empty(x$slug)) paste0("Slug: ", x$slug, "\n"), 162 | if (length(x$suppl)) paste0("Suppl:\n ", paste(unlist(x$suppl), collapse=', '), "\n"), 163 | "Authors:", if (!empty(authors)) "\n ", authors, "\n", 164 | if (!empty(x$keywords)) paste0("Keywords: ", x$keywords, "\n"), 165 | "Editor: ", x$editor, "\n", 166 | "AE: ", x$ae, "\n", 167 | "Reviewers:", if (!empty(reviewers)) "\n ", reviewers, "\n", 168 | "Status: ", if (!empty(status)) "\n ", status, 169 | sep = "" 170 | ) 171 | } 172 | 173 | save_article <- function(article, quiet = FALSE) { 174 | stopifnot(is.article(article)) 175 | stopifnot(!is.null(article$path)) 176 | 177 | path <- file.path(article$path, "DESCRIPTION") 178 | writeLines(format(article), path, useBytes = TRUE) 179 | cli::cli_alert_info(paste("Updated", path)) 180 | invisible(article) 181 | } 182 | 183 | 184 | #' @method print article 185 | #' @export 186 | print.article <- function(x, ...) cat(format(x), "\n") 187 | 188 | unparsed <- function(...) { 189 | structure(list(...), class = c("unparsed", "article")) 190 | } 191 | 192 | #' @method format unparsed 193 | #' @export 194 | format.unparsed <- function(x, ...) { 195 | paste( 196 | "ID:", x$id, "\n", 197 | "Title:", x$title, "\n", 198 | "Authors:", x$authors, "\n", 199 | "Editor:", x$editor, "\n", 200 | "Reviewers:", x$reviewers, "\n", 201 | "Status:", x$status, 202 | sep = "" 203 | ) 204 | } 205 | 206 | article_ids <- function(dirs = TRUE) { 207 | if(identical(dirs, TRUE)) { 208 | dirs <- c("Accepted", "Proofs", "Rejected", "Submissions") 209 | } 210 | c( 211 | # Articles not in proofs 212 | dir(file.path(get_articles_path(), setdiff(dirs, "Proofs")), pattern = "\\d{4}-\\d{2,}"), 213 | # Articles in proofs 214 | dir( 215 | # In all proofs... 216 | dir(file.path(get_articles_path(), intersect(dirs, "Proofs")), pattern = "\\d{4}-[1-4]", full.names = TRUE), 217 | # Search for articles 218 | pattern = "\\d{4}-\\d{2,}" 219 | ) 220 | ) 221 | } 222 | 223 | #' Tabulate article descriptions 224 | #' 225 | #' Produce a table describing articles in specific directories. 226 | #' 227 | #' @param dirs The directories containing articles to be searched and tabulated. 228 | #' If TRUE, then all directories will be searched. 229 | #' 230 | #' @export 231 | tabulate_articles <- function(dirs = c("Accepted", "Submissions")) { 232 | # Find matching ids 233 | ids <- article_ids(dirs) 234 | 235 | # Read DESCRIPTION from ids and arrange in data frame. 236 | purrr::map_dfr(ids, tabulate_single) 237 | } 238 | 239 | 240 | tabulate_single <- function(id){ 241 | art <- tryCatch(as.article(id), error = function(e) { 242 | stop(stringr::str_glue("Failed to parse the DESCRIPTION file of {id}: {e$message}")) 243 | }) 244 | lst_to_tbl <- function(x) { 245 | x <- lapply(x, function(z) if(rlang::is_empty(z)) NA else z) 246 | tibble::as_tibble(x) 247 | } 248 | field_tbl <- function(field) { 249 | list(purrr::map_dfr(field, lst_to_tbl)) 250 | } 251 | art$id <- format(art$id) 252 | art$suppl <- list(art$suppl) 253 | art$authors <- field_tbl(art$authors) 254 | art$reviewers <- field_tbl(art$reviewers) 255 | art$status <- field_tbl(art$status) 256 | tibble::new_tibble(art, nrow = 1) 257 | } 258 | -------------------------------------------------------------------------------- /R/articles.R: -------------------------------------------------------------------------------- 1 | # Parse articles in subdirectories of base. 2 | # On error raises articleListError conditon which has 3 | # all individual errors as a list in the `errors` element. 4 | .parse.articles <- function(subpaths, base=get_articles_path()) { 5 | bases <- file.path(base, subpaths) 6 | paths <- grep("2\\d{3}-\\d{1,3}$", dir(bases, full.names=TRUE), value=TRUE) 7 | l <- lapply(paths, function(id) tryCatch(as.article(id), 8 | error=function(e) e)) 9 | names(l) <- paths 10 | err <- sapply(l, inherits, "error") 11 | if (any(err)) 12 | stop(errorCondition( 13 | paste0("The following directories had errors:\n", paste(names(l)[err], collapse="\n")), 14 | errors=l[err], class="articleErrorList")) 15 | l 16 | } 17 | 18 | #' List articles. 19 | #' 20 | #' List all articles in common directories. 21 | #' 22 | #' \itemize{ 23 | #' \item \code{active_articles}: \file{Submissions/}, \file{Accepted/} 24 | #' \item \code{accepted_articles}: \file{Accepted/} 25 | #' } 26 | #' @param include A character vector of directories to include. 27 | #' @export 28 | active_articles <- function(include = c("Submissions", "Accepted")) 29 | .parse.articles(include) 30 | 31 | #' @rdname active_articles 32 | #' @export 33 | accepted_articles <- function() 34 | .parse.articles("Accepted") 35 | 36 | news_articles <- function(issue) 37 | .parse.articles("News_items") 38 | 39 | #' Generate a new id value. 40 | #' 41 | #' Inspects submissions/, accepted/ and rejected to figure out which 42 | #' id is next in sequence. 43 | #' 44 | #' @export 45 | new_id <- function() { 46 | ids <- dir(file.path(get_articles_path(), c("Submissions", "Accepted", "Rejected"))) 47 | ids <- lapply(ids, parse_id) 48 | 49 | this_year <- Filter(function(x) x$year == year(), ids) 50 | 51 | if (length(this_year) == 0) { 52 | id(year(), 1) 53 | } else { 54 | seqs <- vapply(this_year, function(x) x$seq, integer(1)) 55 | id(year(), max(seqs) + 1L) 56 | } 57 | } 58 | 59 | #' Find articles with a given status. 60 | #' 61 | #' @param articles A vector of articles, as given by accepted_articles() 62 | #' @param status The status you are looking for 63 | #' @export 64 | filter_status <- function(articles, status) { 65 | Filter(function(a) has_status(a, status), articles) 66 | } 67 | 68 | #' Get articles to go online 69 | #' 70 | #' Find the articles that are accepted but have not yet been 71 | #' published to online 72 | #' 73 | #' @export 74 | get_accepted_but_not_online <- function() { 75 | l <- accepted_articles() 76 | o <- filter_status(l, "online") 77 | l_id <- sub("Accepted/", "", sapply(l, '[[', 5)) 78 | o_id <- sub("Accepted/", "", sapply(o, '[[', 5)) 79 | l_id[!(l_id %in% o_id)] 80 | } 81 | -------------------------------------------------------------------------------- /R/author-email.R: -------------------------------------------------------------------------------- 1 | #' Extract email from leading author of an issue for emailing 2 | #' 3 | #' If the email of leading author is not available, the first author with 4 | #' available email will be used. 5 | #' 6 | #' @param issue the issue number in the `Proofs` folder, i.e. "2021-2" 7 | #' 8 | #' @export 9 | #' @examples 10 | #' \dontrun{ 11 | #' author_emails("2021-2") 12 | #' } 13 | author_emails <- function(issue){ 14 | 15 | id <- dir(issue_dir(issue), pattern = "\\d{4}-\\d{2}", full.names = TRUE) 16 | 17 | dt <- purrr::map(id, function(x){ 18 | desc <- load_article(file.path(x, "DESCRIPTION")) 19 | Filter(function(x)!is.null(x$email), desc$authors)[[1]] 20 | }) 21 | 22 | out <- purrr::map_dfr(dt, ~tibble(name = .x$name, email = .x$email)) 23 | out 24 | } 25 | 26 | issue_dir <- function(id) { 27 | file.path(get_articles_path(), "Proofs", id) 28 | } 29 | -------------------------------------------------------------------------------- /R/current.R: -------------------------------------------------------------------------------- 1 | #' Generate a summary of current and recent assignments to each editor 2 | #' 3 | #' This should be run weekly. 4 | #' 5 | #' @export 6 | assignments <- function() { 7 | # Grab all articles 8 | rejected <- .parse.articles("Rejected") 9 | accepted <- .parse.articles("Accepted") 10 | submissions <- .parse.articles("Submissions") 11 | # Remove resubmissions 12 | resubmit <- lapply(submissions, function(u) last_status(u)$status == "resubmission") |> 13 | unlist() 14 | submissions[resubmit] <- NULL 15 | 16 | # Calculate summary by editor 17 | active <- lapply(submissions, function(u) u$editor) |> unlist() 18 | active <- table(active) |> tibble::as_tibble() 19 | active$active[active$active == ""] <- "Unassigned" 20 | active <- active |> 21 | tidyr::pivot_wider(names_from = "active", values_from = "n") |> 22 | dplyr::mutate(Assignment = "Active") 23 | 24 | # Remove articles not submitted in last year 25 | all_articles <- c(rejected, accepted, submissions) 26 | submitted <- lapply(all_articles, function(u) u$status[[1]]$date) |> 27 | unlist() |> as.Date() 28 | all_articles <- all_articles[submitted > Sys.Date() - 365] 29 | submitted <- submitted[submitted > Sys.Date() - 365] 30 | last3 <- submitted > Sys.Date() - 90 31 | last1 <- submitted > Sys.Date() - 30 32 | # Replace missing editors 33 | editors <- lapply(all_articles, function(u) u$editor) |> unlist() 34 | editors[editors == "" & submitted < as.Date("2024-12-31")] <- "MV" 35 | editors[editors == "" & submitted >= as.Date("2025-01-01")] <- "RH" 36 | # Calculate summary 37 | last12 <- table(editors) |> 38 | tibble::as_tibble() |> 39 | tidyr::pivot_wider(names_from = "editors", values_from = "n") |> 40 | dplyr::mutate(Assignment = "Last 12 months") 41 | last3 <- table(editors[last3], dnn = "editors") |> 42 | tibble::as_tibble() |> 43 | tidyr::pivot_wider(names_from = "editors", values_from = "n") |> 44 | dplyr::mutate(Assignment = "Last 3 months") 45 | last1 <- table(editors[last1], dnn = "editors") |> 46 | tibble::as_tibble() |> 47 | tidyr::pivot_wider(names_from = "editors", values_from = "n") |> 48 | dplyr::mutate(Assignment = "Last month") 49 | 50 | output <- dplyr::bind_rows(active, last12, last3, last1) |> 51 | dplyr::select(Assignment, dplyr::everything()) 52 | output[is.na(output)] <- 0 53 | output <- as.data.frame(output) 54 | rownames(output) <- output$Assignment 55 | output$Assignment <- NULL 56 | return(output) 57 | } 58 | 59 | utils::globalVariables("Assignment") 60 | -------------------------------------------------------------------------------- /R/doi.R: -------------------------------------------------------------------------------- 1 | # find_stack_bib_keys <- function(article) { 2 | # article <- as.article(article) 3 | # article_path <- article$path 4 | # RJw <- readLines(paste0(article_path, "/RJwrapper.tex")) 5 | # str_search_inp <- "((\\\\input\\{)([-a-zA-Z0-9_\\+\\.]*)(\\}))" 6 | # inp_str <- c(na.omit(str_trim(str_extract(RJw, str_search_inp)))) 7 | # inps <- c(str_locate(inp_str, "((\\{)([-a-zA-Z0-9_\\+\\.]*)(\\}))")) 8 | # inp_tex <- str_sub(inp_str, inps[1] + 1, inps[2] - 1) 9 | # if (str_sub(inp_tex, str_length(inp_tex) - 3, str_length(inp_tex)) != ".tex") { 10 | # inp_tex <- paste0(inp_tex, ".tex") 11 | # } 12 | # tex <- readLines(paste0(article_path, "/", inp_tex)) 13 | # str_search_bib <- "((\\\\bibliography\\{)([-a-zA-Z0-9_\\+\\.]*(\\})))" 14 | # bib_str <- c(na.omit(str_trim(str_extract(tex, str_search_bib)))) 15 | # bibs <- c(str_locate(bib_str, "((\\{)([-a-zA-Z0-9_\\+\\.]*)(\\}))")) 16 | # inp_bib <- str_sub(bib_str, bibs[1] + 1, bibs[2] - 1) 17 | # if (str_sub(inp_bib, str_length(inp_bib) - 3, str_length(inp_bib)) != ".bib") { 18 | # inp_bib <- paste0(inp_bib, ".bib") 19 | # } 20 | # tex <- readLines(paste0(article_path, "/", inp_tex)) 21 | # bibfile <- paste0(article_path, "/", inp_bib) 22 | # if (!file.exists(bibfile)) stop(bibfile, " not found in ", article_path) 23 | # # bibtex is orphaned, pass CMD check with the following code: 24 | # # https://stackoverflow.com/questions/61440165/r-check-warning-requires-orphaned-package 25 | # biblist <- NA 26 | # if (requireNamespace("bibtex", quietly=TRUE)) { 27 | # biblist <- bibtex::read.bib(bibfile) 28 | # } else { 29 | # cli::cli_alert_warning("Require {.field bibtex} to parse the bibtex") 30 | # } 31 | # names(biblist) 32 | # } 33 | -------------------------------------------------------------------------------- /R/email.R: -------------------------------------------------------------------------------- 1 | #' Send an email template. 2 | #' 3 | #' Interpolate article values into a template, and create a new (unsent) 4 | #' email in your default mail client. The email is created using 5 | #' \code{\link{browseURL}} and the mailto protocol, so it must be relatively 6 | #' brief. 7 | #' 8 | #' @section Text format: 9 | #' The template should be divided into header and body with \code{---}. 10 | #' The header should contain fields and values separated by \code{:} - 11 | #' only a limited 12 | #' 13 | #' @section Template parameters: 14 | #' The templates use whisker to insert template values. These have the form 15 | #' \code{\{\{field_name\}\}}. You can use any field from the description as 16 | #' well as the following special fields: 17 | #' 18 | #' \itemize{ 19 | #' \item name: the name of the first author 20 | #' \item email: email address of first author 21 | #' \item editor: name of editor 22 | #' \item me: your name, as determine by envvar \code{RJ_NAME} 23 | #' } 24 | #' 25 | #' 26 | #' @param article An article id, e.g. \code{"2013-01"} 27 | #' @param template The name of a template (without extension) found 28 | #' in \code{inst/templates}. 29 | #' @export 30 | email_template <- function(article, template) { 31 | article <- as.article(article) 32 | text <- render_template(article, template) 33 | save_email(article, text) 34 | email_text(text) 35 | } 36 | 37 | # Save email to correspondence folder 38 | 39 | save_email <- function(article, text) { 40 | # Create correspondence folder if necessary 41 | dir <- file.path(article$path, "correspondence") 42 | if (!dir.exists(dir)) dir.create(dir, recursive = TRUE) 43 | # Create filename using date and time in UTC 44 | file <- file.path(dir, paste0(format(Sys.time(), "%Y-%m-%d-%H-%m", tz="UTC"), "_email.txt")) 45 | writeLines(text, file) 46 | } 47 | 48 | #' Generate an email template. 49 | #' 50 | #' @param text character vector, text of the e-mail (including headers) 51 | #' @param means string, one of "mailto", "browser" (both open mailto: URL), 52 | #' "show" (file.show), "edit" (file.edit), "open" (shell open) or 53 | #' "clip" (system clipboard). Defaults to RJ_EMAIL_TOOL environment 54 | #' variable. 55 | email_text <- function(text, means = getOption("RJ_EMAIL_TOOL", "mailto")) { 56 | stopifnot(is.character(text)) 57 | text <- paste(text, collapse = "\n") 58 | 59 | pieces <- strsplit(text, "---", fixed = TRUE)[[1L]] 60 | stopifnot(is.character(pieces), length(pieces) == 2) 61 | 62 | head <- pieces[1] 63 | body <- pieces[2] 64 | 65 | head_lines <- str_split(head, "\n")[[1]] 66 | head_lines <- head_lines[head_lines != ""] 67 | head_pieces <- str_split_fixed(head_lines, ": ", n = 2) 68 | 69 | fields <- setNames(as.list(head_pieces[, 2]), tolower(head_pieces[, 1])) 70 | to <- fields$to 71 | fields$to <- NULL 72 | 73 | fields$body <- str_trim(body) 74 | fields <- lapply(fields, URLencode, reserved = TRUE) 75 | 76 | url <- paste0( 77 | "mailto:", to, "?", 78 | paste0(names(fields), "=", unlist(fields), collapse = "&") 79 | ) 80 | 81 | if (means == "mailto" || means == "browser") { 82 | if (is.function(getOption("browser"))) { 83 | cli::cli_alert_info( 84 | "You have setup a custom 'browser' function which may or may not work. 85 | If your e-mail doesn't open up ready to send, try 86 | {.code options(browser=Sys.getenv('R_BROWSER'))}") 87 | } 88 | return(browseURL(url)) 89 | } 90 | tmp <- tempfile("mail", fileext = ".txt") 91 | writeLines(text, tmp) 92 | switch(means, 93 | show = file.show(tmp), 94 | edit = file.show(tmp), 95 | open = system(paste("open", shQuote(tmp))), 96 | clip = { 97 | if (length(grep("^darwin", R.Version()$os))) { 98 | message("E-mail has been written to the macOS clipboard") 99 | on.exit(unlink(tmp)) 100 | con <- pipe("pbcopy") 101 | writeLines(text, con) 102 | close(con) 103 | } else if (.Platform$OS.type == "windows") { 104 | message("E-mail has been written to the Windows clipboard") 105 | on.exit(unlink(tmp)) 106 | utils::writeClipboard(text, format = 1) 107 | } else { 108 | on.exit(unlink(tmp)) 109 | if ( 110 | suppressWarnings( 111 | system(paste("xsel -i -c <", shQuote(tmp)), ignore.stdout=TRUE, ignore.stderr=TRUE) != 0 && 112 | system(paste("xclip -selection clipboard <", shQuote(tmp)), ignore.stdout=TRUE, ignore.stderr=TRUE) != 0 113 | )) 114 | stop("Neither xclip not xsel works - please install either tool") 115 | message("E-mail has been written to the X11 clipboard") 116 | } 117 | }, 118 | { 119 | unlink(tmp) 120 | stop("Unknown RJ_EMAIL_TOOL, must be one of mailto, browser, show, edit, open or clip.") 121 | }) 122 | } 123 | 124 | find_template <- function(name) { 125 | if (name == "major revision") { 126 | path <- system.file("templates/revision-major.txt", package = "rj") 127 | } else if (name == "minor revision") { 128 | path <- system.file("templates/revision-minor.txt", package = "rj") 129 | } else { 130 | path <- system.file("templates", paste0(name, ".txt"), package = "rj") 131 | } 132 | if (path == "") stop("Template not found: ", name, call. = FALSE) 133 | 134 | path 135 | } 136 | 137 | guess_real_name <- function() { 138 | Sys.getenv("RJ_NAME", 139 | unset = if (.Platform$OS.type == "unix") { 140 | login <- Sys.info()[["login"]] 141 | finger <- system(paste("finger", login), intern = TRUE) 142 | sub(".*Name: ", "", finger[1L]) 143 | } else { 144 | "Use RJ_NAME envvar to set your name" 145 | } 146 | ) 147 | } 148 | 149 | as.data <- function(x) { 150 | stopifnot(is.article(x)) 151 | 152 | data <- lapply(x, format) 153 | data$name <- x$authors[[1]]$name 154 | data$email <- x$authors[[1]]$email 155 | if (!empty(x$editor)) data$editor <- editors(TRUE)[x$editor] 156 | data$me <- guess_real_name() 157 | 158 | data 159 | } 160 | 161 | # @importFrom utils read.csv 162 | # @param by.name logical, if \code{TRUE} then names are names and 163 | # values e-mails, otherwise names are e-mails and values names 164 | editors <- function(by.name=FALSE) { 165 | cli::cli_alert_info("Reading inst/editor.csv") 166 | fname <- system.file("editors.csv", package = "rj") 167 | out <- read.csv(fname, stringsAsFactors = FALSE) 168 | # out <- read.csv("editors.csv", stringsAsFactors = FALSE) 169 | if (by.name) 170 | `names<-`(out[["email"]], out[["name"]]) 171 | else 172 | `names<-`(out[["name"]], out[["email"]]) 173 | } 174 | 175 | #' @importFrom whisker whisker.render 176 | render_template <- function(article, template) { 177 | article <- as.article(article) 178 | template <- find_template(template) 179 | 180 | whisker.render(readLines(template), as.data(article)) 181 | } 182 | -------------------------------------------------------------------------------- /R/git.R: -------------------------------------------------------------------------------- 1 | ## calls git with the given arguments. No path expansion is done, 2 | ## but all arguments are quoted. All arguments are unfolded, i.e., 3 | ## they don't need to be scalar but the result will be single 4 | ## character vector sequence. 5 | ## If GIT is not set it defaults to "git" 6 | git <- function(..., git=Sys.getenv("GIT")) { 7 | if (!nzchar(git)) git <- "git" 8 | args <- sapply(as.character(unlist(list(...))), shQuote) 9 | system2(git, args) 10 | } 11 | -------------------------------------------------------------------------------- /R/id.R: -------------------------------------------------------------------------------- 1 | parse_id <- function(x) { 2 | if (is.id(x)) { 3 | return(x) 4 | } 5 | 6 | re <- "^([0-9]{4})-([0-9]{2,3})[a-z]?$" 7 | 8 | x <- str_trim(x) 9 | 10 | if (!str_detect(x, re)) stop("ID must have form XXXX-YYY?") 11 | 12 | pieces <- str_match(x, re)[1, ] 13 | year <- pieces[2] 14 | seq <- pieces[3] 15 | 16 | if (!is.number(year)) stop("Year must be a number") 17 | if (!is.number(seq)) stop("ID must be a number") 18 | 19 | if (year > year()) stop("Year must be in the present or past") 20 | if (year < 2002) stop("Year must be >= 2002") 21 | 22 | id(as.integer(year), as.integer(seq)) 23 | } 24 | 25 | id <- function(year, seq) { 26 | stopifnot(is.numeric(year), length(year) == 1) 27 | stopifnot(is.numeric(seq), length(seq) == 1) 28 | 29 | year <- as.integer(year) 30 | seq <- as.integer(seq) 31 | 32 | structure(list(year = year, seq = seq), class = "id") 33 | } 34 | 35 | is.id <- function(x) inherits(x, "id") 36 | 37 | #' @method format id 38 | #' @export 39 | format.id <- function(x, ...) { 40 | paste(x$year, sprintf("%02d", x$seq), sep = "-") 41 | } 42 | #' @method print id 43 | #' @export 44 | print.id <- function(x, ...) cat(format(x), "\n") 45 | 46 | year <- function() as.POSIXlt(Sys.Date())$year + 1900 47 | 48 | is.number <- function(x) { 49 | suppressWarnings(!is.na(as.numeric(x))) 50 | } 51 | 52 | #' @method as.character id 53 | #' @export 54 | as.character.id <- function(x, ...) 55 | format.id(x) 56 | -------------------------------------------------------------------------------- /R/issue.R: -------------------------------------------------------------------------------- 1 | #' Make a proof of an issue 2 | #' 3 | #' The `make_proof()` function is the first step to creating an issue. It moves 4 | #' the 'proofed' articles from the `Accepted` folder and news articles from 5 | #' `News_items/\{id\}` into `Proofs/\{id\}`. 6 | #' 7 | #' After the proof is made with this function, `publish_issue()` can be used to 8 | #' publish these articles into the `rjournal.github.io` repository. 9 | #' 10 | #' @param id The id of the issue to proof 11 | #' @param exec Set to TRUE to make the proof, the default (FALSE) allows a preview of which articles will be moved where. 12 | #' 13 | #' @export 14 | make_proof <- function(id, exec = FALSE) { 15 | old <- setwd(get_articles_path()) 16 | on.exit(setwd(old)) 17 | dir <- issue_dir(id) 18 | if (!file.exists(file.path(dir, "news"))) { 19 | xfun::dir_create(file.path(dir, "news")) 20 | } 21 | 22 | arts <- accepted_articles() 23 | ready <- filter_status(arts, "proofed") 24 | for (art in ready) { 25 | if (exec) { 26 | system(paste( 27 | "git mv", 28 | shQuote(art$path), 29 | shQuote(file.path(dir, format(art$id))) 30 | )) 31 | } else { 32 | cat(art$path, file.path(dir, format(art$id)), "\n") 33 | } 34 | } 35 | 36 | news <- news_articles(id) 37 | for (art in news) { 38 | if (exec) { 39 | file.copy( 40 | art, 41 | file.path(dir, "news"), 42 | recursive = TRUE 43 | ) 44 | } else { 45 | cat(art, file.path(dir, "news", basename(art)), "\n") 46 | } 47 | } 48 | 49 | if (!exec) { 50 | cli_alert_info("If these articles look correct, re-run the function with `exec = TRUE` to execute the move.") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /R/late.R: -------------------------------------------------------------------------------- 1 | #' Find late AEs for submissions being handled by a given editor 2 | #' 3 | #' This should be run regularly and AEs chased up if they are late. 4 | #' 5 | #' @param editor The editor handling the submissions 6 | #' 7 | #' @return A data frame of AEs who are late in handling a paper. 8 | #' @details The number of stars has the following meaning: 9 | #' \itemize{ 10 | #' \item 1 star: not responded in more than 12 weeks; 11 | #' \item 2 stars: not responded in more than 18 weeks; 12 | #' \item 3 stars: not responded in more than 24 weeks; 13 | #' } 14 | #' Please chase up late AEs. 15 | #' @export 16 | 17 | late_aes <- function(editor) { 18 | articles <- get_assignments(editor) |> 19 | get_latest() 20 | articles <- articles[articles$status == "with AE", ] 21 | if (NROW(articles) == 0L) { 22 | warning("No articles are with AEs") 23 | return(invisible(NULL)) 24 | } 25 | # Add stars 26 | days <- Sys.Date() - as.Date(articles$date) 27 | nstars <- unlist(lapply(days, function(u) sum(u > c(12L, 18L, 24L) * 7))) 28 | articles$stars <- stringr::str_dup("*", nstars) 29 | output <- dplyr::arrange(articles, date) |> as.data.frame() 30 | # Replace AE initials with name 31 | aes <- AEs() 32 | output$ae <- aes[match(output$ae, aes$initials), "name"] 33 | # Return 34 | output <- output[output$stars != "", c("id", "date", "ae", "stars")] 35 | if (NROW(output) == 0L) { 36 | return(invisible(NULL)) 37 | } else { 38 | return(output) 39 | } 40 | } 41 | 42 | #' Find all reviewers for submissions being handled by a given editor 43 | #' 44 | #' @param editor The editor or associate editor handling the submissions 45 | #' 46 | #' @return A data frame of reviewers 47 | #' @export 48 | 49 | get_reviewers <- function(editor) { 50 | articles <- get_assignments(editor) |> 51 | get_latest() 52 | articles <- articles[articles$status == "out for review", ] 53 | if (NROW(articles) == 0L) { 54 | warning("No articles are out for review") 55 | return(invisible(NULL)) 56 | } else { 57 | articles[c("id", "reviewers")] |> 58 | tidyr::unnest(reviewers) 59 | } 60 | } 61 | 62 | #' Find late reviewers for submissions being handled by a given editor 63 | #' 64 | #' This should be run regularly and reviewers chased up if they are late. 65 | #' 66 | #' @param editor The editor or associate editor handling the submissions 67 | #' 68 | #' @return A data frame of reviewers who have not responded or not submitted their review on time. 69 | #' @details The number of stars has the following meaning: 70 | #' \itemize{ 71 | #' \item 1 star: not responded in more than a week or accepted more than 4 weeks ago; 72 | #' \item 2 stars: not responded in more than 2 weeks or accepted more than 8 weeks ago; 73 | #' \item 3 stars: not responded in more than 3 weeks or accepted more than 12 weeks ago; 74 | #' } 75 | #' Please chase up late reviewers. 76 | #' If a reviewer has declined, use \code{\link{decline_reviewer}} to remove them from the list. 77 | #' If you have decided to abandon a reviewer, use \code{\link{abandon_reviewer}}. 78 | #' @export 79 | 80 | late_reviewers <- function(editor) { 81 | reviewers <- get_reviewers(editor) 82 | if (is.null(reviewers)) { 83 | return(invisible(NULL)) 84 | } 85 | status <- last_reviewer_status(reviewers$comment) 86 | invited <- status == "Invited" & !is.na(reviewers$comment) 87 | agreed <- status == "Agreed" & !is.na(reviewers$comment) 88 | output <- dplyr::arrange(reviewers[invited | agreed, ], comment) 89 | agreed <- last_reviewer_status(output$comment) == "Agreed" 90 | dates <- stringr::str_extract(output$comment, "[0-9]*\\-[0-9]*\\-[0-9]*$") |> 91 | as.Date() 92 | days <- Sys.Date() - dates 93 | agreed_stars <- unlist(lapply(days, function(u) sum(u > c(4L, 8L, 12L) * 7))) 94 | invited_stars <- unlist(lapply(days, function(u) sum(u > c(7L, 14L, 21L)))) 95 | nstars <- (invited_stars * !agreed) + (agreed_stars * agreed) 96 | output$stars <- stringr::str_dup("*", nstars) 97 | output <- as.data.frame(dplyr::arrange(output, -nstars)) 98 | output$status <- stringr::str_extract( 99 | output$comment, 100 | "[a-zA-Z\\s]*[Agreed|Invited] [0-9]*\\-[0-9]*\\-[0-9]*$" 101 | ) 102 | output <- output[output$stars != "", c("id", "name", "status", "stars")] 103 | if (NROW(output) == 0L) { 104 | return(invisible(NULL)) 105 | } else { 106 | return(output) 107 | } 108 | } 109 | 110 | #' Find articles that need reviewers for submissions being handled by a given editor. 111 | #' 112 | #' Returns all articles with fewer than 2 invited reviewers. 113 | #' This should be run regularly and new reviewers appointed if necessary. 114 | #' 115 | #' @param editor The editor or associate editor handling the submissions 116 | #' 117 | #' @return A data frame of papers needing more reviewers 118 | #' @export 119 | 120 | need_reviewers <- function(editor) { 121 | # Find existing reviewers 122 | reviewers <- get_reviewers(editor = editor) 123 | if (!is.null(reviewers)) { 124 | reviewers <- dplyr::filter(reviewers, !is.na(reviewers$comment)) 125 | # Extract last status 126 | status <- last_reviewer_status(reviewers$comment) 127 | reviewers <- reviewers[!status %in% c("Declined", "Abandoned"), ] |> 128 | dplyr::select(id) |> 129 | dplyr::count(id) |> 130 | dplyr::filter(n < 2) 131 | } 132 | 133 | # Add papers ready for review 134 | papers <- report(editor = editor) |> 135 | dplyr::filter(status == "waiting (editor)" | status == "needs reviewers (editor)") |> 136 | dplyr::mutate(n = 0) |> 137 | dplyr::select(id, n) |> 138 | tibble::as_tibble() 139 | # Combine both lists and sort by number of reviewers 140 | output <- bind_rows(reviewers, papers) 141 | output <- as.data.frame(dplyr::arrange(output, n)) 142 | colnames(output) <- c("id", "number_reviewers") 143 | 144 | # Remove papers that need decision 145 | output <- output[!(output$id %in% need_decision(editor)$id), ] 146 | 147 | if (NROW(output) == 0L) { 148 | return(invisible(NULL)) 149 | } else { 150 | return(output) 151 | } 152 | } 153 | 154 | # Extract last reviewer status from string 155 | last_reviewer_status <- function(string) { 156 | # Remove last date 157 | status <- stringr::str_remove(string, " [0-9]*\\-[0-9]*\\-[0-9]*$") 158 | # Extract last status 159 | stringr::str_extract(status, "[a-zA-Z]*$") 160 | } 161 | 162 | 163 | # Find number of completed reviews for article 164 | 165 | completed_reviews <- function(x) { 166 | stopifnot(is.article(x)) 167 | if (is.null(x$reviewers)) { 168 | return(0L) 169 | } 170 | reviews <- reviewer_status(x) 171 | NROW(reviews[reviews$st %in% c("major", "minor", "reject", "accept"), ]) 172 | } 173 | 174 | 175 | #' Find articles that have at least two completed reviews and need a decision 176 | #' 177 | #' Returns all articles with at least 2 completed reviews but no decision 178 | #' This should be run regularly and decisions made if necessary. 179 | #' 180 | #' @param editor The editor or associate editor handling the submissions 181 | #' 182 | #' @return A data frame of papers needing more reviewers 183 | #' @export 184 | 185 | need_decision <- function(editor) { 186 | # Find papers with AE recommendation as last status 187 | all_articles <- get_assignments(editor, "Submissions") 188 | latest <- get_latest(all_articles) 189 | ae_recommendation <- latest[grep("^AE", latest$status), ] |> 190 | unnest(reviewers) |> 191 | dplyr::group_by(id, ae, status, date) |> 192 | dplyr::summarise(n = dplyr::n()) 193 | 194 | # Find existing reviewers 195 | reviewers <- get_reviewers(editor = editor) 196 | ids <- unique(reviewers$id) 197 | paths <- paste0(get_articles_path(), "/Submissions/", ids) 198 | nreviews <- lapply(paths, function(x) { 199 | completed_reviews(as.article(x)) 200 | }) |> 201 | unlist() 202 | reviewers <- subset(reviewers, reviewers$id %in% ids[nreviews >= 2]) 203 | status <- last_reviewer_status(reviewers$comment) 204 | reviewers <- reviewers[status %in% c("Major", "Minor", "Accept", "Reject"), ] 205 | 206 | if (NROW(reviewers) > 0L) { 207 | reviewers$date <- stringr::str_extract( 208 | reviewers$comment, 209 | "[0-9]*\\-[0-9]*\\-[0-9]*$" 210 | ) |> 211 | as.Date() 212 | reviewers <- reviewers |> 213 | dplyr::group_by(id) |> 214 | dplyr::summarise(date = max(date), n = dplyr::n()) |> 215 | dplyr::arrange(date) 216 | reviewers$status <- "needs decision" 217 | } 218 | output <- as.data.frame(bind_rows(ae_recommendation, reviewers)) 219 | days_taken <- difftime(Sys.Date(), output$date, units = "days") 220 | output$stars <- unlist(lapply(days_taken, function(u) { 221 | str_dup("*", sum(u > deadlines("needs editor"))) 222 | })) 223 | output$ae[is.na(output$ae)] <- "" 224 | if (NROW(output) == 0L) { 225 | return(invisible(NULL)) 226 | } else { 227 | return(output) 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /R/path.R: -------------------------------------------------------------------------------- 1 | #' Set the directory of the articles repository 2 | #' 3 | #' This path is used to locate articles on your file system. If this path is not 4 | #' specified, the path will default to the root of the repository which contains 5 | #' the working directory 6 | #' @param path Articles path 7 | #' @export 8 | set_articles_path <- function(path) { 9 | .articles$path <- normalizePath(path.expand(path)) 10 | } 11 | 12 | #' Get the directory of the articles repository, either as set by 13 | #' set_articles_path() or using git to determine repository root 14 | #' 15 | #' This path is used to point to articles on your file system. 16 | #' @export 17 | get_articles_path <- function() { 18 | dir <- .articles$path 19 | if (!is.null(.articles$path)) { 20 | if (!dir.exists(dir)) { 21 | warning("Articles path set via set_articles_path() does not exits, ignoring.") 22 | dir <- NULL 23 | } 24 | } 25 | 26 | ## not set or not valid, use git 27 | if (is.null(dir)) { 28 | dir <- try(system("git rev-parse --show-toplevel", intern = TRUE), 29 | silent = TRUE 30 | ) 31 | if (inherits(dir, "try-error")) { 32 | abort("Current directory is not a git repository, use set_articles_path() if you don't have wokring git (error: ", dir) 33 | } 34 | } 35 | dir 36 | } 37 | 38 | # Articles settings 39 | .articles <- new.env() 40 | -------------------------------------------------------------------------------- /R/report.R: -------------------------------------------------------------------------------- 1 | #' Generate a status report. 2 | #' 3 | #' This should be run weekly. 4 | #' 5 | #' @param articles list of articles to generate report for. Defaults to 6 | #' all active reports in \file{Submissions/}. 7 | #' @param editor editor to generate report for. Defaults to all editors. 8 | #' @export 9 | report <- function(articles = active_articles(), editor = NULL) { 10 | rpt <- do.call("rbind", lapply(articles, report_line)) 11 | rpt <- rpt[order(rpt$date, rpt$ed), ] 12 | rpt$status <- factor(rpt$status, order_status(rpt$status)) 13 | # Sort by editor, then status, then date 14 | rpt <- rpt[order(-nchar(rpt$stars), rpt$ed, rpt$status, rpt$date), ] 15 | if(!is.null(editor)) { 16 | rpt <- rpt[rpt$ed == editor, ] 17 | } 18 | structure(rpt, class = c("report", "data.frame")) 19 | } 20 | 21 | report_line <- function(x) { 22 | #message(x$path) # to identify culprit when things go wrong 23 | stopifnot(is.article(x)) 24 | 25 | todo <- todo(x) 26 | status <- last_status(x) 27 | last_date <- last_status(x)$date 28 | days_taken <- difftime(Sys.Date(), last_date, "days") 29 | 30 | if(todo == "waiting (author)") { 31 | stars <- 0 32 | } else { 33 | stars <- sum(days_taken > deadlines(todo)) 34 | } 35 | 36 | data.frame( 37 | status = todo, 38 | ed = editor_abbr(x$editor), 39 | id = format(x$id), 40 | title = str_trunc(x$title, 34), 41 | date = last_date, 42 | stars = str_dup("*", stars), 43 | stringsAsFactors = FALSE 44 | ) 45 | } 46 | 47 | last_status <- function(x) { 48 | stopifnot(is.article(x)) 49 | x$status[[length(x$status)]] 50 | } 51 | 52 | has_status <- function(x, status) { 53 | stopifnot(is.article(x)) 54 | status %in% vapply(x$status, `[[`, character(1L), "status") 55 | } 56 | 57 | #' @export 58 | print.report <- function(x, ...) { 59 | cat("BY STATUS:\n") 60 | parts <- split(x, x$status) 61 | # Omit parts with no rows 62 | parts <- parts[lapply(parts, nrow) > 0] 63 | for (nm in names(parts)) { 64 | part <- parts[[nm]] 65 | 66 | str_sub(nm, 1, 1) <- toupper(str_sub(nm, 1, 1)) 67 | cat(str_pad(nm, 60, "right", "-"), "\n") 68 | 69 | # Reorder by stars and dates 70 | part <- part[order(-nchar(part$stars), part$date), ] 71 | out <- capture.output(print.data.frame(part[, -1], 72 | row.names = FALSE, 73 | right = FALSE 74 | )) 75 | cat(paste(out[-1], collapse = "\n"), "\n\n") 76 | } 77 | 78 | if(length(unique(x$ed)) > 1L) { 79 | cat("BY EDITOR:\n") 80 | actionable <- x[x$ed != "" & 81 | !(x$status %in% c("accepted", "online", "complete")), ] 82 | parts <- split(actionable, actionable$ed) 83 | for (nm in names(parts)) { 84 | part <- parts[[nm]] 85 | 86 | str_sub(nm, 1, 1) <- toupper(str_sub(nm, 1, 1)) 87 | cat(str_pad(nm, 60, "right", "-"), "\n") 88 | 89 | out <- capture.output(print.data.frame(part[, c("id", "status", "date", "stars")], 90 | row.names = FALSE, 91 | right = FALSE 92 | )) 93 | cat(paste(out[-1], collapse = "\n"), "\n\n") 94 | } 95 | } 96 | } 97 | 98 | order_status <- function(x) { 99 | x <- unique(x) 100 | eic <- x[grepl("editor-in-chief", x)] 101 | editors <- setdiff(x[grepl("editor", x)], eic) 102 | author <- x[grepl("author", x)] 103 | others <- setdiff(x, c(eic, editors, author)) 104 | c(eic, editors, others, author) 105 | } 106 | 107 | editor_abbr <- function(x) { 108 | if (empty(x)) { 109 | return("") 110 | } 111 | toupper(str_c(str_sub(str_split(x, " ")[[1]], 1, 2), collapse = "")) 112 | } 113 | -------------------------------------------------------------------------------- /R/reviewers-utils.R: -------------------------------------------------------------------------------- 1 | add_reviewer_comment <- function(article, reviewer_id, comment) { 2 | article <- as.article(article) 3 | reviewers <- article$reviewers 4 | reviewer <- reviewers[[reviewer_id]] 5 | old_comment <- reviewer$comment 6 | if (!is.null(old_comment) && nchar(old_comment) > 0) { 7 | old_comment <- stringr::str_remove(old_comment, "\\[|\\]") 8 | comment <- paste0(old_comment, "; ", comment) 9 | } 10 | reviewer$comment <- comment 11 | article$reviewers[[reviewer_id]] <- reviewer 12 | cli::cli_alert_info(comment) 13 | article <- save_article(article) 14 | return(invisible(article)) 15 | } 16 | 17 | check_dup_comment <- function(article, reviewer_id, test_string) { 18 | article <- as.article(article) 19 | comment <- article$reviewers[[reviewer_id]]$comment 20 | check <- stringr::str_detect(comment, pattern = test_string) 21 | if (isTRUE(check)) { 22 | stop(paste("Reviewer", reviewer_id, "has already", test_string), call. = FALSE) 23 | } 24 | return(invisible(NULL)) 25 | } 26 | -------------------------------------------------------------------------------- /R/rfc822.R: -------------------------------------------------------------------------------- 1 | #' Parse a string into a rfc822 address list. 2 | #' 3 | #' EBNF at \url{http://tools.ietf.org/html/rfc2822#section-3.4} 4 | #' 5 | #' @param x string to parse 6 | #' @return a list of \code{\link{address}}es 7 | #' @export 8 | #' @examples 9 | #' parse_address_list(" Alison, Colin") 10 | parse_address_list <- function(x) { 11 | stopifnot(is.character(x), length(x) == 1) 12 | if (empty(x)) { 13 | return(address_list()) 14 | } 15 | 16 | addresses <- str_trim(str_split(x, ",")[[1]]) 17 | address_list(lapply(addresses, parse_address)) 18 | } 19 | 20 | address_list <- function(addresses = list()) { 21 | stopifnot(is.list(addresses)) 22 | structure(addresses, class = "address_list") 23 | } 24 | 25 | #' @method format address_list 26 | #' @export 27 | format.address_list <- function(x, ...) { 28 | addresses <- vapply(x, format, character(1)) 29 | paste(addresses, collapse = ",\n ") 30 | } 31 | 32 | #' @method print address_list 33 | #' @export 34 | print.address_list <- function(x, ...) cat(format(x), "\n") 35 | 36 | #' An S3 class to represent email addresses. 37 | #' 38 | #' @param email Email address of the reviewer 39 | #' @param name Display name, optional 40 | #' @param comment comment, optional 41 | #' @export 42 | #' @examples 43 | #' address("h.wickham@@gmail.com") 44 | #' address("h.wickham@@gmail.com", "Hadley Wickham") 45 | address <- function(email = NULL, name = NULL, comment = NULL) { 46 | if (is.null(email) && is.null(name)) { 47 | stop("Address must have name or email", call. = FALSE) 48 | } 49 | 50 | structure(list(name = name, email = email, comment = comment), class = "address") 51 | } 52 | 53 | #' @export 54 | format.address <- function(x, ...) { 55 | name <- if (!is.null(x$name)) paste('"', x$name, '"', sep = "") 56 | email <- if (!is.null(x$email)) paste("<", x$email, ">", sep = "") 57 | comment <- if (!is.null(x$comment)) paste("[", x$comment, "]", sep = "") 58 | 59 | paste(c(name, email, comment), collapse = " ") 60 | } 61 | 62 | #' @export 63 | print.address <- function(x, ...) cat(format(x), "\n") 64 | 65 | parse_address <- function(x) { 66 | stopifnot(is.character(x), length(x) == 1) 67 | 68 | pieces <- str_match(x, "^\\s*([^<>]*) ?(<.*>)? ?(\\[.*\\])?")[1, ] 69 | 70 | comment <- str_trim(pieces[4]) 71 | comment <- str_replace_all(comment, "\\[|\\]", "") 72 | if (is.na(comment) || str_length(comment) == 0) comment <- NULL 73 | 74 | email <- str_trim(pieces[3]) 75 | email <- str_replace_all(email, "<|>", "") 76 | if (is.na(email) || str_length(email) == 0) email <- NULL 77 | 78 | name <- str_trim(pieces[2]) 79 | name <- str_replace_all(name, fixed('"'), "") 80 | if (is.na(name) || str_length(name) == 0) name <- NULL 81 | 82 | address(email, name, comment) 83 | } 84 | -------------------------------------------------------------------------------- /R/rj.R: -------------------------------------------------------------------------------- 1 | #' R Journal Package 2 | #' 3 | #' @keywords package 4 | #' @name rj 5 | "_PACKAGE" 6 | 7 | #' @import stringr 8 | #' @import rlang 9 | #' @import fs 10 | #' @importFrom utils read.csv 11 | 12 | #' @export 13 | dplyr::`%>%` 14 | -------------------------------------------------------------------------------- /R/status.R: -------------------------------------------------------------------------------- 1 | final_status <- c( 2 | "reject and resubmit", 3 | "published", 4 | "withdrawn", 5 | "rejected" 6 | ) 7 | 8 | #' A list of all valid statuses. 9 | #' 10 | #' @export 11 | #' @examples 12 | #' valid_status 13 | valid_status <- c( 14 | "submitted", 15 | "acknowledged", 16 | "passed initial checks", 17 | "resubmission", 18 | "needs reviewers", 19 | "needs editor", 20 | "updated", 21 | "out for review", 22 | "major revision", 23 | "minor revision", 24 | "revision received", 25 | "accepted", 26 | "copy edited", 27 | "online", 28 | "proofed", 29 | "out for proofing", 30 | "style checked", 31 | "with AE", 32 | "AE: major revision", 33 | "AE: minor revision", 34 | "AE: accept", 35 | "AE: reject", 36 | final_status 37 | ) 38 | 39 | # Status class and methods ----------------------------------------------------- 40 | 41 | #' Create a S3 status object 42 | #' 43 | #' @param status A string description the status. Must be listed in 44 | #' \code{\link{valid_status}} 45 | #' @param date Date, defaults to today. Must be after 2002-01-01 and 46 | #' not in the future. 47 | #' @param comments any additional extra comments 48 | #' @keywords internal 49 | #' @export 50 | #' @examples 51 | #' status("rejected") 52 | #' c(status("rejected"), status("accepted")) 53 | status <- function(status, date = Sys.Date(), comments = "") { 54 | stopifnot(is.Date(date), length(date) == 1) 55 | stopifnot(is.character(status), length(status) == 1) 56 | stopifnot(is.character(comments), length(comments) == 1) 57 | 58 | # Date + 1 provides a buffer for timezones with remote resources. 59 | if (date > (Sys.Date() + 1)) stop("Date must not be in the future") 60 | if (date < as.Date("2002-01-01")) { 61 | stop("Date must not before the R journal was created") 62 | } 63 | 64 | status <- str_trim(status) 65 | if (!(status %in% valid_status)) { 66 | guess <- amatch_status(status) 67 | if (tolower(status) == tolower(guess)) { 68 | status <- guess 69 | } else { 70 | stop( 71 | status, 72 | " is not a known status. ", 73 | "Did you mean ", 74 | amatch_status(status), 75 | "?", 76 | call. = FALSE 77 | ) 78 | } 79 | } 80 | 81 | structure( 82 | list(date = date, status = status, comments = comments), 83 | class = "status" 84 | ) 85 | } 86 | 87 | is.status <- function(x) inherits(x, "status") 88 | 89 | 90 | #' @export 91 | c.status <- c.status_list <- function(..., recursive = FALSE) { 92 | pieces <- list(...) 93 | statuses <- lapply(pieces, function(x) { 94 | if (is.status(x)) { 95 | list(x) 96 | } else if (is.status_list(x)) { 97 | x 98 | } else { 99 | stop("Don't know how to combine with ", class(x)[1]) 100 | } 101 | }) 102 | 103 | status_list(unlist(statuses, recursive = FALSE)) 104 | } 105 | 106 | #' @export 107 | format.status <- function(x, ...) { 108 | paste( 109 | format(x$date), 110 | " ", 111 | x$status, 112 | if (!empty(x$comments)) paste(" [", x$comments, "]", sep = ""), 113 | sep = "" 114 | ) 115 | } 116 | #' @export 117 | print.status <- function(x, ...) cat(format(x), "\n") 118 | 119 | #' @importFrom utils adist 120 | amatch_status <- function(status) { 121 | ldist <- adist( 122 | status, 123 | valid_status, 124 | ignore.case = TRUE, 125 | partial = FALSE, 126 | costs = c(ins = 0.5, sub = 1, del = 2) 127 | )[1, ] 128 | valid_status[which.min(ldist)] 129 | } 130 | 131 | is.date <- function(x) { 132 | parsed <- strptime(x, "%Y-%m-%d") 133 | !is.na(parsed) && format(parsed) == x 134 | } 135 | 136 | is.Date <- function(x) inherits(x, "Date") 137 | 138 | 139 | # Reporting -------------------------------------------------------------------- 140 | 141 | todo <- function(x) { 142 | stopifnot(is.article(x)) 143 | 144 | status <- last_status(x)$status 145 | status_date <- last_status(x)$date 146 | if ( 147 | status %in% 148 | c( 149 | "resubmission", 150 | "reject and resubmit", 151 | "major revision", 152 | "minor revision", 153 | "accepted", 154 | "copy edited" 155 | ) 156 | ) { 157 | "waiting (author)" 158 | } else if ( 159 | empty(x$editor) | 160 | status == "submitted" | 161 | (status == "revision received" & status_date > "2025-01-01") 162 | ) { 163 | # Needs an editor, or needs an acknowledgement. 164 | # Ignore unacknowledged revisions before 2025 165 | "waiting (editor-in-chief)" 166 | } else if (status == "with AE") { 167 | "waiting (AE)" 168 | } else if (empty(x$reviewers)) { 169 | "waiting (editor)" 170 | } else if (status == "out for review" & completed_reviews(x) >= 2) { 171 | "waiting (editor)" 172 | } else { 173 | switch( 174 | status, 175 | "out for review" = "waiting (reviewers)", 176 | "updated" = "waiting (editor)", 177 | "published" = "needs removal (editor)", 178 | "withdrawn" = "needs removal (editor)", 179 | "rejected" = "needs removal (editor)", 180 | "revision received" = "waiting (editor)", 181 | "AE: major revision" = "waiting (editor)", 182 | "AE: minor revision" = "waiting (editor)", 183 | "AE: accept" = "waiting (editor)", 184 | "AE: reject" = "waiting (editor)", 185 | "style checked" = "needs online (editor-in-chief)", 186 | "online" = "needs copy-editing (editor)", 187 | "proofed" = "ready for publication (editor-in-chief)", 188 | "acknowledged" = "needs reviewers (editor)", 189 | stop("Unknown status: ", status) 190 | ) 191 | } 192 | } 193 | 194 | # Takes a summary status as input, and returns number of days before it's due 195 | deadlines <- function(sstatus) { 196 | if (sstatus %in% final_status) { 197 | return(rep(Inf, 3)) 198 | } 199 | 200 | # > 1st = *; > 2nd = **; > 3rd = *** 201 | special <- list( 202 | "needs editor" = c(7L, 14L, 28L), 203 | "needs reviewers" = c(7L, 14L, 28L), 204 | "submitted" = c(3L, 7L, 28L), 205 | "proofed" = c(7L, 14L, 28L), 206 | "major revision" = c(60L, 90L, 180L), 207 | "waiting (AE)" = c(60L, 90L, 150L), 208 | "waiting (editor)" = c(7L, 14L, 28L), 209 | "waiting (editor-in-chief)" = c(7L, 14L, 21L) 210 | ) 211 | if (sstatus %in% names(special)) { 212 | special[[sstatus]] 213 | } else { 214 | c(4L, 6L, 26) * 7L 215 | } 216 | } 217 | 218 | # status_list class ------------------------------------------------------------ 219 | 220 | status_list <- function(x = list()) { 221 | structure(x, class = "status_list") 222 | } 223 | 224 | #' @export 225 | format.status_list <- function(x, ...) { 226 | statuses <- lapply(x, format) 227 | paste(statuses, collapse = ",\n ") 228 | } 229 | 230 | #' @export 231 | print.status_list <- function(x, ...) { 232 | statuses <- lapply(x, format) 233 | cat(paste(statuses, collapse = "\n")) 234 | } 235 | is.status_list <- function(x) inherits(x, "status_list") 236 | 237 | # Parsing ---------------------------------------------------------------------- 238 | 239 | parse_status_list <- function(x) { 240 | stopifnot(is.character(x), length(x) == 1) 241 | x <- trimws(x) 242 | if (empty(x)) { 243 | return(status_list()) 244 | } 245 | 246 | statuses <- trimws(strsplit(x, ",[ \t\r]*(\n|$)")[[1]]) 247 | statuses <- statuses[statuses != ""] 248 | 249 | status_list(lapply(statuses, parse_status)) 250 | } 251 | 252 | parse_status <- function(x) { 253 | x <- stringr::str_trim(x) 254 | 255 | re <- "^(\\d{4}-\\d{2}-\\d{2}) ([^\\[]*)(?: \\[([^\\[]+)\\])?$" 256 | if (!stringr::str_detect(x, re)) { 257 | # NM added line 258 | cat("bad status:", x, "\n") 259 | stop( 260 | "Status must have form 'yyyy-mm-dd status [optional comments]'", 261 | call. = FALSE 262 | ) 263 | } 264 | 265 | pieces <- stringr::str_match(x, re)[1, ] 266 | 267 | date <- pieces[2] 268 | if (!is.date(date)) stop("Date must be a valid date") 269 | date <- as.Date(date) 270 | 271 | status <- pieces[3] 272 | comments <- if (is.na(pieces[4])) "" else pieces[4] 273 | 274 | status(status = status, date = date, comments = comments) 275 | } 276 | 277 | #' @export 278 | as.data.frame.status_list <- function(x, ...) { 279 | message("DF") 280 | ml <- vector(mode = "list", length = length(x)) 281 | for (i in seq(along = ml)) ml[[i]] <- as.data.frame(unclass(x[[i]])) 282 | do.call("rbind", ml) 283 | } 284 | -------------------------------------------------------------------------------- /R/summary_plots.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | article_status_data <- function(years=NULL, save=TRUE){ 5 | if (is.null(years)){ 6 | this_year <- as.numeric(format(Sys.Date(), "%Y")) 7 | years <- this_year - (1:4) 8 | } 9 | sel_years <- as.character(years) 10 | 11 | # similar to tabulate_articles, excepted added year, as some earlier articles have malformed DESCRIPTION files 12 | tabulate_articles_year<- 13 | function (dirs = c("Accepted", "Submissions"), id_year=NULL) { 14 | ids <- article_ids(dirs) 15 | if (is.character(id_year)) 16 | ids <- ids[substr(ids,1,4) %in% id_year] 17 | purrr::map_dfr(ids, tabulate_single) 18 | } 19 | 20 | 21 | subs <- tabulate_articles_year("Submissions", sel_years ) |> 22 | select(id, status) |> 23 | unnest(status) |> 24 | group_by(id) |> 25 | dplyr::slice_tail() |> 26 | mutate(current="in progress") 27 | 28 | rej <- tabulate_articles_year("Rejected",sel_years) |> 29 | select(id, status) |> 30 | unnest(status) |> 31 | group_by(id) |> 32 | dplyr::slice_tail() |> 33 | mutate(current="rejected") 34 | 35 | acc <- tabulate_articles_year(c( "Accepted", "Proofs"),sel_years )|> 36 | select(id, status) |> 37 | unnest(status) |> 38 | group_by(id) |> 39 | dplyr::slice_tail() |> 40 | mutate(current="accepted/published") 41 | 42 | article_status_data <- bind_rows(acc, subs, rej) |> 43 | mutate(year = substr(id,1,4)) |> ungroup() 44 | 45 | if (save) { 46 | fn <- file.path(normalizePath("../rjournal.github.io/resources", mustWork = TRUE), "article_status_data.Rdata") 47 | save(article_status_data,file=fn) 48 | print(paste("Saving article status data to", fn)) 49 | } 50 | 51 | article_status_data 52 | } 53 | 54 | 55 | 56 | #' Generates a status plot for articles submitted in the last few years. 57 | #' 58 | #' @param years years considered. A vector of years, or defaults to last four years. 59 | #' @param save Defaults to TRUE. The plot is saved in the rjournal.github.io/resources folder. 60 | #' 61 | #' @return a ggplot, one bar per year (taken from article id) 62 | #' @export 63 | #' 64 | #' @examples 65 | #' \dontrun{ 66 | #' article_status_plot() 67 | #' } 68 | article_status_plot <- function(years=NULL, save=TRUE){ 69 | 70 | allart <- article_status_data(years) 71 | 72 | g <- ggplot2::ggplot(allart, ggplot2::aes(x=year, fill=.data$current))+ 73 | ggplot2::geom_bar() + ggplot2::xlab("Submission year") + 74 | ggplot2::scale_fill_manual(values=c("#31a354", "#9ecae1", "#fee8c8"))+ 75 | ggplot2::theme_bw() 76 | if (save) { 77 | fn <- file.path(normalizePath("../rjournal.github.io/resources", mustWork = TRUE), "article_status_plot.png") 78 | ggplot2::ggsave(fn,g,device="png",width = 5, height = 3) 79 | print(paste("Save to", fn)) 80 | } 81 | g 82 | 83 | } 84 | 85 | 86 | time_to_accept_data <- function(years=NULL, save=TRUE){ 87 | if (is.null(years)){ 88 | this_year <- as.numeric(format(Sys.Date(), "%Y")) 89 | years <- this_year - (1:4) 90 | } 91 | sel_years <- as.character(years) 92 | 93 | proof_folders <-dir(file.path(get_articles_path(),"Proofs")) 94 | 95 | proof_folders <- 96 | file.path("Proofs", 97 | proof_folders[substr(proof_folders,1,4) %in% sel_years]) 98 | 99 | # slug is not pub_year, always, so need to get pub_year from folder 100 | 101 | published <- 102 | map(proof_folders, tabulate_articles) |> 103 | bind_rows(.id ="index") |> 104 | mutate(pub_year = substr(proof_folders[as.numeric(.data$index)],8,11)) 105 | 106 | 107 | # some dates are messed up, or not present, so this version of code accounts for that 108 | # also no submitted line, multiple accepted lines 109 | 110 | submitted_info <- 111 | published |> 112 | select(id, status, .data$pub_year) |> 113 | unnest(status) |> 114 | filter(status =="submitted") |> 115 | group_by(id) |> 116 | dplyr::slice_min(date) 117 | 118 | accepted_info <- 119 | published |> 120 | select(id, status, .data$pub_year) |> 121 | unnest(status) |> 122 | filter(status =="accepted") |> 123 | group_by(id) |> 124 | dplyr::slice_max(date, with_ties=FALSE) 125 | 126 | time_to_accept_data <- 127 | dplyr::full_join(submitted_info, accepted_info, by="id") |> 128 | mutate(pub_year = max(.data$pub_year.x, .data$pub_year.y, na.rm=T), 129 | days = dplyr::case_when( 130 | is.na(date.x) | is.na(date.y) ~ NA, 131 | date.y >= date.x ~ as.numeric(date.y- date.x), 132 | TRUE ~ NA 133 | )) 134 | if (save) { 135 | fn <- file.path(normalizePath("../rjournal.github.io/resources", mustWork = TRUE), "time_to_accept_data.Rdata") 136 | save(time_to_accept_data,file=fn) 137 | print(paste("Saving time to accept data to", fn)) 138 | } 139 | 140 | time_to_accept_data 141 | } 142 | 143 | 144 | #' Generates a plot of acceptance times for articles published in the last few years. 145 | #' 146 | #' @param years years considered. A vector of years, or defaults to last four years. 147 | #' @param save Defaults to TRUE. The plot is saved in the rjournal.github.io/resources folder. 148 | #' 149 | #' @return a ggplot, one boxplot per publication year 150 | #' @export 151 | #' 152 | #' @examples 153 | #' \dontrun{ 154 | #' time_to_accept_plot() 155 | #' } 156 | #' 157 | time_to_accept_plot <- function(years=NULL, save=TRUE){ 158 | accepted_all <- time_to_accept_data(years) 159 | 160 | g <- ggplot2::ggplot(accepted_all, ggplot2::aes(x=.data$pub_year, y=.data$days))+ 161 | ggplot2::geom_boxplot(color="navy", fill="lightblue1")+ ggplot2::xlab("Year of publication")+ 162 | ggplot2::ylab("Submission to acceptance (days)") + 163 | ggplot2::theme_bw() 164 | 165 | if (save) { 166 | fn <- file.path(normalizePath("../rjournal.github.io/resources", mustWork = TRUE), "time_to_accept_plot.png") 167 | ggplot2::ggsave(fn,g,device="png",width = 4, height = 3) 168 | print(paste("Save to", fn)) 169 | } 170 | g 171 | } 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /R/sysdata.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjournal/rj/400a26d94e8a8a7378aee3edf0189ba16f572da6/R/sysdata.rda -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' @importFrom stats setNames na.omit 2 | #' @importFrom utils URLencode as.person browseURL 3 | #' @importFrom utils capture.output type.convert zip 4 | #' @importFrom utils edit glob2rx str unzip 5 | #' @importFrom cli cli_alert_info cli_h1 cli_alert_danger 6 | 7 | empty <- function(x) UseMethod("empty") 8 | #' @method empty character 9 | #' @export 10 | empty.character <- function(x) str_length(x) == 0 11 | #' @method empty address_list 12 | #' @export 13 | empty.address_list <- function(x) length(x) == 0 14 | #' @method empty NULL 15 | #' @export 16 | empty.NULL <- function(x) TRUE 17 | 18 | is.dir <- function(x) file.info(x)$isdir 19 | 20 | in_dir <- function(path, code) { 21 | old <- setwd(path) 22 | on.exit(setwd(old)) 23 | code 24 | } 25 | 26 | "%||%" <- function(a, b) if (empty(a)) b else a 27 | 28 | "%NA%" <- function(a, b) ifelse(is.na(a), b, a) 29 | 30 | issue_month <- function(issue) { 31 | issue_regex <- "^(\\d{4})-(\\d{1})" 32 | issue_year <- as.integer(sub(issue_regex, "\\1", issue)) 33 | issue_num <- as.integer(sub(issue_regex, "\\2", issue)) 34 | 35 | issue_months <- if(issue_year < 2022) { 36 | c("June", "December") 37 | } else { 38 | c("March", "June", "September", "December") 39 | } 40 | issue_months[issue_num] 41 | } 42 | 43 | partition_rmd <- function(file) { 44 | input <- xfun::read_utf8(file) 45 | front_matter_delimiters <- grep("^(---|\\.\\.\\.)\\s*$", input) 46 | 47 | list( 48 | front_matter = yaml::yaml.load(input[(front_matter_delimiters[1L]+1L):(front_matter_delimiters[2L]-1)]), 49 | body = input[(front_matter_delimiters[2L]+1L):length(input)] 50 | ) 51 | } 52 | 53 | 54 | update_front_matter <- function(yml, file) { 55 | input <- xfun::read_utf8(file) 56 | front_matter_delimiters <- grep("^(---|\\.\\.\\.)\\s*$", input) 57 | 58 | xfun::write_utf8( 59 | c( 60 | "---", 61 | yaml::as.yaml(yml), 62 | "---", 63 | "", 64 | input[(front_matter_delimiters[2]+1):length(input)] 65 | ), 66 | file 67 | ) 68 | } 69 | 70 | format_non_null <- function(x) if(is.null(x)) NULL else format(x) 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rj 2 | 3 | This package is for the editorial team to conduct operations for the R Journal. 4 | 5 | ## Installation 6 | 7 | Install the package from the rjournal github site: 8 | 9 | `remotes::install_github("rjournal/rj")` 10 | 11 | -------------------------------------------------------------------------------- /README.nm: -------------------------------------------------------------------------------- 1 | 2 | Various functions to facilitate R Journal administration. 3 | 4 | See comments in the source files for usage. 5 | 6 | Needed environment variables: 7 | 8 | RJ_NAME environment variable, your name 9 | RJNM_DIR environment variable, full path of rj/nm 10 | 11 | Global variables (for submissions-oriented functions): 12 | 13 | desFiles: 14 | 15 | list created by getAll(); gives overview of all current 16 | submissions, one elememt per submission; e.g. subs[['2019-47']] 17 | 18 | subs: 19 | 20 | data frame created by getAll(); gives overview of all current 21 | submissions, one row per submission; e.g. subs['2019-47',] 22 | 23 | Utils.R: 24 | 25 | getAll(): reads the Submissions directory, and returns an R list, one 26 | element 'des' per submission, basically summarizing the DESCRIPTION 27 | file; list is indexed by manuscript number, e.g. subs['2018-52',] 28 | getTitle(des): extracts title from 'des' 29 | getMSnumByAut(aut): finds the manun number by author e-mail addres 30 | (full or unique partial) 31 | getMSnumByTitle(tleinds the manun number by title (full or unique partial) 32 | getDESbyAut(aut): returns ms number(s) for e-mail address of 1st author 33 | getAutInfo(des): extracts author info 34 | getAutAddr(des): extracts author e-mail address 35 | getEd(des): extracts editor name 36 | setEd(des): sets editor name 37 | getReviewStatus(des) extracts review status 38 | autAdd(aut) get lead author's e-mail address from aut name 39 | pullRepo(): git pull origin, and call getAll() 40 | makeSysCmd(...): aids in setting up quotes commands for system() 41 | pushToGitHub(fileList,commitComment,op,mvdest): 42 | if op is 'add', pushes the files to GitHub, or if 'mv' does mv of 43 | fileList to mv dest; then comment for 'commit -m' 44 | ghPush(): does the actual pushing, with a loop to deal with mistyped 45 | passwords 46 | editPush(fname,commitComment): edit the given file, then push to GitHub 47 | sendLetter(msNum,surname,addr,subject,template,attaches): send letter 48 | to author, e.g. conditional acceptance 49 | mailIt(addr,subject,attaches,ltr,mailer='muttMailer'): e-mail letter 50 | getLocalDirName(): get last part of path, e.g. z from /x/y/z 51 | 52 | ExtractPreambles.R: 53 | 54 | getAllPreambles(): extracts all \usepackage etc. from authors' 55 | RJwrapper.tex, to insert into RJ-yearid.tex for building an issue 56 | ACK.R: 57 | 58 | ack(msNum): sends acknowledgement of receipt of manuscript 59 | 60 | Reject.R: 61 | 62 | reject(msNum): performs all needed operations -- updates the 63 | DESCRIPTION file, git-moves the directory to Rejected; 64 | pushes to GitHub; first displays to the editor the 65 | DESCRIPTION file, so the editor can double-check that 66 | this is the right one; editor must separately send letter to 67 | author 68 | 69 | ConditAccept.R: 70 | 71 | conditAcc(): like reject() above, plus sending letter to author, 72 | including the review files 73 | 74 | Accept.R: 75 | 76 | accept(): performs all needed operations -- updates the 77 | DESCRIPTION file, git-moves the directory to Accepted; sends 78 | letter to the author; pushes new author files, DESCRIPTION etc. 79 | to GitHub; first displays to the 80 | editor the DESCRIPTION file, so the editor can double-check that 81 | this is the right one 82 | 83 | CopyEdit.R: 84 | 85 | copyEdit(): sendis letter to author, with the final PDF, asking for 86 | confirmation 87 | 88 | CheckNewSubmit.R: 89 | 90 | various checks to make sure files OK for review 91 | 92 | CheckDupBib.R: 93 | 94 | run before using rj:::build_issue() 95 | 96 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://rjournal.github.io/rj/ 2 | 3 | template: 4 | bootstrap: 5 5 | theme: tango 6 | bootswatch: cosmo 7 | bslib: 8 | base_font: {google: "Fira Sans"} 9 | heading_font: {google: "Fira Sans"} 10 | primary: "#234460" 11 | link-color: "#234460" 12 | 13 | reference: 14 | - title: Primarily AE article management functions 15 | desc: Functions needed in the AE workflow 16 | contents: 17 | - match_keywords 18 | - add_reviewer 19 | - agree_reviewer 20 | - decline_reviewer 21 | - abandon_reviewer 22 | - get_reviewers 23 | - late_reviewers 24 | - need_reviewers 25 | - need_decision 26 | - add_review 27 | - reviewer_status 28 | - update_status 29 | - title: Primarily Editor article management functions 30 | desc: Functions to assist the Editors handle submissions 31 | contents: 32 | - ae_workload 33 | - add_ae 34 | - update_status 35 | - accept 36 | - reject 37 | - withdraw 38 | - report 39 | - summarise_articles 40 | - late_aes 41 | - reviewer_status 42 | - reviewer_summary 43 | - title: Primarily Editor-in-Chief functions 44 | desc: Functions to assist the EiC extract new submissions, build the article folder 45 | contents: 46 | - get_submissions 47 | - assignments 48 | - acknowledge_submission 49 | - acknowledge_revision 50 | - online_metadata 51 | - active_articles 52 | - tabulate_articles 53 | - get_accepted_but_not_online 54 | - author_emails 55 | - title: Utilities 56 | desc: Vectors of valid status 57 | contents: 58 | - valid_status 59 | - valid_reviewer_status 60 | - title: Build an issue 61 | contents: 62 | - make_proof 63 | - publish_article 64 | - publish_issue 65 | - publish_news 66 | - title: Misc 67 | contents: 68 | - AEs 69 | - actionable_articles 70 | - address 71 | - article 72 | - build_latex 73 | - corr_author 74 | - email_template 75 | - email_text 76 | - filter_status 77 | - future_ids 78 | - get_article_keywords 79 | - get_articles_path 80 | - get_md_from_pdf 81 | - get_reviewer_keywords 82 | - git_user 83 | - invite_reviewers 84 | - new_id 85 | - online_metadata_for_article 86 | - parse_address_list 87 | - get_accepted_articles 88 | - rj 89 | - set_articles_path 90 | - article_status_plot 91 | - time_to_accept_plot 92 | 93 | articles: 94 | - title: Articles 95 | navbar: ~ 96 | contents: 97 | - roles_and_responsibilities 98 | - conflict_of_interest 99 | - editor_in_chief_guide 100 | - editors_guide 101 | - associate_editors_guide 102 | - issue_build_guide 103 | - website_build_guide 104 | -------------------------------------------------------------------------------- /data-raw/keywords_list_concat.R: -------------------------------------------------------------------------------- 1 | reviewer <- tibble::tribble( 2 | ~reviewer, ~details, 3 | "Bayesian", "Bayesian Inference", 4 | "ChemPhys", "Chemometrics and Computational Physics", 5 | "ClinicalTrials", "Clinical Trial Design Monitoring and Analysis", 6 | "Cluster", "Cluster Analysis & Finite Mixture Models", 7 | "Databases", "Databases with R", 8 | "DifferentialEquations", "Differential Equations", 9 | "Distributions", "Probability Distributions", 10 | "Econometrics", "Econometrics", 11 | "Environmetrics", "Analysis of Ecological and Environmental Data", 12 | "ExperimentalDesign", "Design of Experiments (DoE) & Analysis of Experimental Data", 13 | "ExtremeValue", "Extreme Value Analysis", 14 | "Finance", "Empirical Finance", 15 | "FunctionalData", "Functional Data Analysis", 16 | "Genetics", "Statistical Genetics", 17 | "Graphics", "Graphic Displays & Dynamic Graphics & Graphic Devices & Visualization", 18 | "Graphics", "Graphic Displays & Dynamic Graphics & Graphic Devices & Visualization", 19 | "HighPerformanceComputing", "High-Performance and Parallel Computing with R", 20 | "Hydrology", "Hydrological Data and Modeling", 21 | "MachineLearning", "Machine Learning & Statistical Learning", 22 | "MedicalImaging", "Medical Image Analysis", 23 | "MetaAnalysis", "Meta-Analysis", 24 | "MissingData", "Missing Data", 25 | "ModelDeployment", "Model Deployment with R", 26 | "Multivariate", "Multivariate Statistics", 27 | "NaturalLanguageProcessing", "Natural Language Processing", 28 | "NumericalMathematics", "Numerical Mathematics", 29 | "OfficialStatistics", "Official Statistics & Survey Methodology", 30 | "Optimization", "Optimization and Mathematical Programming", 31 | "Pharmacokinetics", "Analysis of Pharmacokinetic Data", 32 | "Phylogenetics", "Phylogenetics, Especially Comparative Methods", 33 | "Psychometrics", "Psychometric Models and Methods", 34 | "ReproducibleResearch", "Reproducible Research", 35 | "Robust", "Robust Statistical Methods", 36 | "SocialSciences", "Statistics for the Social Sciences", 37 | "Spatial", "Analysis of Spatial Data", 38 | "SpatioTemporal", "Handling and Analyzing Spatio-Temporal Data", 39 | "Survival", "Survival Analysis", 40 | "TeachingStatistics", "Teaching Statistics", 41 | "TimeSeries", "Time Series Analysis", 42 | "Tracking", "Processing and Analysis of Tracking Data", 43 | "WebTechnologies", "Web Technologies and Services", 44 | "gR", "gRaphical Models in R" 45 | ) %>% 46 | mutate(reviewer = gsub("([A-Z])", " \\1", reviewer), 47 | reviewer = stringr::str_trim(reviewer)) 48 | 49 | submission <- 50 | tibble::tribble( 51 | ~submission, 52 | "Bayesian Inference", 53 | "Chemometrics and Computational Physics", 54 | "Clinical Trial Design Monitoring and Analysis", 55 | "Cluster Analysis & Finite Mixture Models", 56 | "Databases with R", 57 | "Differential Equations", 58 | "Distributions", 59 | "Econometrics", 60 | "Ecological and Environmental analysis", 61 | "Design and Analysis of Experiments (DoE)", 62 | "Extreme Value Analysis", 63 | "Empirical Finance", 64 | "Functional Data Analysis", 65 | "Statistical Genetics", 66 | "Graphical Models", 67 | "Graphics and Visualisation", 68 | "High-Performance Computing", 69 | "Hydrological Data and Modeling", 70 | "Machine Learning & Statistical Learning", 71 | "Medical Image Analysis", 72 | "Meta-Analysis", 73 | "Missing Data", 74 | "Model Deployment", 75 | "Multivariate Statistics", 76 | "Natural Language Processing", 77 | "Numerical Mathematics", 78 | "Official Statistics & Survey Methodology", 79 | "Optimization and Mathematical Programming", 80 | "Pharmacokinetic Analysis", 81 | "Phylogenetics", 82 | "Psychometric Models and Methods", 83 | "Reproducible Research", 84 | "Robust Statistical Methods", 85 | "Social Sciences", 86 | "Spatial Analysis", 87 | "Spatio-Temporal Analysis", 88 | "Survival Analysis", 89 | "Teaching Statistics", 90 | "Time Series Analysis", 91 | "Tracking Data", 92 | "Web Technologies and Services", 93 | "gRaphical Models in R", 94 | ) 95 | 96 | keywords_list <- cbind( 97 | reviewer, 98 | submission 99 | ) 100 | 101 | 102 | usethis::use_data(keywords_list, 103 | overwrite = TRUE, 104 | internal = TRUE) 105 | -------------------------------------------------------------------------------- /inst/RJwrapper_template.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{report} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage[T1]{fontenc} 4 | \usepackage{RJournal} 5 | \usepackage{amsmath,amssymb,array} 6 | \usepackage{booktabs} 7 | 8 | %% load any required packages, macros etc. FOLLOWING this line 9 | 10 | \begin{document} 11 | 12 | %% do not edit, for illustration only 13 | \sectionhead{Contributed research article} 14 | \volume{XX} 15 | \volnumber{YY} 16 | \year{20ZZ} 17 | \month{AAAA} 18 | 19 | %% replace RJtemplate with your article 20 | \begin{article} 21 | {{article_input}} 22 | \end{article} 23 | 24 | \end{document} 25 | -------------------------------------------------------------------------------- /inst/associate-editors.csv: -------------------------------------------------------------------------------- 1 | name,initials,github_handle,email,start_year,end_year,keywords,github,affiliation,comment 2 | Earo Wang,EW,earowang,earo.wang@gmail.com,2020,2022,"Graphics, Reproducible Research, Time Series, Web Technologies",ae-articles-ew,U. Auckland,Finished December 2022 3 | Xiaoyue Cheng,XC,chxy,xycheng@unomaha.edu,2020,2025,"Graphics, Machine Learning",ae-articles-xcz,U.Nebraska - Omaha, 4 | Emily Zabor,EZ,zabore,zabore2@ccf.org,2020,2024,"Clinical Trials, Reproducible Research, Survival",ae-articles-ez,Taussig Cancer Institute, 5 | Susan Vanderplas,SV,srvanderplas,susan.vanderplas@unl.edu,2020,2025,"Graphics, Machine Learning, Reproducible Research, Web Technologies, Forensics",ae-articles-sv,U. Nebraska - Lincoln, 6 | Rafael de Andrade Moral,RAM,rafamoral,rafael.deandrademoral@mu.ie,2020,2025,"Environmetrics, Experimental Design, Machine Learning, Multivariate, Survival",ae-articles-ram,Maynooth U., 7 | Beth Atkinson,BA,bethatkinson,atkinson@mayo.edu,2020,2022,"Genetics, Graphics, Machine Learning, Reproducible Research, Survival",ae-articles-ba,Mayo Clinic,Finished December 2022 8 | Nicholas Tierney,NT,njtierney,nicholas.tierney@gmail.com,2021,2024,"Bayesian, Graphics, Missing Data, Reproducible Research, Teaching Statistics",ae-articles-nt,Telethon Kids, 9 | Isabella Gollini,IG,igollini,isabella.gollini@ucd.ie,2021,2025,"Bayesian, Cluster, Machine Learning, Multivariate, Social Sciences, Graphical Models in R",ae-articles-ig,UC Dublin, 10 | Rasmus Baath,RB,rasmusab,rasmus.baath@gmail.com,2021,2025,"Bayesian,Databases,Distributions,Machine Learning,Meta Analysis,Missing Data,Psychometrics,Reproducible Research,Social Sciences,Teaching Statistics,Time Series,Web Technologies",ae-articles-rb,Lund U., 11 | Elizabeth Sweeney,ES,emsweene,elizabeth.sweeney@pennmedicine.upenn.edu,2021,2023,"Medical Imaging, Teaching Statistics",ae-articles-es,Cornell U.,Finished June 2022 12 | Louis Aslett,LA,louisaslett,louis.aslett@durham.ac.uk,2021,2023,"Bayesian, Distributions, High Performance Computing, Machine Learning, Multivariate, Numerical Mathematics, Survival, Teaching Statistics, Web Technologies, Privacy, Reliability Theory, Computational Stats (MCMC/ABC/etc)",ae-articles-la,Durham University, 13 | Katarina Domijan,KD,domijan,katarina.domijan@mu.ie,2021,2025,"Bayesian, Cluster, Graphics, Machine Learning, Multivariate, Teaching Statistics",ae-articles-kd,Maynooth University, 14 | Adam Loy,AL,aloy,aloy@carleton.edu,2021,2025,"Bayesian, Distributions, Graphics, Reproducible Research, Social Sciences, Teaching Statistics",ae-articles-al,Carleton University, 15 | Przemek Biecek,PB,pbiecek,przemyslaw.biecek@gmail.com,2021,2023,"Graphics, Machine Learning, Model Deployment, Reproducible Research, Teaching Statistics",ae-articles-pb,University of Warsaw , Finished Feb 2024 16 | Priyanga Dilini Talagala,DT,pridiltal,priyangad@uom.lk,2021,2023,"Extreme Value, Time Series, Outlier Detection",ae-articles-dt,University of Moratuwa, 17 | Kieran Healy,KH,kjhealy,kjhealy@gmail.com,2021,2022,"Social Sciences, Spatial, Spatio Temporal, Teaching Statistics, transportation",ae-articles-kh,Duke University,Finished December 2022 18 | Mine Cetinkaya-Rundel,MC,mine-cetinkaya-rundel,mc301@duke.edu,2022,2027,"Graphics, Reproducible Research, Teaching Statistics",ae-articles-mc,Duke University, 19 | Chris Brunsdon,CB,chrisbrunsdon,Christopher.Brunsdon@mu.ie,2022,2024,"Cluster, Graphics, Multivariate, Official Statistics, Reproducible Research, Spatial, Spatio Temporal",ae-articles-cb,Maynooth University, 20 | Simone Blomberg,SB,simone66b,s.blomberg1@uq.edu.au,2022,2023,"Bayesian, Environmetrics, Missing Data, Phylogenetics, Teaching Statistics, stochastic differential equations",ae-articles-sb,University of Queensland,Finished April 2023 21 | Kevin Burke,KB,kevinburke15,kevin.burke@ul.ie,2022,2027,"Bayesian, Multivariate, Survival",ae-articles-kb,University of Limerick, 22 | Michael Kane,MK,kaneplusplus,kaneplusplus@protonmail.com,2022,2027,"Bayesian,Clinical Trials,Cluster,Databases,High Performance Computing,Machine Learning,Medical Imaging,Numerical Mathematics,Optimization,Reproducible Research,Web Technologies,Graphical Models in R",ae-artickes-mk,Yale University, 23 | Vincent Arel-Bundock,VA,vincentarelbundock,vincent.arel-bundock@umontreal.ca,2023,2025,"Social Sciences, Econometrics",ae-articles-va,Université de Montréal, 24 | Ursula Laa,UL,uschiLaa,ursula.laa@boku.ac.at,2023,2025,"Cluster, Graphics, Machine Learning, Physics",ae-articles-ul,BOKU Vienna, 25 | Yanfei Kang,YK,ykang,yanfeikang@buaa.edu.cn,2023,2025,"Cluster, Distributions, High Performance Computing, Machine Learning, Natural Language Processing, Optimization, Reproducible Research, Teaching Statistics, Time Series",ae-articles-yk,Beihang University, 26 | Lucy D'Agostino McGowan,LM,lucymcgowan,mcgowald@wfu.edu,2023,2025,"Clinical Trials, Cluster, Databases, Experimental Design, Graphics, Machine Learning, Missing Data, Phylogenetics, Reproducible Research, Survival, Teaching Statistics, Graphical Models in R",ae-articles-lm,Wake Forest University, 27 | Jouni Helske,JH,helske,jouni.helske@utu.fi,2024,2026,"Bayesian, Cluster, Time Series, Spatio Temporal, Social Sciences",ae-articles-jh,University of Turku, 28 | Christoph Sax,CS,christophsax,christoph.sax@gmail.com,2024,2026,"Time Series, Econometrics, Shiny, tidyverse",ae-articles-cs,"cynkra LLC, University of Basel", 29 | Thomas Fung,TF,thomas-fung,thomas.fung.dr@gmail.com,2024,2026,"Time Series, Reproducible Research, Teaching Statistics, GLM, Distributions, Nonparametric method",ae-articles-tf,Macquarie University, 30 | Thiyanga Talagala,TT,thiyangt,ttalagala@sjp.ac.lk,2024,2026,"Time Series, Machine Learning Interpretability, Applied Statistics, Meta-Learning",ae-articles-tt,University of Sri Jayewardenepura, 31 | Xiaoqian Wang,XW,xqnwang,xiaoqian.wang@amss.ac.cn,2024,2026,"Distributional Computing, Time Series, Optimization, Statistical Modeling",ae-articles-xw,Monash University, 32 | Ivan Svetunkov,IS,config-i1,ivan@svetunkov.ru,2024,2024,"Time Series",ae-articles-is,Lancaster University, 33 | Wenjie Wang,WW,wenjie2wang,wang@wwenjie.org,2024,2026,"Databases, Clinical Trials, Machine Learning, Survival",ae-articles-ww,Eli Lilly and Company, 34 | Matthias Templ,MT,matthias-da,matthias.templ@fhnw.ch,2024,2026,"Compositional Data Analysis, Imputation/Missing Values, Anonymization/Synthetic Data, Survey Statistics",ae-articles-mt,University of Applied Sciences Northwestern Switzerland, 35 | Romain Lesur,RL,RLesur,romain.lesur@gmail.com,2024,2024,"Reproducible Research",ae-articles-rl,INSEE, 36 | Tomasz Woźniak,TW,donotdespair,twozniak@unimelb.edu.au,2025,2027,"Bayesian Inference, Cluster Analysis & Finite Mixture Models, Econometrics, Multivariate Statistics, Reproducible Research, Teaching Statistics, Time Series Analysis",ae-articles-tw,University of Melbourne,Starting March 2025 37 | Alexander Kowarik,AK,alexkowa,alexander.kowarik@statistik.gv.at,2025,2027,"Missing Data, Official Statistics & Survey Methodology, Reproducible Research, Robust Statistical Methods, Time Series Analysis, Web Technologies and Services",ae-articles-tk,Statistics Austria,Starting March 2025 38 | Thomas Nagler,TN,tnagler,mail@tnagler.com,2025,2027,"Econometrics, Extreme Value Analysis, Functional Data Analysis, Graphical Models, High-Performance Computing, Machine Learning & Statistical Learning, Multivariate Statistics, Numerical Mathematics, Time Series Analysis",ae-articles-tn,LMU Munich,Starting March 2025 39 | Robin Lovelace,RLL,robinlovelace,r.lovelace@leeds.ac.uk,2025,2027,"Ecological and Environmental analysis, Spatial Analysis",ae-articles-rll,University of Leeds,Starting April 2025 40 | Valentin Todorov,VT,valentint,valentin@todorov.at,2025,2027,"Cluster Analysis & Finite Mixture Models, Databases with R, Graphics and Visualisation, Machine Learning & Statistical Learning, Missing Data, Multivariate Statistics, Official Statistics & Survey Methodology, Reproducible Research, Robust Statistical Methods",ae-articles-vt,UNIDO,Starting March 2025 41 | Julia Wrobel,JW,julia-wrobel,julia.wrobel@emory.edu,2025,2027,"Functional Data Analysis, Reproducible Research",ae-articles-jw,Emory University,Starting March 2025 42 | David Ardia,DA,ArdiaD,david.ardia@hec.ca,2025,2027,"Econometrics, Empirical Finance",ae-articles-da,HEC,Starting April 2025 43 | -------------------------------------------------------------------------------- /inst/auth/client_id.json: -------------------------------------------------------------------------------- 1 | {"installed":{"client_id":"241261609346-0so0bei1pmnt4qtotlq5bn324eigdjum.apps.googleusercontent.com","project_id":"r-journal","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"KcXg5KhkbpWt9llHyZmL5Jak","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}} 2 | -------------------------------------------------------------------------------- /inst/editors.csv: -------------------------------------------------------------------------------- 1 | name,email,github,real 2 | Hadley Wickham,h.wickham@gmail.com,,Hadley Wickham 3 | Heather Turner,ht@heatherturner.net,,Heather Turner 4 | Martyn Plummer,Martyn.Plummer@r-project.org,,Martyn Plummer 5 | Deepayan Sarkar,deepayan.sarkar@r-project.org,,Deepayan Sarkar 6 | Bettina Grün,Bettina.Gruen@jku.at,,Bettina Grün 7 | Michael Lawrence,lawrence.michael@gene.com,,Michael Lawrence 8 | Roger Bivand,Roger.Bivand@nhh.no,,Roger Bivand 9 | John Verzani,verzani@math.csi.cuny.edu,,John Verzani 10 | Olivia Lau,olivia.lau@post.harvard.edu,,Olivia Lau 11 | NM,nsmatloff@ucdavis.edu,,Norm Matloff 12 | CG,csgillespie@gmail.com,csgillespie,Colin Gillespie 13 | GS,ucfagls@gmail.com,gavinsimpson,Gavin Simpson 14 | MK,michael.kane@yale.edu,kaneplusplus,Michael Kane 15 | DC,dicook.rj@gmail.com,dicook,Di Cook 16 | CH,Catherine.Hurley@mu.ie,cbhurley,Catherine Hurley 17 | SU,s.urbanek@auckland.ac.nz,s-u,Simon Urbanek 18 | MV,mark.vanderloo@gmail.com,markvanderloo,Mark var der Loo 19 | RH,rob.hyndman@monash.edu,robjhyndman,Rob Hyndman 20 | ET,emi.tanaka@anu.edu.au,emitanaka,Emi Tanaka 21 | EZ,zabore2@ccf.org,zabore,Emily Zabor 22 | -------------------------------------------------------------------------------- /inst/examples/example.R: -------------------------------------------------------------------------------- 1 | library(rj) 2 | setwd("/data/ncsg3/github/r-journal//articles/") 3 | summarise_articles() 4 | options(browser = "google-chrome") 5 | 6 | article = "2020-39" 7 | path = "../articles/Submissions/2020-39/DESCRIPTION" 8 | article = as.article("2020-39") 9 | add_reviewer(article, 10 | name = "Kris Sankaran", 11 | email = "kris.sankaran@umontreal.ca", 12 | invite = TRUE) 13 | 14 | 15 | file.copy(from = file.path(article$path, "RJwrapper.pdf"), 16 | to = paste0(file.path("/tmp/", 17 | paste(article$id, collapse = "-") 18 | ), ".pdf")) 19 | setwd("/data/ncsg3/github/r-journal/rj/") 20 | -------------------------------------------------------------------------------- /inst/issue_template.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Volume {{volume}}/{{num}}" 3 | description: "Articles published in the {{month}} {{year}} issue" 4 | draft: false 5 | date: {{year}}-{{month_num}}-01 6 | output: 7 | rjdistill::rjournal_web_article: 8 | self_contained: false 9 | --- 10 | 11 | ```{css, echo = FALSE} 12 | d-article > * { 13 | grid-column: page; 14 | } 15 | ``` 16 | 17 | [Complete issue `r icons::fontawesome$solid$"file-pdf"`](RJ-{{issue}}.pdf) 18 | 19 | 20 | 21 | 22 | 23 | ## Table of contents 24 | 25 | ```{r, results = "asis", echo = FALSE, layout = "l-page"} 26 | articles <- yaml::read_yaml("{{issue}}.yml")$articles 27 | contents <- lapply(articles, function(x) { 28 | if(!is.null(x$heading)) { 29 | return(paste("\n\n###", x$heading)) 30 | } 31 | stringr::str_glue( 32 | "[{x$title}]({if(grepl('RJ-\\\\d{4}-\\\\d{2,}', x$slug)) '../../articles/' else '../../news/RJ-{{issue}}-'}{x$slug})
{glue::glue_collapse(x$author, sep = ', ', last = ' and ')} {x$pages[1]}\n\n") 33 | }) 34 | 35 | cat(do.call(c, contents), sep = "\n") 36 | ``` 37 | -------------------------------------------------------------------------------- /inst/keywords-list.csv: -------------------------------------------------------------------------------- 1 | "","reviewer","details","submission" 2 | "1","Bayesian","Bayesian Inference","Bayesian Inference" 3 | "2","Chem Phys","Chemometrics and Computational Physics","Chemometrics and Computational Physics" 4 | "3","Clinical Trials","Clinical Trial Design Monitoring and Analysis","Clinical Trial Design Monitoring and Analysis" 5 | "4","Cluster","Cluster Analysis & Finite Mixture Models","Cluster Analysis & Finite Mixture Models" 6 | "5","Databases","Databases with R","Databases with R" 7 | "6","Differential Equations","Differential Equations","Differential Equations" 8 | "7","Distributions","Probability Distributions","Distributions" 9 | "8","Econometrics","Econometrics","Econometrics" 10 | "9","Environmetrics","Analysis of Ecological and Environmental Data","Ecological and Environmental analysis" 11 | "10","Experimental Design","Design of Experiments (DoE) & Analysis of Experimental Data","Design and Analysis of Experiments (DoE)" 12 | "11","Extreme Value","Extreme Value Analysis","Extreme Value Analysis" 13 | "12","Finance","Empirical Finance","Empirical Finance" 14 | "13","Functional Data","Functional Data Analysis","Functional Data Analysis" 15 | "14","Genetics","Statistical Genetics","Statistical Genetics" 16 | "15","Graphics","Graphic Displays & Dynamic Graphics & Graphic Devices & Visualization","Graphical Models" 17 | "16","Graphics","Graphic Displays & Dynamic Graphics & Graphic Devices & Visualization","Graphics and Visualisation" 18 | "17","High Performance Computing","High-Performance and Parallel Computing with R","High-Performance Computing" 19 | "18","Hydrology","Hydrological Data and Modeling","Hydrological Data and Modeling" 20 | "19","Machine Learning","Machine Learning & Statistical Learning","Machine Learning & Statistical Learning" 21 | "20","Medical Imaging","Medical Image Analysis","Medical Image Analysis" 22 | "21","Meta Analysis","Meta-Analysis","Meta-Analysis" 23 | "22","Missing Data","Missing Data","Missing Data" 24 | "23","Model Deployment","Model Deployment with R","Model Deployment" 25 | "24","Multivariate","Multivariate Statistics","Multivariate Statistics" 26 | "25","Natural Language Processing","Natural Language Processing","Natural Language Processing" 27 | "26","Numerical Mathematics","Numerical Mathematics","Numerical Mathematics" 28 | "27","Official Statistics","Official Statistics & Survey Methodology","Official Statistics & Survey Methodology" 29 | "28","Optimization","Optimization and Mathematical Programming","Optimization and Mathematical Programming" 30 | "29","Pharmacokinetics","Analysis of Pharmacokinetic Data","Pharmacokinetic Analysis" 31 | "30","Phylogenetics","Phylogenetics, Especially Comparative Methods","Phylogenetics" 32 | "31","Psychometrics","Psychometric Models and Methods","Psychometric Models and Methods" 33 | "32","Reproducible Research","Reproducible Research","Reproducible Research" 34 | "33","Robust","Robust Statistical Methods","Robust Statistical Methods" 35 | "34","Social Sciences","Statistics for the Social Sciences","Social Sciences" 36 | "35","Spatial","Analysis of Spatial Data","Spatial Analysis" 37 | "36","Spatio Temporal","Handling and Analyzing Spatio-Temporal Data","Spatio-Temporal Analysis" 38 | "37","Survival","Survival Analysis","Survival Analysis" 39 | "38","Teaching Statistics","Teaching Statistics","Teaching Statistics" 40 | "39","Time Series","Time Series Analysis","Time Series Analysis" 41 | "40","Tracking","Processing and Analysis of Tracking Data","Tracking Data" 42 | "41","Web Technologies","Web Technologies and Services","Web Technologies and Services" 43 | "42","g R","gRaphical Models in R","gRaphical Models in R" 44 | -------------------------------------------------------------------------------- /inst/sample/Accepted/2012-01/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Title: An exciting new R package 2 | Authors: 3 | "Tina D. Richardson" , 4 | "Brian Marshall" 5 | Editor: John Smith 6 | Reviewers: 7 | "Cynthia Scott" , 8 | "William Brumfield" 9 | Status: 10 | 2012-01-24 Submitted, 11 | 2012-02-03 Acknowledged, 12 | 2012-03-22 Under review, 13 | 2012-06-07 Minor revision, 14 | 2012-08-13 Accepted 15 | -------------------------------------------------------------------------------- /inst/sample/Accepted/2012-02/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Title: Another R package 2 | Authors: 3 | "Liang Mao" 4 | Editor: Numan Dreijer 5 | Reviewers: 6 | "Ye Yeh" , 7 | "Hua Ch'iu" 8 | Status: 9 | 2012-02-01 Submitted, 10 | 2012-02-03 Acknowledged, 11 | 2012-07-12 Under review, 12 | 2012-10-16 Minor revision, 13 | 2012-10-24 Accepted 14 | -------------------------------------------------------------------------------- /inst/sample/Rejected/2012-03/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Title: Innovations in R 2 | Authors: 3 | "Dian Bottenberg" , 4 | "Jisca Kwarten" 5 | Editor: Numan Dreijer 6 | Reviewers: 7 | "Vasiliki van der Voorn" 8 | Status: 9 | 2012-02-17 Submitted, 10 | 2012-03-16 Acknowledged, 11 | 2012-04-12 Updated, 12 | 2012-09-04 Rejected 13 | -------------------------------------------------------------------------------- /inst/sample/Rejected/2012-04/DESCRIPTION: -------------------------------------------------------------------------------- 1 | ID: 2012-04 2 | Title: How to write great R code 3 | Authors: 4 | "Abdo Perales Vallejo" 5 | Editor: John Smith 6 | Reviewers: 7 | "Elbio Tejada Carrillo" , 8 | "Camille Rolón" 9 | Status: 10 | 2012-02-20 Submitted, 11 | 2012-03-16 Acknowledged, 12 | 2012-03-30 Under review, 13 | 2012-06-06 Rejected 14 | -------------------------------------------------------------------------------- /inst/sample/Submitted/2012-05/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Title: An exciting new R package 2 | Authors: 3 | "Tina D. Richardson" , 4 | "Brian Marshall" 5 | Editor: John Smith 6 | Reviewers: 7 | "Cynthia Scott" , 8 | "William Brumfield" 9 | Status: 10 | 2012-01-24 Submitted, 11 | 2012-02-03 Acknowledged, 12 | 2012-03-22 Under review, 13 | 2012-06-07 Minor revision 14 | -------------------------------------------------------------------------------- /inst/templates/accept.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | CC: {{editor}} 3 | Subject: [RJournal {{id}}] accepted 4 | --- 5 | Dear {{name}}, 6 | 7 | Congratulations, your submission to the R journal is now accepted. The RJournal currently published four issues per year. Your paper will probably appear in the issue for the quarter in which it was accepted. Next, please: 8 | 9 | Check your paper against the checklist provided at https://rjournal.github.io/rjtools/articles/check_functions.html and also that the formatting adheres to the advice at https://rjournal.github.io/rjtools/articles/format-details.html 10 | 11 | Once you've done that, please respond to this email with a full copy of the files for your article as a zip archive. 12 | 13 | Prior to publication, the paper will be proofed by a copy-editor and requested changes will be sent to you to make, with a short turn-around time. 14 | 15 | As an R Journal author, if you are not already, we would be delighted to have your expertise as a reviewer for future submissions; please fill in your details here: https://journal.r-project.org/reviewer.html 16 | 17 | Regards, 18 | 19 | {{me}} 20 | -------------------------------------------------------------------------------- /inst/templates/acknowledge.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | CC: {{edmail}} 3 | Subject: [RJournal {{id}}] submission acknowledged 4 | --- 5 | Dear {{name}}, 6 | 7 | We are pleased to receive your submission "{{title}}" to the R Journal. It has been given id {{id}}; please use that in future correspondence. Your handling editor will be {{edname}} (CCd). 8 | 9 | The submission process proceeds as follows: 10 | 11 | * review by editorial board & assignment of associate editor (~2-3 weeks) 12 | * reviewers solicited (~2-4 weeks) 13 | * reviews received (~2-3 months) 14 | 15 | We are currently experiencing many submissions, and these times are ambitious. The reviewing process can take anywhere from a few weeks to a number of months, but feel free to inquire on the progress of your submission. 16 | 17 | Regards, 18 | {{me}} 19 | 20 | -------------------------------------------------------------------------------- /inst/templates/acknowledge_revision.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] submission acknowledged 3 | Cc: {{editor}} 4 | --- 5 | Dear {{name}}, 6 | 7 | We are pleased to receive the revision of your submission "{{title}}" to the R Journal. 8 | 9 | Regards, 10 | 11 | {{me}} 12 | 13 | -------------------------------------------------------------------------------- /inst/templates/ae-invitation.txt: -------------------------------------------------------------------------------- 1 | Invitation to become an Associate Editor for the R Journal 2 | 3 | Dear {invitee}, 4 | 5 | On behalf of the R Journal Editorial board, I am writing to invite you to be an Associate Editor for our journal. These are new positions that have recently been approved by the R Foundation to help the journal grow to meet the increasing number of submissions as we continue to see greater impact in research in statistical computing and data science infrastructure. 6 | 7 | As an Associate Editor, you will receive manuscripts from an Editor for review. You will be responsible for 8 | 9 | 1. Soliciting 1-3 reviewers - In general we prefer at least two reviewers. However, one is acceptable when reviewers are sufficiently difficult to find, and the subject area is esoteric. To make this process easier we have compiled a list of potential reviewers and their subject expertise. You are also welcome to invite other reviewers 10 | 2. Receiving those reviews - Reviewers are given approximately 1 - 3 month to review a paper. 11 | 3. Deciding if the paper should be rejected, accepted with major revisions, accepted with minor revisions, or accepted as is. 12 | 13 | So that this responsibility is not overwhelming, we aim to have AE's handle at most 1-2 paper per month. 14 | 15 | All of these operations are conducted using the rj package, available from the rjournal Github site. You will be provided with a private Github repository that will hold submissions in the R Journal's current structure. We will provide instruction and examples as to how steps 1 - 3 should be recorded. 16 | 17 | In addition, we have a slack organisation for regular communication, and we hold monthly meetings to facilitate communication among all editors and address issues and concerns. 18 | 19 | We hope you will accept the invitation and we look forward to working with you. 20 | 21 | Sincerely, 22 | 23 | Mark van der Loo 24 | Editor-in-Chief, R Journal 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /inst/templates/gmail_acknowledge.txt: -------------------------------------------------------------------------------- 1 | Dear {{name}}, 2 | 3 | We are pleased to receive your submission "{{title}}" to the R Journal. Prior to publication we would like you to carefully proof your article. 4 | 5 | The submission process proceeds as follows: 6 | 7 | * review by editorial board & assignment of associate editor (~1-2 weeks) 8 | * reviewers solicited (~1-2 weeks) 9 | * reviews received (~1-2 months) 10 | 11 | We are currently experiencing many submissions, and these times are ambitious. The editorial team is juggling many tasks, working from home and child care, which is making processing articles quickly very difficult. The reviewing process can take anywhere from a few weeks to a number of months, but feel free to inquire on the progress of your submission. 12 | 13 | Best wishes, 14 | 15 | {{me}} 16 | 17 | -------------------------------------------------------------------------------- /inst/templates/gmail_proofing.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] Please proof your article 3 | --- 4 | Dear {{name}}, 5 | 6 | We are pleased to publish your article "{{title}}" in the next issue of the R Journal. 7 | 8 | The journal runs with volunteer efforts, and we hope that you can assist us by carefully proofing the final version of your article. We have included a version of the pdf annotated with corrections that need to be made as specified by our editorial assistant. Annotations ending with "?" are optional, but should be considered. It might also helpful to have a colleague read through it fully. 9 | 10 | Please don't use \section*{} except for special sections such as Acknowledgements or Appendix. All regular sections should use \section{}. 11 | 12 | Only the first line of the affilliation in \address{} is used for the article header. If you have multiple parts of the address that you want to show, you have to make sure you put them on the first line, e.g., "Department of Bar, University of Foo \\" instead of "Department of Bar\\" which would not show the university. 13 | 14 | In addition, your R code uses following packages that are not mentioned in the article - please add a mention and cite them: 15 | 16 | and the manuscript mentions following packages without citing them - please add the corresponding citations (see citation() in R): 17 | 18 | Please submit the full, final version of your submission as a ZIP file to: 19 | 20 | https://journal-submission.r-project.org/proofs.html 21 | 22 | by {{date}}. If you would like to select one of your paper figures as a graphical abstract, please also let me know the name of the image file. 23 | 24 | Regards, 25 | {{me}} 26 | 27 | -------------------------------------------------------------------------------- /inst/templates/issue.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | CC: {{editor}} 3 | Subject: RJ {{id}}: Issue published 4 | --- 5 | TODO: change from address 6 | 7 | Dear {{name}}, 8 | 9 | The latest issue of the R Journal including your paper has now been published on our website. 10 | You will find the new issue at https://journal.r-project.org under "CURRENT". 11 | 12 | Regards, 13 | 14 | {{me}} 15 | 16 | -------------------------------------------------------------------------------- /inst/templates/online.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | CC: {{editor}} 3 | Subject: RJ {{id}}: Online 4 | --- 5 | TODO: change from address 6 | 7 | Dear {{name}}, 8 | 9 | I have just published your article to the online first section of the R journal website: it should be available from 10 | http://journal.r-project.org/archive/accepted/ in the next few hours. 11 | 12 | Regards, 13 | 14 | {{me}} 15 | 16 | -------------------------------------------------------------------------------- /inst/templates/reject.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] journal submission 3 | --- 4 | Dear {{name}}, 5 | 6 | Thank you for submitting your article "{{title}}" to the R Journal. The 7 | work is very interesting (SAY SOMETHING NICE) but it falls short on 8 | (REASON FOR REJECTION). For these reasons, we have decided to reject 9 | the paper. Please see the reviews provided for more details. 10 | 11 | We wish you the best, whatever you decide to do with your article. 12 | 13 | Regards, 14 | 15 | {{me}} 16 | -------------------------------------------------------------------------------- /inst/templates/reject_format.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] journal submission 3 | --- 4 | Dear {{name}}, 5 | 6 | Thank you for submitting your article "{{title}}" to the R Journal. The 7 | work looks to be very interesting but the files are not in the format required by the R Journal. Please read the author instructions, and re-submit. 8 | 9 | Regards, 10 | 11 | {{me}} 12 | -------------------------------------------------------------------------------- /inst/templates/resubmission.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] journal submission 3 | --- 4 | Dear {{name}}, 5 | 6 | Thank you for your submission to the R Journal, titled "{{title}}". It has been given id {{id}}; please use that in future correspondence. 7 | 8 | A requirement of the journal before we consider a submission is that it is fully reproducible. I'm getting the following errors: 9 | 10 | 11 | 12 | Please fix these problems and submit a zip containing all relevant files through the article submission at https://journal.r-project.org/submissions.html. You will need to include the article id {{id}} in the form. 13 | 14 | Regards, 15 | 16 | {{me}} 17 | -------------------------------------------------------------------------------- /inst/templates/review-template.txt: -------------------------------------------------------------------------------- 1 | ## Referee report of "{{title}}". 2 | 3 | - **Id**: {{id}} 4 | 5 | ## General 6 | 7 | A short summary of the article's main contributions and shortcomings. Is the 8 | article important to the R the community? Is the approach sensible? Are technology 9 | and methods up-to-date? 10 | 11 | 12 | ## Detailed comments 13 | 14 | Point-wise comments (if applicable) and things to improve. 15 | 16 | ## Code 17 | 18 | In the case of an R package, review the code with 'the eye of the expert'. 19 | That is, not a line-by-line analyses for correctness, but look for obvious 20 | pitfalls. In particular, you could look at the following. 21 | 22 | Usability: 23 | 24 | - Does the package have a clear and user-friendly workflow? 25 | - Is the documentation well-written and user-focused? 26 | 27 | Code: 28 | 29 | - Are there obvious edge cases that are missed? 30 | - Could the functions presented be improved? 31 | - Is the code well-organized and understandable? 32 | 33 | -------------------------------------------------------------------------------- /inst/templates/review-updated.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] Revised Manuscript 3 | --- 4 | Dear {{firstname}}, 5 | 6 | We have received revisions for manuscript {{id}}, "{{title}}” incorporating your and other reviewers' feedback. Would you please provide an updated review with the following. 7 | - Have your comments have been sufficiently addressed? If not, please provide suggestions to better. 8 | - Are there other issues with either the package or the manuscript that have been introduced? 9 | 10 | As with the first review, it should be returned by email. Our preferred format for the review is a text file attachment, but a pdf is also acceptable. This will be sent on to the authors. Your email should also give your overall recommendation (this part is for the editor only): Accept with minor revisions (you won't see it again), accept with major revisions (you'll re-review the revisions), or reject. 11 | 12 | 13 | Please let me know if you are unwilling or unable to provide an updated review. I would like to receive the review, if possible, in the next month. 14 | 15 | Regards, 16 | 17 | {{me}} 18 | -------------------------------------------------------------------------------- /inst/templates/review.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] Review invitation 3 | --- 4 | 5 | TODO: attach paper, supplementaries (code and data) 6 | TODO: attach review template, find in correspondence folder 7 | 8 | Dear {{firstname}}, 9 | 10 | Would you be willing to review the attached submission to the R Journal titled "{{title}}"? I think the topic is a good fit with your interests and expertise. 11 | 12 | The R Journal is an indexed, open-access scientific journal published by the R foundation. The editorial board consists of volunteers from academia, industry and government. The scope of the journal is described at https://journal.r-project.org/#article-types. 13 | 14 | Please let me know if you are willing to take on this review. If you have a conflict of interest, we request that you decline the invitation. In the case you decline, suggestions for alternative reviewers are welcome. 15 | 16 | If you agree to review this work, we kindly ask you to use the attached template for your review report, to be returned by email. Note that both the paper and the package or code should be reviewed. Your email should also give your overall recommendation (this part is for the editor only): Accept with minor revisions (you won't see it again), accept with major revisions (you'll re-review the revisions), or reject. 17 | 18 | Please let me know if you are willing to take on this review. I would like to receive the review, if possible, by {{date}}. 19 | 20 | With kind regards, 21 | {{me}} 22 | -------------------------------------------------------------------------------- /inst/templates/revision-major.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] journal submission 3 | --- 4 | Dear {{name}}, 5 | 6 | Thank you for your submission to the R Journal, titled "{{title}}". The attached reports from expert reviewers request some major revisions. 7 | 8 | We would appreciate a revised version and point-by-point response to the reviewers comments within 2 months. Remember, that when responding to the reviewer's comments, your job is to persuade me, the editor, that either you've dealt with the issue, or that it's not relevant. To this end, please produce a single document that includes all the reviewers comments mingled with your responses. 9 | 10 | When you have your revised article ready, please submit a zip containing all relevant files through the article submission at https://journal.r-project.org/submissions.html. You will need to include the article id {{id}} in the form. 11 | 12 | I look forward to seeing the next iteration. 13 | 14 | Regards, 15 | 16 | {{me}} 17 | -------------------------------------------------------------------------------- /inst/templates/revision-minor.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | Subject: [RJournal {{id}}] journal submission 3 | --- 4 | TODO: attach review comments 5 | 6 | Dear {{name}} 7 | 8 | Thank you for your submission to the R Journal, titled "{{title}}". The attached reports from expert reviewers request some minor revisions. 9 | 10 | We would appreciate a revised version and point-by-point response to the reviewers comments within 1 month. Remember, that when responding to the reviewer's comments, your job is to persuade me, the editor, that either you've dealt with the issue, or that it's not relevant. To this end, please produce a single document that includes all the reviewers comments mingled with your responses. 11 | 12 | When you have your revised article ready, please submit a zip containing all relevant files through the article submission at https://journal.r-project.org/submissions.html. You will need to include the article id {{id}} in the form. 13 | 14 | I look forward to seeing the next iteration. 15 | 16 | Regards, 17 | 18 | {{me}} 19 | -------------------------------------------------------------------------------- /inst/templates/withdraw.txt: -------------------------------------------------------------------------------- 1 | To: {{email}} 2 | CC: {{editor}} 3 | Subject: [RJournal {{id}}] withdrawn 4 | --- 5 | Dear {{name}}, 6 | 7 | This email is to acknowledge the withdrawal of your article, "{{title}}", from the R journal. 8 | 9 | Regards, 10 | 11 | {{me}} 12 | -------------------------------------------------------------------------------- /man/AEs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ae.R 3 | \name{AEs} 4 | \alias{AEs} 5 | \alias{detect_AE} 6 | \alias{AE} 7 | \alias{is_AE} 8 | \title{Associate editor (AE) functions} 9 | \usage{ 10 | AEs() 11 | 12 | detect_AE(path = ".", require = FALSE) 13 | 14 | AE(path = ".") 15 | 16 | is_AE(path = ".") 17 | } 18 | \arguments{ 19 | \item{path}{string, path to the git repository to use 20 | for detection.} 21 | 22 | \item{require}{logical, if \code{TRUE} then failing to detect 23 | the AE is considered an error} 24 | } 25 | \value{ 26 | \itemize{ 27 | \item{\code{AEs()}: a data frame with all associate editors} 28 | \item{\code{detect_AE()}: \code{NULL} if not found or the 29 | row from \code{AEs()} corresponding to the AE} 30 | \item{ \code{AE}: \code{NULL} if not an AE or a row from \code{AEs()}} 31 | \item{\code{is_AE}: \code{TRUE} if the user if an AE or 32 | the repository is an AE repository, \code{FALSE} otherwise} 33 | } 34 | } 35 | \description{ 36 | Functions to determine if the user is an AE and retrieve relevant AE information 37 | } 38 | \details{ 39 | \itemize{ 40 | \item{\code{AEs()}: read the associate-editors.csv in the rj package as a data frame} 41 | \item{\code{detect_AE()}: determine AE from the remote of the repository in 42 | \code{path} or from the git config e-mail.this only work for AE and will fail for editors} 43 | \item{\code{AE()}: returns the corresponding row from \code{AEs()} if called 44 | by an associate editor or in an AE repository, otherwise 45 | \code{NULL}. It relies on either \code{RJ_EDITOR} environment 46 | variable to contain the name of the editor or detection from the 47 | git repository pointed to by \code{path} (see 48 | \code{\link{detect_AE}}).} 49 | \item{\code{is_AE()}: determine if the user is an AE or the repository is an AE repository} 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /man/acknowledge_revision.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/submissions.R 3 | \name{acknowledge_revision} 4 | \alias{acknowledge_revision} 5 | \title{Send revision received acknowledgement} 6 | \usage{ 7 | acknowledge_revision(article) 8 | } 9 | \arguments{ 10 | \item{article}{this is the article id} 11 | } 12 | \description{ 13 | Send revision received acknowledgement 14 | } 15 | -------------------------------------------------------------------------------- /man/acknowledge_submission.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/submissions.R 3 | \name{acknowledge_submission} 4 | \alias{acknowledge_submission} 5 | \title{Send submission acknowledgements} 6 | \usage{ 7 | acknowledge_submission(article, editor) 8 | } 9 | \arguments{ 10 | \item{article}{this is the article id} 11 | 12 | \item{editor}{optional string, if specified, also sets the \code{Editor:} field to that value and the handling editor will be CCd on the e-mail.} 13 | } 14 | \description{ 15 | Send submission acknowledgements 16 | } 17 | -------------------------------------------------------------------------------- /man/action.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{reject} 4 | \alias{reject} 5 | \alias{reject_format} 6 | \alias{accept} 7 | \alias{withdraw} 8 | \alias{resubmission} 9 | \alias{major_revision} 10 | \alias{minor_revision} 11 | \title{Accept, reject, or withdraw an article} 12 | \usage{ 13 | reject(article, comments = "", date = Sys.Date()) 14 | 15 | reject_format(article, comments = "", date = Sys.Date()) 16 | 17 | accept(article, comments = "", date = Sys.Date()) 18 | 19 | withdraw(article, comments = "", date = Sys.Date()) 20 | 21 | resubmission(article, comments = "", date = Sys.Date()) 22 | 23 | major_revision(article, comments = "", date = Sys.Date()) 24 | 25 | minor_revision(article, comments = "", date = Sys.Date()) 26 | } 27 | \arguments{ 28 | \item{article}{Article id, like \code{"2014-01"}} 29 | 30 | \item{comments}{Any additional comments} 31 | 32 | \item{date}{Date of status update. If omitted defaults to today.} 33 | } 34 | \description{ 35 | This set of functions wraps around \code{update_status()} and \code{email_template} 36 | to first update the status field in the DESCRIPTION file and 37 | then draft an email from the template. Articles are verified to be under the 38 | Submission folder before carrying out the actions to avoid mistake 39 | input of article ID. 40 | } 41 | -------------------------------------------------------------------------------- /man/actionable_articles.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/assignments.R 3 | \name{actionable_articles} 4 | \alias{actionable_articles} 5 | \title{Show articles that require attention with the corresponding action} 6 | \usage{ 7 | actionable_articles(editor, invite = 7, review = 30, verbose = FALSE) 8 | } 9 | \arguments{ 10 | \item{editor}{string, initial of an editor or an associate editor, defaults to \code{RJ_EDITOR} env var} 11 | 12 | \item{invite}{integer, number of days after which an invite is considered overdue} 13 | 14 | \item{review}{integer, number of days after which a review is considered overdue. Note that you can extend this by specifying a due date in the comment, e.g.: "2021-03-01 Agreed (until 2021-05-01)" would allow for two months.} 15 | 16 | \item{verbose}{logical, if \code{TRUE} it will always list the full reviewer report for each entry} 17 | } 18 | \description{ 19 | Show articles that require attention with the corresponding action 20 | } 21 | -------------------------------------------------------------------------------- /man/active_articles.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/articles.R 3 | \name{active_articles} 4 | \alias{active_articles} 5 | \alias{accepted_articles} 6 | \title{List articles.} 7 | \usage{ 8 | active_articles(include = c("Submissions", "Accepted")) 9 | 10 | accepted_articles() 11 | } 12 | \arguments{ 13 | \item{include}{A character vector of directories to include.} 14 | } 15 | \description{ 16 | List all articles in common directories. 17 | } 18 | \details{ 19 | \itemize{ 20 | \item \code{active_articles}: \file{Submissions/}, \file{Accepted/} 21 | \item \code{accepted_articles}: \file{Accepted/} 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /man/add_ae.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ae_workload.R 3 | \name{add_ae} 4 | \alias{add_ae} 5 | \title{Add AE to the DESCRIPTION} 6 | \usage{ 7 | add_ae(article, name, date = Sys.Date()) 8 | } 9 | \arguments{ 10 | \item{article}{article id} 11 | 12 | \item{name}{a name used to match AE, can be AE initials, name, github handle, or email} 13 | 14 | \item{date}{the date for updating status} 15 | } 16 | \description{ 17 | Fuzzy match to find the initial of the AE to fill in the article DESCRIPTION. 18 | Checks that AE term has not ended. 19 | The status field is also updated with a new line of add AE. 20 | } 21 | -------------------------------------------------------------------------------- /man/add_review.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reviewers.R 3 | \name{add_review} 4 | \alias{add_review} 5 | \title{Add the review file received from the reviewer} 6 | \usage{ 7 | add_review( 8 | article, 9 | reviewer_id, 10 | review, 11 | recommend = NULL, 12 | date = Sys.Date(), 13 | AE = is_AE() 14 | ) 15 | } 16 | \arguments{ 17 | \item{article}{Article id, like \code{"2014-01"}} 18 | 19 | \item{reviewer_id}{Numeric, the index of the intended reviewer in the Reviewer field. 20 | 1 for the first reviewer, 2 for the second} 21 | 22 | \item{review}{Path to the review file, e.g. pdf, txt, or docx format. If not specified it is assumed that you added the new file into the correspondence directory and the last file for that reviewer will be used. If you specify \code{-review-.} filename (no path) and it already exists in the correspondence directory, it will be used.} 23 | 24 | \item{recommend}{Reviewer's recommendation, one of: Accept, Minor, Major, Reject. 25 | If not specified, an attempt is made to auto-detect it from the file by 26 | looking at the first occurrence of those keywords. If auto-detect fails, use "Received".} 27 | 28 | \item{date}{Date of the comment, defaults to today's date} 29 | 30 | \item{AE}{Logical, if \code{TRUE} then \code{"AE: "} prefix is added to the recommendation.} 31 | } 32 | \description{ 33 | This function adds the review file received from the reviewer to 34 | the correspondence folder of the article. 35 | } 36 | \examples{ 37 | \dontrun{ 38 | # add review file from the first reviewer and recommend accepting it 39 | add_review("2020-114", reviewer_id = 1, review = file.choose(), recommend = "Accept") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /man/add_reviewer.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reviewers.R 3 | \name{add_reviewer} 4 | \alias{add_reviewer} 5 | \title{Invite an reviewer} 6 | \usage{ 7 | add_reviewer(article, name, email, invite = TRUE) 8 | } 9 | \arguments{ 10 | \item{article}{Article id, like \code{"2014-01"}} 11 | 12 | \item{name}{Full name of the reviewer} 13 | 14 | \item{email}{Email address of the reviewer} 15 | 16 | \item{invite}{Logical, whether to automatically construct an email to invite the reviewer} 17 | } 18 | \description{ 19 | This function adds the reviewer information(name and email) to the reviewers 20 | field in the DESCRIPTION as well as draft an email to invite the reviewer. 21 | } 22 | \examples{ 23 | \dontrun{ 24 | add_reviewer("2019-117", "Quiet Quokka", "qqplot@waspot.com") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /man/address.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rfc822.R 3 | \name{address} 4 | \alias{address} 5 | \title{An S3 class to represent email addresses.} 6 | \usage{ 7 | address(email = NULL, name = NULL, comment = NULL) 8 | } 9 | \arguments{ 10 | \item{email}{Email address of the reviewer} 11 | 12 | \item{name}{Display name, optional} 13 | 14 | \item{comment}{comment, optional} 15 | } 16 | \description{ 17 | An S3 class to represent email addresses. 18 | } 19 | \examples{ 20 | address("h.wickham@gmail.com") 21 | address("h.wickham@gmail.com", "Hadley Wickham") 22 | } 23 | -------------------------------------------------------------------------------- /man/ae_workload.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ae_workload.R 3 | \name{ae_workload} 4 | \alias{ae_workload} 5 | \alias{get_AE} 6 | \title{Check the number of articles an AE is currently working on} 7 | \usage{ 8 | ae_workload(articles = NULL, day_back = 365, active_only = FALSE) 9 | 10 | get_AE(x) 11 | } 12 | \arguments{ 13 | \item{articles}{a tibble summary of articles in the accepted and submissions 14 | folder. Output of \code{tabulate_articles()}} 15 | 16 | \item{day_back}{numeric; positive number of day to go back for calculating AE 17 | workload. Retains any article where any status entry for an article is 18 | newer than `day_back` days ago.} 19 | 20 | \item{active_only}{Toggle to show only active AEs (filtered by end year and comment field).} 21 | 22 | \item{x}{a single article, i.e. as.article("Submissions/2020-114")} 23 | } 24 | \description{ 25 | This will examine the DESCRIPTION files for articles in 26 | the Submissions folder, check articles that have status 27 | "with AE". 28 | } 29 | \examples{ 30 | \dontrun{ 31 | ae_workload() 32 | } 33 | \dontrun{ 34 | art <- as.article("Submissions/2020-114") 35 | get_AE(art) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /man/article.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/article.R 3 | \name{article} 4 | \alias{article} 5 | \alias{as.article} 6 | \title{S3 class for article objects} 7 | \usage{ 8 | article(..., quiet = FALSE) 9 | 10 | as.article(id) 11 | } 12 | \arguments{ 13 | \item{...}{Named arguments giving the components of an article: 14 | id, authors, title, editor, reviewers, status} 15 | 16 | \item{quiet}{if \code{TRUE} suppresses failure messages.} 17 | 18 | \item{id}{a path to a DESCRIPTION, a path to a directory containing 19 | DESCRIPTION, or a article name, found in a sub-directory rejected, 20 | accepted or submissions} 21 | } 22 | \description{ 23 | Create or convert input into an s3 article object 24 | } 25 | \details{ 26 | if the article is not parsable, \code{article()} will 27 | print an error message and return a unparsed blob. This ensures that 28 | information is not lost even if some articles have parsing errors. 29 | 30 | Usually the best way to use \code{as_article()} is to have your working directory set to the 31 | admin repo, and refer to articles by their id. See the examples section. 32 | } 33 | \examples{ 34 | \dontrun{ 35 | as.article("2012-01") 36 | as.article("2012-02") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /man/article_status_plot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/summary_plots.R 3 | \name{article_status_plot} 4 | \alias{article_status_plot} 5 | \title{Generates a status plot for articles submitted in the last few years.} 6 | \usage{ 7 | article_status_plot(years = NULL, save = TRUE) 8 | } 9 | \arguments{ 10 | \item{years}{years considered. A vector of years, or defaults to last four years.} 11 | 12 | \item{save}{Defaults to TRUE. The plot is saved in the rjournal.github.io/resources folder.} 13 | } 14 | \value{ 15 | a ggplot, one bar per year (taken from article id) 16 | } 17 | \description{ 18 | Generates a status plot for articles submitted in the last few years. 19 | } 20 | \examples{ 21 | \dontrun{ 22 | article_status_plot() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /man/assignments.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/current.R 3 | \name{assignments} 4 | \alias{assignments} 5 | \title{Generate a summary of current and recent assignments to each editor} 6 | \usage{ 7 | assignments() 8 | } 9 | \description{ 10 | This should be run weekly. 11 | } 12 | -------------------------------------------------------------------------------- /man/author_emails.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/author-email.R 3 | \name{author_emails} 4 | \alias{author_emails} 5 | \title{Extract email from leading author of an issue for emailing} 6 | \usage{ 7 | author_emails(issue) 8 | } 9 | \arguments{ 10 | \item{issue}{the issue number in the `Proofs` folder, i.e. "2021-2"} 11 | } 12 | \description{ 13 | If the email of leading author is not available, the first author with 14 | available email will be used. 15 | } 16 | \examples{ 17 | \dontrun{ 18 | author_emails("2021-2") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /man/build_latex.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/publish.R 3 | \name{build_latex} 4 | \alias{build_latex} 5 | \title{Build article from LaTeX} 6 | \usage{ 7 | build_latex( 8 | article, 9 | share_path = normalizePath("../share", mustWork = TRUE), 10 | clean = TRUE 11 | ) 12 | } 13 | \arguments{ 14 | \item{article}{The article to build} 15 | 16 | \item{share_path}{???} 17 | 18 | \item{clean}{Remove generated files} 19 | } 20 | \description{ 21 | Build article from LaTeX 22 | } 23 | -------------------------------------------------------------------------------- /man/corr_author.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ae_workload.R 3 | \name{corr_author} 4 | \alias{corr_author} 5 | \title{Extract corresponding author from an article} 6 | \usage{ 7 | corr_author(article) 8 | } 9 | \arguments{ 10 | \item{article}{Article id, like \code{"2014-01"}} 11 | } 12 | \description{ 13 | Extract corresponding author from an article 14 | } 15 | \examples{ 16 | \dontrun{ 17 | # extract from a single article 18 | corr_author("Submissions/2020-114") 19 | 20 | # extract corresponding authors from the active articles 21 | all <- active_articles() 22 | purrr::map_dfr(all, corr_author) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /man/decline_reviewer.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reviewers.R 3 | \name{decline_reviewer} 4 | \alias{decline_reviewer} 5 | \alias{agree_reviewer} 6 | \alias{abandon_reviewer} 7 | \title{Update reviewer's response to invite} 8 | \usage{ 9 | decline_reviewer(article, reviewer_id) 10 | 11 | agree_reviewer(article, reviewer_id) 12 | 13 | abandon_reviewer(article, reviewer_id) 14 | } 15 | \arguments{ 16 | \item{article}{Article id, like \code{"2014-01"}} 17 | 18 | \item{reviewer_id}{Numeric, the index of the intended reviewer in the Reviewer field. 19 | 1 for the first reviewer, 2 for the second} 20 | } 21 | \description{ 22 | This function updates the reviewers field in the DESCRIPTION with reviewer's response: 23 | accept, decline, or abandon if no reply from the reviewer for a period of time. 24 | } 25 | \examples{ 26 | \dontrun{ 27 | # first reviewer declined 28 | decline_reviewer("2020-114", reviewer_id = 1) 29 | 30 | # second reviewer agreed 31 | agree_reviewer("2020-114", reviewer_id = 2) 32 | 33 | # third reviewer doesn't reply and deemed abandon 34 | abandon_reviewer("2020-114", reviewer_id = 3) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /man/email_template.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/email.R 3 | \name{email_template} 4 | \alias{email_template} 5 | \title{Send an email template.} 6 | \usage{ 7 | email_template(article, template) 8 | } 9 | \arguments{ 10 | \item{article}{An article id, e.g. \code{"2013-01"}} 11 | 12 | \item{template}{The name of a template (without extension) found 13 | in \code{inst/templates}.} 14 | } 15 | \description{ 16 | Interpolate article values into a template, and create a new (unsent) 17 | email in your default mail client. The email is created using 18 | \code{\link{browseURL}} and the mailto protocol, so it must be relatively 19 | brief. 20 | } 21 | \section{Text format}{ 22 | 23 | The template should be divided into header and body with \code{---}. 24 | The header should contain fields and values separated by \code{:} - 25 | only a limited 26 | } 27 | 28 | \section{Template parameters}{ 29 | 30 | The templates use whisker to insert template values. These have the form 31 | \code{\{\{field_name\}\}}. You can use any field from the description as 32 | well as the following special fields: 33 | 34 | \itemize{ 35 | \item name: the name of the first author 36 | \item email: email address of first author 37 | \item editor: name of editor 38 | \item me: your name, as determine by envvar \code{RJ_NAME} 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /man/email_text.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/email.R 3 | \name{email_text} 4 | \alias{email_text} 5 | \title{Generate an email template.} 6 | \usage{ 7 | email_text(text, means = getOption("RJ_EMAIL_TOOL", "mailto")) 8 | } 9 | \arguments{ 10 | \item{text}{character vector, text of the e-mail (including headers)} 11 | 12 | \item{means}{string, one of "mailto", "browser" (both open mailto: URL), 13 | "show" (file.show), "edit" (file.edit), "open" (shell open) or 14 | "clip" (system clipboard). Defaults to RJ_EMAIL_TOOL environment 15 | variable.} 16 | } 17 | \description{ 18 | Generate an email template. 19 | } 20 | -------------------------------------------------------------------------------- /man/filter_status.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/articles.R 3 | \name{filter_status} 4 | \alias{filter_status} 5 | \title{Find articles with a given status.} 6 | \usage{ 7 | filter_status(articles, status) 8 | } 9 | \arguments{ 10 | \item{articles}{A vector of articles, as given by accepted_articles()} 11 | 12 | \item{status}{The status you are looking for} 13 | } 14 | \description{ 15 | Find articles with a given status. 16 | } 17 | -------------------------------------------------------------------------------- /man/future_ids.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/submissions.R 3 | \name{future_ids} 4 | \alias{future_ids} 5 | \title{Generate a new id value.} 6 | \usage{ 7 | future_ids(ids, n = 1) 8 | } 9 | \arguments{ 10 | \item{ids}{XXX} 11 | 12 | \item{n}{No. of ids to generate} 13 | } 14 | \description{ 15 | Inspects submissions/, accepted/ and rejected to figure out which 16 | id is next in sequence. 17 | } 18 | -------------------------------------------------------------------------------- /man/get_accepted_but_not_online.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/articles.R 3 | \name{get_accepted_but_not_online} 4 | \alias{get_accepted_but_not_online} 5 | \title{Get articles to go online} 6 | \usage{ 7 | get_accepted_but_not_online() 8 | } 9 | \description{ 10 | Find the articles that are accepted but have not yet been 11 | published to online 12 | } 13 | -------------------------------------------------------------------------------- /man/get_article_keywords.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/match.R 3 | \name{get_article_keywords} 4 | \alias{get_article_keywords} 5 | \title{Extract keywords from a submitted article} 6 | \usage{ 7 | get_article_keywords(id) 8 | } 9 | \arguments{ 10 | \item{id}{the article id} 11 | } 12 | \value{ 13 | a list with keywords and authors of the article 14 | } 15 | \description{ 16 | Extract keywords from a submitted article 17 | } 18 | -------------------------------------------------------------------------------- /man/get_articles_path.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/path.R 3 | \name{get_articles_path} 4 | \alias{get_articles_path} 5 | \title{Get the directory of the articles repository, either as set by 6 | set_articles_path() or using git to determine repository root} 7 | \usage{ 8 | get_articles_path() 9 | } 10 | \description{ 11 | This path is used to point to articles on your file system. 12 | } 13 | -------------------------------------------------------------------------------- /man/get_md_from_pdf.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/publish.R 3 | \name{get_md_from_pdf} 4 | \alias{get_md_from_pdf} 5 | \title{Get metadata from a PDF file} 6 | \usage{ 7 | get_md_from_pdf(from, final = FALSE) 8 | } 9 | \arguments{ 10 | \item{from}{pdf file} 11 | 12 | \item{final}{If in a final state, then also include page information.} 13 | } 14 | \description{ 15 | Get metadata from a PDF file 16 | } 17 | -------------------------------------------------------------------------------- /man/get_reviewer_keywords.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/match.R 3 | \name{get_reviewer_keywords} 4 | \alias{get_reviewer_keywords} 5 | \title{Extract keywords from reviewer list} 6 | \usage{ 7 | get_reviewer_keywords() 8 | } 9 | \description{ 10 | Extract keywords from reviewer list 11 | } 12 | -------------------------------------------------------------------------------- /man/get_reviewers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/late.R 3 | \name{get_reviewers} 4 | \alias{get_reviewers} 5 | \title{Find all reviewers for submissions being handled by a given editor} 6 | \usage{ 7 | get_reviewers(editor) 8 | } 9 | \arguments{ 10 | \item{editor}{The editor or associate editor handling the submissions} 11 | } 12 | \value{ 13 | A data frame of reviewers 14 | } 15 | \description{ 16 | Find all reviewers for submissions being handled by a given editor 17 | } 18 | -------------------------------------------------------------------------------- /man/get_submissions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/submissions.R 3 | \name{get_submissions} 4 | \alias{get_submissions} 5 | \title{Download submissions.} 6 | \usage{ 7 | get_submissions(dry_run = FALSE) 8 | } 9 | \arguments{ 10 | \item{dry_run}{Use TRUE for testing, which will not change the sheet. 11 | Default is FALSE.} 12 | } 13 | \description{ 14 | Obtains submissions from the Google Sheets spreadsheet and downloads 15 | submission files from Google Drive. 16 | } 17 | \section{Process}{ 18 | 19 | The function does three things automatically: 20 | \enumerate{ 21 | \item Downloads and extracts submissions into appropriate directories. 22 | \item Marks submissions as "read" in the spreadsheet. 23 | \item Uploads acknowledgement emails to gmail account as drafts. 24 | } 25 | 26 | The user (editor-in-chief) then: 27 | \enumerate{ 28 | \item Ensures that the files have unzipped correctly (some authors 29 | incorrectly upload .rar or .tar.gz files) and that the latex 30 | compiles 31 | \item Manually sends the draft emails 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /man/git_user.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ae.R 3 | \name{git_user} 4 | \alias{git_user} 5 | \title{Detect user name and e-mail from GIT} 6 | \usage{ 7 | git_user() 8 | } 9 | \value{ 10 | named string, e-mail of the user 11 | } 12 | \description{ 13 | Detect user name and e-mail from GIT 14 | } 15 | -------------------------------------------------------------------------------- /man/invite_reviewers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reviewers.R 3 | \name{invite_reviewers} 4 | \alias{invite_reviewers} 5 | \alias{invite_reviewer} 6 | \title{Invite reviewer(s).} 7 | \usage{ 8 | invite_reviewers(article, prefix = "1") 9 | 10 | invite_reviewer(article, reviewer_id, prefix = "1") 11 | } 12 | \arguments{ 13 | \item{article}{Article id, like \code{"2014-01"}} 14 | 15 | \item{prefix}{Prefix added to start file name - used to distinguish 16 | between multiple rounds of reviewers (if needed)} 17 | 18 | \item{reviewer_id}{Numeric, the index of the intended reviewer in the Reviewer field. 19 | 1 for the first reviewer, 2 for the second} 20 | } 21 | \description{ 22 | Once you have added reviewers to the DESCRIPTION file, you can use 23 | this function to draft invite emails to them all. As well as drafting 24 | the emails, it also caches them locally in the \code{correspodence/} 25 | directory of the corresponding article. 26 | } 27 | -------------------------------------------------------------------------------- /man/late_aes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/late.R 3 | \name{late_aes} 4 | \alias{late_aes} 5 | \title{Find late AEs for submissions being handled by a given editor} 6 | \usage{ 7 | late_aes(editor) 8 | } 9 | \arguments{ 10 | \item{editor}{The editor handling the submissions} 11 | } 12 | \value{ 13 | A data frame of AEs who are late in handling a paper. 14 | } 15 | \description{ 16 | This should be run regularly and AEs chased up if they are late. 17 | } 18 | \details{ 19 | The number of stars has the following meaning: 20 | \itemize{ 21 | \item 1 star: not responded in more than 12 weeks; 22 | \item 2 stars: not responded in more than 18 weeks; 23 | \item 3 stars: not responded in more than 24 weeks; 24 | } 25 | Please chase up late AEs. 26 | } 27 | -------------------------------------------------------------------------------- /man/late_reviewers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/late.R 3 | \name{late_reviewers} 4 | \alias{late_reviewers} 5 | \title{Find late reviewers for submissions being handled by a given editor} 6 | \usage{ 7 | late_reviewers(editor) 8 | } 9 | \arguments{ 10 | \item{editor}{The editor or associate editor handling the submissions} 11 | } 12 | \value{ 13 | A data frame of reviewers who have not responded or not submitted their review on time. 14 | } 15 | \description{ 16 | This should be run regularly and reviewers chased up if they are late. 17 | } 18 | \details{ 19 | The number of stars has the following meaning: 20 | \itemize{ 21 | \item 1 star: not responded in more than a week or accepted more than 4 weeks ago; 22 | \item 2 stars: not responded in more than 2 weeks or accepted more than 8 weeks ago; 23 | \item 3 stars: not responded in more than 3 weeks or accepted more than 12 weeks ago; 24 | } 25 | Please chase up late reviewers. 26 | If a reviewer has declined, use \code{\link{decline_reviewer}} to remove them from the list. 27 | If you have decided to abandon a reviewer, use \code{\link{abandon_reviewer}}. 28 | } 29 | -------------------------------------------------------------------------------- /man/make_proof.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/issue.R 3 | \name{make_proof} 4 | \alias{make_proof} 5 | \title{Make a proof of an issue} 6 | \usage{ 7 | make_proof(id, exec = FALSE) 8 | } 9 | \arguments{ 10 | \item{id}{The id of the issue to proof} 11 | 12 | \item{exec}{Set to TRUE to make the proof, the default (FALSE) allows a preview of which articles will be moved where.} 13 | } 14 | \description{ 15 | The `make_proof()` function is the first step to creating an issue. It moves 16 | the 'proofed' articles from the `Accepted` folder and news articles from 17 | `News_items/\{id\}` into `Proofs/\{id\}`. 18 | } 19 | \details{ 20 | After the proof is made with this function, `publish_issue()` can be used to 21 | publish these articles into the `rjournal.github.io` repository. 22 | } 23 | -------------------------------------------------------------------------------- /man/match_keywords.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/match.R 3 | \name{match_keywords} 4 | \alias{match_keywords} 5 | \title{Find reviewers through keywords matching} 6 | \usage{ 7 | match_keywords(id, n = 5) 8 | } 9 | \arguments{ 10 | \item{id}{the article id in the description file} 11 | 12 | \item{n}{numeric; number of reviewer to display} 13 | } 14 | \description{ 15 | Find reviewers for an article through matching the article keywords 16 | to the keywords reviewers provided when registering. 17 | Notice that a googlesheet authenticate, with your email address printed, 18 | will first pop up to verify the access to the reviewer googlesheet. 19 | } 20 | \details{ 21 | All the reviewers are ranked based on the number of matching keywords 22 | and when there is a tie, a random draw is used. 23 | 24 | For example, an article A has 3 keywords. Two reviewers have all the 3 keywords matched, 25 | 5 reviewers have 2 matches, and another 10 have 1 match. To get 5 reviewers for article A, 26 | both reviewers with 3 matches are in and a random draw, among the five reviewers with 2 matches, 27 | is used to fill the remaining 3 places. 28 | } 29 | \examples{ 30 | \dontrun{ 31 | m1 <- match_keywords("2021-13") 32 | m2 <- match_keywords("2021-13", n = 10) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /man/need_decision.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/late.R 3 | \name{need_decision} 4 | \alias{need_decision} 5 | \title{Find articles that have at least two completed reviews and need a decision} 6 | \usage{ 7 | need_decision(editor) 8 | } 9 | \arguments{ 10 | \item{editor}{The editor or associate editor handling the submissions} 11 | } 12 | \value{ 13 | A data frame of papers needing more reviewers 14 | } 15 | \description{ 16 | Returns all articles with at least 2 completed reviews but no decision 17 | This should be run regularly and decisions made if necessary. 18 | } 19 | -------------------------------------------------------------------------------- /man/need_reviewers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/late.R 3 | \name{need_reviewers} 4 | \alias{need_reviewers} 5 | \title{Find articles that need reviewers for submissions being handled by a given editor.} 6 | \usage{ 7 | need_reviewers(editor) 8 | } 9 | \arguments{ 10 | \item{editor}{The editor or associate editor handling the submissions} 11 | } 12 | \value{ 13 | A data frame of papers needing more reviewers 14 | } 15 | \description{ 16 | Returns all articles with fewer than 2 invited reviewers. 17 | This should be run regularly and new reviewers appointed if necessary. 18 | } 19 | -------------------------------------------------------------------------------- /man/new_id.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/articles.R 3 | \name{new_id} 4 | \alias{new_id} 5 | \title{Generate a new id value.} 6 | \usage{ 7 | new_id() 8 | } 9 | \description{ 10 | Inspects submissions/, accepted/ and rejected to figure out which 11 | id is next in sequence. 12 | } 13 | -------------------------------------------------------------------------------- /man/online_metadata.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/publish.R 3 | \name{online_metadata} 4 | \alias{online_metadata} 5 | \title{Generate metadata needed for website.} 6 | \usage{ 7 | online_metadata() 8 | } 9 | \description{ 10 | Generate metadata needed for website. 11 | } 12 | -------------------------------------------------------------------------------- /man/online_metadata_for_article.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/publish.R 3 | \name{online_metadata_for_article} 4 | \alias{online_metadata_for_article} 5 | \title{Generate metadata for one article.} 6 | \usage{ 7 | online_metadata_for_article(x, final = FALSE) 8 | } 9 | \arguments{ 10 | \item{x}{An article} 11 | 12 | \item{final}{If in a final state, then also include page information.} 13 | } 14 | \description{ 15 | Generate metadata for one article. 16 | } 17 | -------------------------------------------------------------------------------- /man/parse_address_list.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rfc822.R 3 | \name{parse_address_list} 4 | \alias{parse_address_list} 5 | \title{Parse a string into a rfc822 address list.} 6 | \usage{ 7 | parse_address_list(x) 8 | } 9 | \arguments{ 10 | \item{x}{string to parse} 11 | } 12 | \value{ 13 | a list of \code{\link{address}}es 14 | } 15 | \description{ 16 | EBNF at \url{http://tools.ietf.org/html/rfc2822#section-3.4} 17 | } 18 | \examples{ 19 | parse_address_list(" Alison, Colin") 20 | } 21 | -------------------------------------------------------------------------------- /man/proofing.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{get_accepted_articles} 4 | \alias{get_accepted_articles} 5 | \alias{draft_proofing} 6 | \alias{proofing_article} 7 | \alias{proofing_article_text} 8 | \title{Functions for proofing articles} 9 | \usage{ 10 | get_accepted_articles() 11 | 12 | draft_proofing(article, update = TRUE) 13 | 14 | proofing_article(drafts) 15 | 16 | proofing_article_text(article) 17 | } 18 | \arguments{ 19 | \item{article}{article id} 20 | 21 | \item{update}{logical, if \code{TRUE} then the status is updated to "out for proofing"} 22 | 23 | \item{drafts}{list of \code{gmail_draft} objects} 24 | } 25 | \description{ 26 | Functions for proofing articles 27 | } 28 | \details{ 29 | \itemize{ 30 | \item{\code{get_accepted_articles()}: get list of articles in the Accepted folder to be proofed. This can be used with \code{draft_proofing} to construct emails to authors on the final version.} 31 | \item{\code{draft_proofing()}: generate proofing email for one article} 32 | \item{\code{proofing_article()}: send proofing article emails} 33 | \item{\code{proofing_article_text()}: writes the email text into the correspondence folder} 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /man/publish_article.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/publish.R 3 | \name{publish_article} 4 | \alias{publish_article} 5 | \title{Publish an individual article} 6 | \usage{ 7 | publish_article( 8 | article, 9 | volume, 10 | issue, 11 | home = get_articles_path(), 12 | web_path = file.path(home, "..", "rjournal.github.io"), 13 | legacy = FALSE, 14 | slug 15 | ) 16 | } 17 | \arguments{ 18 | \item{article}{article id} 19 | 20 | \item{volume}{The volume of the article's issue (typically, year - 2008)} 21 | 22 | \item{issue}{The issue number of the article's issue} 23 | 24 | \item{home}{Location of the articles directory} 25 | 26 | \item{web_path}{Location of the web source root of the journal, i.e., where \code{_article} lives. The default assumes all repos are checked out by their name in the same top-level directory.} 27 | 28 | \item{legacy}{(Very) old way of referencing the R journal} 29 | 30 | \item{slug}{optional, explicitly set the slug name (for expert use only, useful to skip problematic articles by advancing the slug names manually)} 31 | } 32 | \description{ 33 | This function will publish an individual article to the `rjournal.github.io` 34 | website repo. 35 | } 36 | \details{ 37 | The function will complete the following tasks: 38 | 1. Assign an appropriate slug if one is not set in the article DESCRIPTION 39 | 2. Produce a zip containing supplementary files described in the DESCRIPTION 40 | 3. If legacy PDF article, the articles will be converted into HTML format 41 | suitable for the distill HTML website. If an Rmd file with the output 42 | format `"rjtools::rjournal_web_article"` is found, it will be directly 43 | copied across as-is. 44 | 4. Set the issue metadata for these articles in the produced/copied Rmd front 45 | matter. 46 | 5. Update the status of the article's DESCRIPTION to 'online' 47 | 6. Render the document to update the article's HTML and PDF output. 48 | } 49 | \seealso{ 50 | `publish_issue()`, `publish_news()` 51 | } 52 | -------------------------------------------------------------------------------- /man/publish_issue.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/publish.R 3 | \name{publish_issue} 4 | \alias{publish_issue} 5 | \title{Publish an issue} 6 | \usage{ 7 | publish_issue(issue, home = get_articles_path(), republish_all = FALSE) 8 | } 9 | \arguments{ 10 | \item{issue}{The name of the issue, for example "2022-1".} 11 | 12 | \item{home}{Location of the articles directory} 13 | 14 | \item{republish_all}{If TRUE, then all articles and news will be published 15 | again in rjournal.github.io.} 16 | } 17 | \description{ 18 | This function will publish an issue in the `rjournal.github.io` repository 19 | from the Proofs folder. If any articles or news from this issue are not yet 20 | published, it will prompt you to also publish these articles and news. 21 | } 22 | \details{ 23 | This is the main function to be used for publishing an issue and its contents 24 | to the R Journal website. It completes these steps: 25 | 26 | 1. Publish any unpublished articles from this issue. 27 | 2. Publish any unpublished news from this issue. 28 | 3. Generate a templated R Markdown file for the issue, with some metadata 29 | completed in the document's front matter. 30 | 4. Open the generated issue R Markdown file for you to update the metadata, 31 | for example to update the editors of the issue. 32 | } 33 | -------------------------------------------------------------------------------- /man/publish_news.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/publish.R 3 | \name{publish_news} 4 | \alias{publish_news} 5 | \title{Publish a news article} 6 | \usage{ 7 | publish_news(news, volume, issue, home = get_articles_path()) 8 | } 9 | \arguments{ 10 | \item{news}{File path to the directory containing the news article to publish} 11 | 12 | \item{volume}{The volume of the article's issue (typically, year - 2008)} 13 | 14 | \item{issue}{The issue number of the article's issue} 15 | 16 | \item{home}{Location of the articles directory} 17 | } 18 | \description{ 19 | This function will publish a news article to the `rjournal.github.io` 20 | repository. 21 | } 22 | \seealso{ 23 | `publish_article`, `publish_issue()` 24 | } 25 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rj.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{\%>\%} 7 | \title{Objects exported from other packages} 8 | \keyword{internal} 9 | \description{ 10 | These objects are imported from other packages. Follow the links 11 | below to see their documentation. 12 | 13 | \describe{ 14 | \item{dplyr}{\code{\link[dplyr:reexports]{\%>\%}}} 15 | }} 16 | 17 | -------------------------------------------------------------------------------- /man/report.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/report.R 3 | \name{report} 4 | \alias{report} 5 | \title{Generate a status report.} 6 | \usage{ 7 | report(articles = active_articles(), editor = NULL) 8 | } 9 | \arguments{ 10 | \item{articles}{list of articles to generate report for. Defaults to 11 | all active reports in \file{Submissions/}.} 12 | 13 | \item{editor}{editor to generate report for. Defaults to all editors.} 14 | } 15 | \description{ 16 | This should be run weekly. 17 | } 18 | -------------------------------------------------------------------------------- /man/reviewer_status.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reviewers.R 3 | \name{list_reviewers} 4 | \alias{list_reviewers} 5 | \alias{reviewer_status} 6 | \title{Summarise reviewers' progression of an article} 7 | \usage{ 8 | list_reviewers(article) 9 | 10 | reviewer_status(article) 11 | } 12 | \arguments{ 13 | \item{article}{Article id, like \code{"2014-01"}} 14 | } 15 | \description{ 16 | This function summarises the status of reviewers who are willing to review 17 | for a particular article. 18 | } 19 | \examples{ 20 | \dontrun{list_reviewers("Submissions/2020-114")} 21 | \dontrun{ 22 | reviewer_status("Submissions/2020-114") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /man/reviewer_summary.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ae_workload.R 3 | \name{reviewer_summary} 4 | \alias{reviewer_summary} 5 | \title{Summarise the reviewer's agree/decline ratio based on past invites} 6 | \usage{ 7 | reviewer_summary(articles, push = FALSE) 8 | } 9 | \arguments{ 10 | \item{articles}{a tibble summary of articles in the accepted and submissions folder. Output of \code{tabulate_articles()}} 11 | 12 | \item{push}{whether the reviewer number of review completed by the reviewer should be pushed to the reviewer sheet} 13 | } 14 | \description{ 15 | The function pulls out the agree/decline incidence of all the reviewers 16 | based on past invite and calculate the agree percentage of each reviewer. 17 | Use \code{tabulate_articles} first to tabulate all the articles in a particular directory 18 | and then apply this function. 19 | } 20 | \examples{ 21 | \dontrun{ 22 | articles <- tabulate_articles() 23 | reviewer_summary(articles) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /man/rj.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rj.R 3 | \docType{package} 4 | \name{rj} 5 | \alias{rj-package} 6 | \alias{rj} 7 | \title{R Journal Package} 8 | \description{ 9 | This package provides useful functions for the editors of the R journal. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://rjournal.github.io/rj} 15 | } 16 | 17 | } 18 | \keyword{package} 19 | -------------------------------------------------------------------------------- /man/set_articles_path.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/path.R 3 | \name{set_articles_path} 4 | \alias{set_articles_path} 5 | \title{Set the directory of the articles repository} 6 | \usage{ 7 | set_articles_path(path) 8 | } 9 | \arguments{ 10 | \item{path}{Articles path} 11 | } 12 | \description{ 13 | This path is used to locate articles on your file system. If this path is not 14 | specified, the path will default to the root of the repository which contains 15 | the working directory 16 | } 17 | -------------------------------------------------------------------------------- /man/status.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/status.R 3 | \name{status} 4 | \alias{status} 5 | \title{Create a S3 status object} 6 | \usage{ 7 | status(status, date = Sys.Date(), comments = "") 8 | } 9 | \arguments{ 10 | \item{status}{A string description the status. Must be listed in 11 | \code{\link{valid_status}}} 12 | 13 | \item{date}{Date, defaults to today. Must be after 2002-01-01 and 14 | not in the future.} 15 | 16 | \item{comments}{any additional extra comments} 17 | } 18 | \description{ 19 | Create a S3 status object 20 | } 21 | \examples{ 22 | status("rejected") 23 | c(status("rejected"), status("accepted")) 24 | } 25 | \keyword{internal} 26 | -------------------------------------------------------------------------------- /man/summarise_articles.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/assignments.R 3 | \name{summarise_articles} 4 | \alias{summarise_articles} 5 | \alias{get_assignments} 6 | \alias{get_unassigned} 7 | \alias{find_articles} 8 | \alias{get_latest} 9 | \title{Summarise editors current in-tray} 10 | \usage{ 11 | summarise_articles(editor = NULL, rejected = FALSE, other = FALSE) 12 | 13 | get_assignments(editor, folder = "Submissions") 14 | 15 | get_unassigned() 16 | 17 | find_articles(editor, folder, role) 18 | 19 | get_latest(assignments) 20 | } 21 | \arguments{ 22 | \item{editor}{Editors initials. If \code{NULL}, looks for the 23 | environment variable \code{RJ_EDITOR}.} 24 | 25 | \item{rejected}{Default \code{FALSE}. If \code{TRUE}, show 26 | rejected articles.} 27 | 28 | \item{other}{Default \code{FALSE}. If \code{TRUE}, list all 29 | articles not covered by any of the other options (typically 30 | accepted and online)} 31 | 32 | \item{folder}{the folder to search for assignments, one of Submissions, Rejected, Accepted, or Proofs} 33 | 34 | \item{role}{string, take value of either "Editor" or "AE"} 35 | 36 | \item{assignments}{an output object from \code{get_assignments()} or \code{get_unassigned()}} 37 | } 38 | \description{ 39 | This function summarises and prints the articles an editor currently have in hand. 40 | It also prints out the articles that has not been assigned to any editor on the top, if any. 41 | If assigned to an object, the unassigned articles will appear on the top of the data frame. 42 | } 43 | -------------------------------------------------------------------------------- /man/tabulate_articles.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/article.R 3 | \name{tabulate_articles} 4 | \alias{tabulate_articles} 5 | \title{Tabulate article descriptions} 6 | \usage{ 7 | tabulate_articles(dirs = c("Accepted", "Submissions")) 8 | } 9 | \arguments{ 10 | \item{dirs}{The directories containing articles to be searched and tabulated. 11 | If TRUE, then all directories will be searched.} 12 | } 13 | \description{ 14 | Produce a table describing articles in specific directories. 15 | } 16 | -------------------------------------------------------------------------------- /man/time_to_accept_plot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/summary_plots.R 3 | \name{time_to_accept_plot} 4 | \alias{time_to_accept_plot} 5 | \title{Generates a plot of acceptance times for articles published in the last few years.} 6 | \usage{ 7 | time_to_accept_plot(years = NULL, save = TRUE) 8 | } 9 | \arguments{ 10 | \item{years}{years considered. A vector of years, or defaults to last four years.} 11 | 12 | \item{save}{Defaults to TRUE. The plot is saved in the rjournal.github.io/resources folder.} 13 | } 14 | \value{ 15 | a ggplot, one boxplot per publication year 16 | } 17 | \description{ 18 | Generates a plot of acceptance times for articles published in the last few years. 19 | } 20 | \examples{ 21 | \dontrun{ 22 | time_to_accept_plot() 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /man/update_status.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{update_status} 4 | \alias{update_status} 5 | \title{Update the article status} 6 | \usage{ 7 | update_status( 8 | article, 9 | status, 10 | comments = "", 11 | date = Sys.Date(), 12 | AE = is_AE(), 13 | replace = TRUE 14 | ) 15 | } 16 | \arguments{ 17 | \item{article}{Article id, like \code{"2014-01"}} 18 | 19 | \item{status}{new status to add, see details section for more} 20 | 21 | \item{comments}{Any additional comments} 22 | 23 | \item{date}{Date of status update. If omitted defaults to today.} 24 | 25 | \item{AE}{Logical, if \code{TRUE}, \code{"AE: "} is prefixed to the status} 26 | 27 | \item{replace}{logical, if the last status already matches \code{status} 28 | then the status is only updated if this flag is set to \code{TRUE}.} 29 | } 30 | \description{ 31 | This is a general function for updating the status field in the DESCRIPTION. 32 | } 33 | \details{ 34 | For AEs, status is prefixed with "AE: " and valid status includes 35 | "AE: major revision", "AE: minor revision", "AE: accept", and "AE: reject". 36 | 37 | For Editors, use \code{accept()}, \code{reject()}, and \code{withdraw()} to 38 | update the status as well as draft an email to the correspondence author. 39 | 40 | Check valid status with \code{valid_status}. 41 | } 42 | \examples{ 43 | \dontrun{ 44 | update_status("2020-114", status = "AE: major revision") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /man/valid_reviewer_status.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reviewers.R 3 | \docType{data} 4 | \name{valid_reviewer_status} 5 | \alias{valid_reviewer_status} 6 | \title{A list of all valid reviewer statuses.} 7 | \format{ 8 | An object of class \code{character} of length 9. 9 | } 10 | \usage{ 11 | valid_reviewer_status 12 | } 13 | \description{ 14 | A list of all valid reviewer statuses. 15 | } 16 | \examples{ 17 | valid_reviewer_status 18 | } 19 | \keyword{datasets} 20 | -------------------------------------------------------------------------------- /man/valid_status.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/status.R 3 | \docType{data} 4 | \name{valid_status} 5 | \alias{valid_status} 6 | \title{A list of all valid statuses.} 7 | \format{ 8 | An object of class \code{character} of length 26. 9 | } 10 | \usage{ 11 | valid_status 12 | } 13 | \description{ 14 | A list of all valid statuses. 15 | } 16 | \examples{ 17 | valid_status 18 | } 19 | \keyword{datasets} 20 | -------------------------------------------------------------------------------- /rj.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: e2de12be-3e36-49f5-acea-0d99544ac8f7 3 | 4 | RestoreWorkspace: No 5 | SaveWorkspace: No 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | LineEndingConversion: Posix 19 | 20 | BuildType: Package 21 | PackageUseDevtools: Yes 22 | PackageInstallArgs: --no-multiarch --with-keep.source 23 | PackageCheckArgs: --as-cran 24 | PackageRoxygenize: rd,collate,namespace 25 | -------------------------------------------------------------------------------- /tests/test-all.R: -------------------------------------------------------------------------------- 1 | library("testthat") 2 | test_check("rj") 3 | -------------------------------------------------------------------------------- /tests/testthat/test-actions.R: -------------------------------------------------------------------------------- 1 | context("Actions") 2 | 3 | test_that("Can't create invalid status", { 4 | valid <- load_article("valid-article.dcf") 5 | 6 | expect_error(update_status(valid, "blah"), "not a known status") 7 | expect_error( 8 | update_status(valid, "rejected", date = "2000-01-01"), 9 | "before the R journal" 10 | ) 11 | }) 12 | -------------------------------------------------------------------------------- /tests/testthat/test-id.r: -------------------------------------------------------------------------------- 1 | context("ID") 2 | 3 | test_that("Must have two components", { 4 | expect_error(parse_id("2005"), "must have form") 5 | expect_error(parse_id("2005-01-01"), "must have form") 6 | }) 7 | 8 | test_that("Components must be numeric", { 9 | expect_error(parse_id("2010-x"), "must have form") 10 | expect_error(parse_id("x-01"), "must have form") 11 | expect_error(parse_id("x-y"), "must have form") 12 | }) 13 | 14 | test_that("Year must be plausible", { 15 | expect_error(parse_id("2000-01"), "must be") 16 | expect_error(parse_id("3000-01"), "must be") 17 | }) 18 | 19 | test_that("No partial matches", { 20 | expect_error(parse_id("a2005-01"), "must have form") 21 | expect_error(parse_id("a2005-01a"), "must have form") 22 | }) 23 | 24 | test_that("Whitespace ignored", { 25 | expect_equal(parse_id("2002-01"), id(2002, 1)) 26 | expect_equal(parse_id("2002-01 "), id(2002, 1)) 27 | expect_equal(parse_id("\t2002-01"), id(2002, 1)) 28 | expect_equal(parse_id("\n2002-01"), id(2002, 1)) 29 | }) 30 | -------------------------------------------------------------------------------- /tests/testthat/test-rfc822.r: -------------------------------------------------------------------------------- 1 | context("rfc822") 2 | 3 | test_that("Addresses parsed into name and email", { 4 | expect_equal(parse_address("b [Blah]"), address("a", "b", "Blah")) 5 | expect_equal(parse_address(""), address("a")) 6 | expect_equal(parse_address("b"), address(NULL, "b")) 7 | 8 | expect_equal(parse_address(" b "), address("a", "b")) 9 | expect_equal(parse_address("\nb "), address("a", "b")) 10 | expect_equal(parse_address("b\n"), address("a", "b")) 11 | }) 12 | -------------------------------------------------------------------------------- /tests/testthat/test-status.r: -------------------------------------------------------------------------------- 1 | context("Status") 2 | 3 | test_that("parsing matches manual creation", { 4 | expect_equal( 5 | parse_status("2005-01-01 Accepted [test]"), 6 | status("Accepted", as.Date("2005-01-01"), "test") 7 | ) 8 | expect_equal( 9 | parse_status("2005-01-01 Accepted"), 10 | status("Accepted", as.Date("2005-01-01")) 11 | ) 12 | }) 13 | 14 | test_that("status must have at least two components", { 15 | expect_error(parse_status("2005-01-01"), "must have form") 16 | expect_error(parse_status("accepted"), "must have form") 17 | }) 18 | 19 | test_that("first component must be valid date", { 20 | expect_error(parse_status("2010-01-99 accepted"), "valid date") 21 | expect_error(parse_status("2010-02-30 accepted"), "valid date") 22 | expect_error(parse_status("2000-01-01 accepted"), "not before") 23 | expect_error(parse_status("3000-01-01 accepted"), "in the future") 24 | }) 25 | 26 | test_that("non-standard statuses give errors", { 27 | expect_error(parse_status("2010-01-01 accept"), "accepted") 28 | expect_error(parse_status("2010-01-01 minor"), "minor revision") 29 | }) 30 | -------------------------------------------------------------------------------- /tests/testthat/valid-article.dcf: -------------------------------------------------------------------------------- 1 | ID: 2010-01 2 | Title: Exploring the wonderful world of R 3 | Authors: 4 | "John Smith" 5 | Editor: Hadley Wickham 6 | Reviewers: 7 | "Marleen Harris" , 8 | "Roger Swansong" 9 | Status: 10 | 2012-05-04 submitted, 11 | 2012-05-18 acknowledged [optional comment], 12 | 2012-09-12 out for review, 13 | 2012-10-12 minor revision, 14 | 2012-11-12 accepted, 15 | 2012-12-12 proofed, 16 | 2013-01-01 published 17 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/conflict_of_interest.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Conflict of Interest" 3 | author: "Mark van der Loo" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Conflict of Interest} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ## What is a conflict of interest? 13 | 14 | For the R Journal, it is important that all decisions taken on papers are 15 | based as much as possible on objective arguments. A conflict of interest is 16 | any situation that may hamper the objectivity of such a decision. Examples 17 | are: 18 | 19 | - you are a collaborator in the work discussed in the submission; 20 | - you are a collaborator in a work that somehow competes with the submission; 21 | - one or more of the authors of the submission is a co-author or collaborator; 22 | - one or more of the authors are friends, family, or colleagues; 23 | - one of the authors has had influence on your professional life, for example via funding. 24 | 25 | ## What to do in case of conflict of interest? 26 | 27 | The most important principle is to be transparent. 28 | 29 | ### For Associate Editors 30 | 31 | If you are asked to handle a paper, and you feel there may be a conflict of 32 | interest, always consult the handling executive editor. The EE will decide 33 | whether someone else needs to handle the submission. Regardless of the 34 | decision, the notification and decision will be archived by the EE. 35 | 36 | ### For Editors 37 | 38 | If you are asked to handle a paper, and you feel there may be a conflict of 39 | interest, consult the Editor-in-Chief. The EE will decide whether someone else 40 | needs to handle the submission. Regardless of the decision, the notification 41 | and decision will be archived by the EE. 42 | 43 | ### For the Editor-in-Chief 44 | 45 | If a submission comes in and you feel there may be a conflict of interest, 46 | never handle the paper yourself. Hand it over to an EE and explain and archive 47 | the conflict of interest. 48 | 49 | ## How to archive a conflict of interest 50 | 51 | Conflicts of interest, and decisions surrounding conflict of interest should be 52 | described in a plain text file called `conflicts.txt` and stored in the 53 | `correspondence` folder with the submission. The description should at least contain: 54 | 55 | - Date 56 | - Reason for possible conflict of interest 57 | - Decision of the executive editor 58 | -------------------------------------------------------------------------------- /vignettes/editor_in_chief_guide.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Editor-in-Chief's Guide" 3 | author: "Di Cook and Rob J Hyndman" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Guide for the Editor-in-Chief} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>", 16 | echo = FALSE, 17 | warning = FALSE, 18 | message = FALSE 19 | ) 20 | ``` 21 | 22 | # Downloading new submissions and revisions 23 | 24 | Articles are submitted through a [Google form](https://forms.gle/ykj6QcoGQD5ctnA5A) which populates a [Google sheet](https://docs.google.com/spreadsheets/d/15Tem82ikjHhNVccZGrHFVdzVjoxAmwaiNwaBE95wu6k/edit#gid=1671813370), and a [Google drive](https://drive.google.com/drive/folders/0B2aFIue-Ar8dfmdVdHlpTnRndWhxQnNtR0o2YnoycDU1cVVzRlRaM0VSQWU1YUlnZlkyZEk?resourcekey=0-pzsHNE5mUiKuzB-VFmGD7g). This sheet contains details of the submission, and also a zip file with the necessary files; `get_submissions()` will authenticate against both. 25 | 26 | On a regular (at least weekly) basis, download newly submitted items: 27 | 28 | ```{r get, eval = FALSE, echo=TRUE} 29 | get_submissions() 30 | ``` 31 | 32 | This will create a new folder in the `Submissions` directory of the `articles` repo, and a `DESCRIPTION` file with the meta data. 33 | 34 | You should check that this file has been constructed correctly, and that the zip file has unpacked into the top level of the folder. Common missing items are the `Suppl:` line which should list the `.R` files and any data needed to reproduce the paper. 35 | 36 | # Check relevance and reproducibility 37 | 38 | First check that the paper fits the [scope of the journal](https://journal.r-project.org/#article-types). If not, `reject()` the paper. 39 | 40 | Then check that the paper is reproducible by either: (a) compiling the `Rmd` file; or (b) compiling both the LaTeX file and all `.R` files. If there are any problems, request a `resubmission()` from the authors. 41 | 42 | # Acknowledge submission and assigning article to an EE 43 | 44 | New articles should be assigned to one of the four EE's. This is good to do at the time of acknowledging the submission, so that the EE can be cc'd on the email. Use `acknowledge_submission(editor = "XX")`. 45 | 46 | To balance EE load, use `assignments()` to see how many papers each EE is currently handling, and what has been assigned to them in recent months. 47 | 48 | # Check progress of all papers 49 | 50 | The function `report()` shows the status of all current papers being handled, by author and by status. Anything with 3 stars needs urgent attention. 51 | 52 | 53 | # New executive editor 54 | 55 | The EIC is responsible to navigating the search for a new EE. The search for a new editorial board member should begin in September, giving enough time to find a replacement before the outgoing member leaves at the end of December. Nominations for the new member are discussed by the editorial board first, and then preferably also with the advisory committee so that they can provide historical perspective. 56 | 57 | Once a shortlist of candidates is created, but before approaching anyone on the list, it should be sent to the R Foundation Board members (R-foundation-board@r-project.org) for their feedback and approval. People on the list are then approached by the EIC or an EE, and once someone agrees, the EIC informs the members of the R Foundation (R-foundation-members@r-project.org). The new editor is formally appointed by the R Foundation president. 58 | 59 | Once a new member is found, the following steps take place: 60 | 61 | - The EIC informs Martin Maechler of the new board member 62 | - The incoming board member requests access to the mailing lists by visiting https://stat.ethz.ch/mailman/listinfo/r-journal-editors and https://stat.ethz.ch/mailman/listinfo/r-journal-advisory. 63 | - The outgoing member unsubscribes from r-journal-editors by visiting the web page above, but should remain subscribed to r-journal-advisory 64 | - The new EE is added to the Editors team at `https://github.com/rjournal` and to the `rjournal.slack.com` organisation. 65 | - The new EE is added to the `editors.csv` file in the `inst` folder of the `rj` package. 66 | 67 | The EIC may take on outstanding handling editor duties of the outgoing member. 68 | 69 | # New associate editors 70 | 71 | The EIC is responsible for recruiting new AEs, after discussion with all editors. Ideally, keywords of submissions over the past year are summarised, and compared with keywords of current AEs. New AEs should be recruited for topics where there is the most need. 72 | 73 | Once a new AE is appointed, the following steps take place: 74 | 75 | - The EIC informs the other EEs. 76 | - The EIC sets up a GitHub repo of the form ae-articles-xx where xx is the initials of the AE 77 | - The new AE is added to the `associate-editors.csv` file in the `inst` folder of the `rj` package. 78 | 79 | # Handover to new Editor-in-Chief 80 | 81 | * Update the [R Journal Wikipedia page](https://en.wikipedia.org/wiki/The_R_Journal) with the change of editors. 82 | * Ask Martin Maechler to forward r-journal@r-project.org to the new EIC's preferred email address. 83 | * Grant permission to the new EIC to access the [Google sheet](https://docs.google.com/spreadsheets/d/15Tem82ikjHhNVccZGrHFVdzVjoxAmwaiNwaBE95wu6k/edit#gid=1671813370), and [Google drive](https://drive.google.com/drive/folders/0B2aFIue-Ar8dfmdVdHlpTnRndWhxQnNtR0o2YnoycDU1cVVzRlRaM0VSQWU1YUlnZlkyZEk?resourcekey=0-pzsHNE5mUiKuzB-VFmGD7g) used for submissions. 84 | 85 | 86 | # Archives 87 | 88 | [Not done since 2019. Is it needed?] 89 | 90 | In the `articles` repo, the `Proofs` folder contains all the supporting files of Accepted articles. The `Rejected` folder contains all of the supporting files for rejected submisisons. 91 | 92 | From time to time, papers with dates older than two years should be moved to the `archive` repo, to make the `articles` repo smaller. Recommend that this is done at the hand-over of the EiC role at the end of each year. 93 | -------------------------------------------------------------------------------- /vignettes/editors_guide.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Editors' Guide" 3 | author: "Di Cook and Rob J Hyndman" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Operating procedures for the editors} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>", 16 | echo = FALSE, 17 | warning = FALSE, 18 | message = FALSE 19 | ) 20 | ``` 21 | 22 | # Mission 23 | 24 | Each Executive Editor (EE) of the R Journal is the primary article handler for about a third of submissions per year, responsible for: 25 | 26 | - matching the article with an appropriate Associate Editor (AE) or handling obtaining reviews without an AE. 27 | - communicating with AEs on submitted articles, and if necessary providing guidance to an AE about use of GitHub, and relevant `rj` functionality. 28 | - communicating progress and reviews with the corresponding author. 29 | - making a final decision if an article should be accepted, or rejected, or on an appropriate revision status, usually based on reviewer and AE feedback. 30 | - maintaining and enhancing the `rj` and `rjtools` software, in collaboration with other EEs and AEs. 31 | 32 | # Getting started 33 | 34 | Install the `rj` package with 35 | 36 | ```{r eval=FALSE, echo=TRUE} 37 | remotes::install_github("rjournal/rj") 38 | ``` 39 | 40 | The package is being updated and revised regularly, so you should re-install occasionally. 41 | 42 | # Emails using the `rj` package 43 | 44 | All communications with authors should be generated using the `rj` package to ensure correspondence is recorded in the GitHub `articles` repo. 45 | 46 | To ensure the email functions work correctly, please add the following line to your `.Rprofile` file. 47 | 48 | ``` 49 | options(browser=Sys.getenv('R_BROWSER')) 50 | ``` 51 | 52 | # Assignments 53 | 54 | The EIC will assign submissions to you. Notification is via an email to the author, cc'd to you. 55 | 56 | With each submission, you can decide to: 57 | 58 | * `reject()` the paper as unsuitable for the R Journal. 59 | * Choose to send it to an AE via `add_ae()`, or handle it yourself. 60 | * If you handle it yourself, please find suitable reviewers as per the [Associate Editor Guide](associate_editors_guide.html). 61 | 62 | Once you have received reports from reviewers or the AE, you can decide to: 63 | 64 | * `reject()` the paper 65 | * Request a `major_revision()` 66 | * Request a `minor_revision()` 67 | * `accept()` the paper 68 | 69 | ```{r wfgraph, fig.width=8, fig.align='center'} 70 | library(DiagrammeR) 71 | editor_df <- create_node_df(n=11, 72 | label=c("is this a new submission?", # 1 73 | "examine article: should the paper progress to review?", # 2 74 | "is there a suitable AE?", # 3 75 | "update status to be `with AE`, update_status(), copy folder to AEs repo", # 4 76 | "message/email AE", # 5 77 | "has AE returned reviews and decision?", # 6 78 | "add_review() to correspondence folder, send decision to author, \n with reviews if appropriate, update_status(), \n notify and thank AE about decision, if appropriate", # 7 79 | "is the revision adequate?", # 8 80 | "should it go back to AE?", # 9 81 | "reject(), contact authors", # 10 82 | "act as AE, find reviewers, update status, follow AE flow chart"), # 11 83 | shape = c("ellipse", "ellipse", "ellipse", 84 | "rectangle", "rectangle", 85 | "ellipse", "rectangle", "ellipse", 86 | "ellipse", "rectangle", "rectangle"), 87 | width = c(3, 5, 3, 5, 2, 3, 4, 3, 3, 3, 4), 88 | fillcolor = c("orange", "orange", "orange", 89 | "grey90", "grey90", 90 | "orange", "salmon", "orange", 91 | "orange", "salmon", "turquoise"), 92 | fontcolor = "black") 93 | 94 | editor_edge_df <- create_edge_df( 95 | from = c(1,2,3,4,5,6,1,8, 8,9,9, 2, 3, 6), 96 | to = c(2,3,4,5,6,7,8,9,10,7,4,10, 11, 5), 97 | label = c("Y", "Y", "Y", ".", ".", "Y", 98 | "N","Y","N", "N", "Y", "N", "N", "N"), 99 | color = c("dodgerblue2", "dodgerblue2", "dodgerblue2", 100 | "grey50", "grey50", "dodgerblue2", 101 | "red", "dodgerblue2", "red", "red", 102 | "dodgerblue2", "red", "red", "red")) 103 | 104 | editor <- create_graph(editor_df, 105 | editor_edge_df, 106 | directed = TRUE, attr_theme = "tb") 107 | render_graph(editor) 108 | ``` 109 | 110 | # Article assignment to AE 111 | 112 | The keywords of a paper should be matched against the keywords of AEs available in `associate-editors.csv`. Also, the function `ae_workload()` should be used to ensure that the potential AE hasn't got too many current assignments. 113 | 114 | A newly assigned article for an AE needs to have the directory copied from the `articles` repo to the relevant `ae-articles-XX`. The `articles` repo is the master copy, and once a paper is retrieved from an AE, the `DESCRIPTION` file is updated, and the `correspondence` folder is populated. 115 | 116 | Notify the AE of the assignment. Make sure the AE responds so you can be sure they are handling the submission. 117 | 118 | Chase up AEs who have been handling a submission for more than 3 months to check progress. 119 | 120 | # Checking progress 121 | 122 | You should check progress of papers you are handling at least weekly. 123 | 124 | * `report(editor = "XX")` where `XX` is your initials provides a convenient summary of papers allocated to you. Submissions with three stars need urgent attention. Submissions with two stars may also need attention. 125 | * `summarise_articles(editor = "XX")` provides a more detailed summary of each paper. 126 | * `late_aes(editor = "XX")` gives a list of AEs who have not submitted their reviews on time. 127 | * `late_reviewers(editor = "XX")` gives a list of reviewers who have not responded or submitted their reviews on time. 128 | 129 | # Communications 130 | 131 | Slack or email is used for communication, between EIC, EEs and AEs, and general information about operations. The channel `editors_private` can be used for protected conversations between EEs. 132 | 133 | Monthly virtual meetings are held to keep communication between editors current. 134 | 135 | Email is the primary manner for communicating with authors. The EE, not AEs, should communicate with authors. 136 | 137 | # Timeline for reviews 138 | 139 | This is what the acknowledgement letter tells authors about the timeline for their paper to be handled: 140 | 141 | > The submission process proceeds as follows: 142 | > 143 | > * review by editorial board & assignment of associate editor (~2-3 weeks) 144 | > * reviewers solicited (~2-4 weeks) 145 | > * reviews received (~2-3 months) 146 | 147 | # Dealing with problematic authors 148 | 149 | A few problematic authors are listed in `vexing_authors.csv` 150 | 151 | Please advise the EIC if you think anyone needs to be added to this list. 152 | -------------------------------------------------------------------------------- /vignettes/issue_build_guide.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Guide to Building an Issue" 3 | author: "Di Cook" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Guide to Building an Issue} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>", 16 | echo = FALSE, 17 | warning = FALSE, 18 | message = FALSE 19 | ) 20 | ``` 21 | 22 | 23 | # Articles to include 24 | 25 | All articles accepted up to the publication dates: Mar 31, Jun 30, Oct 31, Dec 31, should be included. (Traditionally these articles would first be made available online, but with the increased frequency of publication we are not at the moment performing this step.) 26 | 27 | # Preparation 28 | 29 | - Copy editing 30 | - Collect news items 31 | 32 | # Building an issue 33 | 34 | ## Steps 35 | 36 | 1. Copy from Accepted into Proofs (`rj:::make_proof()`): 37 | - It will filter accepted articles that have the current status "proofed". 38 | - You need to provide the issue number, eg "2022-2". 39 | - This will also create the zip file of supplementary material from the list of files in the DESCRIPTION 40 | 2. Copy news, News_items, into Proofs which is done manually. 41 | 3. Copy files across to `rjournal.github.io`, using `rj:::publish_issue()` or `rj:::publish_article()`. Publish news with `rj:::publish_news()` 42 | - This will also assign a "slug" the id of the published article, which is added to the DESCRIPTION file in the Proofs folder. 43 | - This slug will be used for the folder name in the `rjournal.github.io` "_articles" folder. 44 | - Also builds file `volume_issue.Rmd` in the "_issues". 45 | 4. Build the issue, using the file `volume_issue.Rmd` in the "_issues" folder. Make the pdfs/html for each article (using `rjtools::rjournal_web_issue()` and `rjtools::rjournal_pdf_issue()`) 46 | - It generates a folder within "_issues" with the information on organising the articles in the issue 47 | - This will also generate the `doi.xml` used for the DOI of each article. 48 | 5. Register DOIs with using username `rfou` and password provided by previous editor. Click on "Upload submissions", then "Metadata" and upload the file. 49 | -------------------------------------------------------------------------------- /vignettes/roles_and_responsibilities.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Roles and Responsibilities" 3 | author: "Mark van der Loo and Rob J Hyndman" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Roles and Responsibilities} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | This vignette gives an overview of the roles in the R Journal editorial team, 13 | the responsibilities that come with each role, and an overview of the editorial 14 | process. There are separate guides that detail the operational and technical 15 | details, in particular: 16 | 17 | - [Editor-in-Chief Guide](editor_in_chief_guide.html) 18 | - [Executive Editor Guide](executive_editors_guide.html) 19 | - [Associate Editor Guide](associate_editors_guide.html) 20 | 21 | ## Roles and responsibilities in the editorial team 22 | 23 | The editorial team consists of four Executive Editors (EE), one of which is the 24 | Editor-in-Chief (EIC). The EEs are supported by a team of Associate Editors. 25 | 26 | The team of EEs follow a rotating schedule. A new member will act as EE for 2 27 | years, takes the role of EIC in year 3, and returns to EE for their last year. 28 | 29 | AEs are in principle appointed for three years, but extensions are possible if 30 | both the EEs and AE agree. 31 | 32 | All members of the editorial team are expected to take appropriate action in the case of conflicts of interest. See the [conflict of interest policy](conflict_of_interest.html). 33 | 34 | ### Editor-in-Chief 35 | 36 | The EIC is responsible for: 37 | 38 | - production of four journal issues per year, one per quarter; 39 | - timely and proper handling of submitted papers; 40 | - distributing the article workload amongst the EEs; 41 | - ensuring there are always four EEs and sufficient AEs to handle the workload; 42 | - resolving conflicts between authors and the Journal, where necessary in collaboration with the advisory board; 43 | - maintaining the [R Journal website](http://journal.r-project.org); 44 | - maintaining the [`rj` package](https://rjournal.github.io/rj/) used by the editorial team to conduct operations for the R Journal; 45 | - organizing monthly meetings for the EE team, bimonthly meetings for the AE 46 | team, and an annual meeting with the advisory board. 47 | 48 | ### Executive Editors 49 | 50 | An EE is responsible for: 51 | 52 | - proper and timely handling of papers under their responsibility; 53 | - making the editorial decision (Accept/Minor/Major/Reject) for papers under their responsibility; 54 | - ensuring that both the reviews and the decision on submissions are properly argued, and that this argumentation is both archived and communicated to the authors; 55 | - communicating with the AE and the authors of submissions; 56 | - supporting the EIC in recruiting AEs and EEs. 57 | 58 | EEs may choose to hand over a paper to an AE but they will typically also 59 | handle some papers themselves. For those papers, the EE also takes on the 60 | responsibilities of an AE. 61 | 62 | ### Associate Editors 63 | 64 | An AE is responsible for: 65 | 66 | - finding and recruiting appropriate reviewers, such that there are preferably at least two reviews of sufficient quality for each submission; 67 | - ensuring that both the article contents and the code are sufficiently reviewed; 68 | - ensuring timely handling of papers under their responsibility; 69 | - advising the EE on the recommended editorial decision (Accept/Minor/Major/Reject). 70 | 71 | ---- 72 | 73 | ## Editorial Procedure 74 | 75 | When handling a new submission, there are several points of decision. Here we 76 | detail who takes each decision and in which order. The way decisions are made 77 | can be found in the AE, EE and EIC guides. 78 | 79 | When a (re)submission arrives 80 | 81 | 1. **The EIC decides whether it can be assigned to an EE.** The decision is based 82 | on technical checks, including completeness, formatting, and reproducibility. 83 | If the checks are not passed, the paper is rejected and the authors could be 84 | asked to possibly resubmit. If all checks pass, the EIC assigns the paper to an 85 | EE. 86 | 2. **The EE decides whether the submission is of sufficient quality for review.** 87 | 3. The paper is assigned to an AE or handled by the EE. 88 | a. Reviewers are recruited by EE or AE. They ensure that enough reviews of sufficient quality are obtained. 89 | b. If an AE handles the paper, the AE advises the EE on a decision. 90 | 4. **The EE decides whether the reviews are of sufficient quality to support an argued decision**. 91 | Reviews should be clear, objective, and together cover both the paper and the R code. 92 | 6. **The EE makes the editorial decision: Accept/Minor revision/Major revision/Reject**. 93 | The EE also archives the decision and communicates it with the authors. 94 | -------------------------------------------------------------------------------- /vignettes/website_build_guide.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Guide to Building the New Distill Web Site" 3 | author: "Di Cook" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Guide to Building the New Distill Web Site} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>", 16 | echo = FALSE, 17 | warning = FALSE, 18 | message = FALSE 19 | ) 20 | ``` 21 | 22 | 23 | # About the structure of the files 24 | 25 | During development the files for the web site are in the `dev` branch of the `github.com/rjournal/rjournal.github.io` repo. 26 | 27 | It is a distill web site. Content is in the `.Rmd` files: 28 | 29 | - `index.Rmd` contains the content linked to the HOME button 30 | - `submissions.Rmd` contains the content linked to the SUBMIT button 31 | - The `articles.Rmd` and `issues.Rmd` files contain R code to automatically generate the list of papers as available in the `_articles` and `_issues` folders. 32 | 33 | which are rendered using `rmarkdown`. The page styling can be modified by changing items in `theme.css`. 34 | 35 | # Build 36 | 37 | This is a static web site, so that each time you make a change to the content you should build the site locally, which will populate the `_web` folder. This folder contains the completed `html` and supporting files that deliver the final site. 38 | 39 | Within the RStudio IDE, "Build Website" can be used to render the site. It can also be scripted using: 40 | 41 | ``` 42 | rmarkdown::render_site() 43 | ``` 44 | 45 | The site can be viewed locally by opening `index.html` in your web browser. 46 | 47 | # Resources 48 | 49 | - [Building the site](https://rstudio.github.io/distill/website.html#building-the-site) 50 | - [Ways of publishing](https://rstudio.github.io/distill/publish_website.html) 51 | - [The Distillery](https://distillery.rbind.io/posts/2021-03-18-use-github-actions-with-r-markdown-and-distill/) 52 | --------------------------------------------------------------------------------