Skip to content

Instantly share code, notes, and snippets.

@jhollist
Last active March 14, 2018 21:49
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jhollist/abd8a7da543f6230096ec035fc195ab2 to your computer and use it in GitHub Desktop.
Save jhollist/abd8a7da543f6230096ec035fc195ab2 to your computer and use it in GitHub Desktop.
An R function to turn a vector of package names into a bibliorgraphy
#' Generate a bibliography of packages
#'
#' This function creates an output bibliography from an input character vector
#' of package names. The primary use case for this function is when working
#' with other authors and you need to provide a reference list of packages used.
#'
#' @param pkgs A character vector of packages
#' @param csl A csl file used to format the output bibliography. Good place to
#' look for these is \url{https://github.com/citation-style-language/styles}
#' @param append_bib Existing \code{.bib} file to append package citations to.
#' Defaults to NULL
#' @param keep_intermediate A logical indicating if you would like to keep the
#' intermediate \code{.bib} and \code{.md} files. Most useful if you would
#' like to get an output \code{.bib} or need to troubleshoot. Default is
#' FALSE.
#' @param ... Used to pass arguments to \link[rmarkdown]{render}. Most useful
#' for changing output format (e.g. \code{output_format = "word_document"}) or
#' output file name.
#' @export
#' @examples
#' download.file("https://raw.githubusercontent.com/citation-style-language/styles/master/plos.csl",
#' destfile = "plos.csl")
#' bibify_pkgs(c("dplyr","readr"),"plos.csl", output_format = "word_document",
#' output_file = "my_package_bib.docx")
bibify_pkgs <- function(pkgs, csl, append_bib = NULL, keep_intermediate = FALSE, ...){
if(!is.null(append_bib)){
tmpbib <- append_bib
con <- file(append_bib, "a+", encoding = "UTF-8")
} else {
tmpbib <- paste0(tempfile(tmpdir = "."), ".bib")
con <- file(tmpbib, "w+", encoding = "UTF-8")
}
for(i in pkgs){
ref <- toBibtex(citation(i))
#adds an id to bib entry
ref[1] <- gsub("\\{,", paste0("{", i, ","), ref[1])
#Edit title to maintain capitalization and adds version number.
ref[2] <- gsub("\\{", "{{", ref[2])
ref[2] <- gsub("\\}", paste0("(v ",packageVersion(i),")}}"), ref[2])
writeLines(ref, con)
}
close(con)
tmpmd <- "references.md"
con <- file(tmpmd, "w+", encoding = "UTF-8")
lines <- paste0("---\n",
"bibliography: ", tmpbib, "\n",
"csl: ", csl, "\n",
"nocite: |\n",
" @*\n",
"---")
writeLines(lines, con)
close(con)
rmarkdown::render(tmpmd, ...)
if(!keep_intermediate & is.null(append_bib)){
del <- suppressWarnings(file.remove(c(tmpbib, tmpmd)))
}
}
@HeidiSeibold
Copy link

Cool! I did something similar. Maybe might help you: I take a current bib-File, extract all package names and get the current citation info.

### Generate bibtex entries as does citation(), but from GitHub 
### https://github.com/cran
### to avoid having to install the packages

### Just run:
# pkgs <- get_cited_packages("ref.bib")
# citations <- lapply(pkgs, get_citation, .token = NULL)

### If the citation is Text only you can use toBibtex()
### just as you would do for results of citation()
# toBibtex(citations[[1]])

### If you download a lot, use a token:
### https://github.com/blog/1509-personal-api-tokens


library("gh")
library("bibtex")

## extract cited packages (\pkg{}) from bibtex file
get_cited_packages <- function(bibfile = "ref.bib") {
  bib <- read.bib(bibfile)
  titles <- sapply(bib, function(x) x$title)
  pkgs0 <- regmatches(titles, gregexpr("(?<=pkg\\{).*?(?=\\})", 
                                       titles, perl = TRUE))
  pkgs <- unlist(pkgs0)
}


## get citation from CITATION file
citation_from_citation <- function(gh_cit) {
  
  # download file and save as tmp_citation
  filenam <- tempfile()
  download.file(url = gh_cit$download_url, filenam)
  
  # read file
  ret <- readCitationFile(filenam)
  
  return(ret)
  
}




## get citation from DESCRIPTION file
citation_from_description <- function(repo, package, .token = NULL) {
  
  # get path to DESCRIPTION file from GitHub
  gh_descr <- try(gh("GET /repos/:repo/contents/:path",
                     repo = repo,
                     path = "DESCRIPTION",
                     .token = .token))
  if(class(gh_descr)[[1]] == "try-error") 
    return(paste(package, 
                 "does not exist on https://github.com/cran or you accessed API too many times (try using .token)"))
  
  # download file and save as tmp_description
  filenam <- tempfile()
  download.file(url = gh_descr$download_url, filenam)
  
  # read file
  dcf <- read.dcf(file = filenam)
  meta <- as.list(dcf[1, ])
  
  # extract needed information
  year <- sub("-.*", "", meta$`Date/Publication`)
  author <- meta$Author
  z <- list(title = paste0(package, ": ", meta$Title), author = author, 
            year = year, note = paste("R package version", meta$Version))
  z$url <- sprintf("https://CRAN.R-project.org/package=%s", 
                   package)
  
  # generate bibtex entry
  ret <- utils:::.citation(
    bibentry(bibtype = "Manual", 
             textVersion = paste0(author, " (", z$year, "). ", 
                                  z$title, ". ", z$note, ". ",z$url), 
             other = z)
  )
  
}




## get bibtex citation
get_citation <- function(package = "igraph", .token = NULL) {
  
  message("Getting citation for package ", package)
  
  # get path to CITATION file from GitHub
  repo <- paste0("cran/", package)
  gh_cit <- try(gh("GET /repos/:repo/contents/:path",
                   repo = repo,
                   path = "inst/CITATION", 
                   .token = .token),
                silent = TRUE)
  
  # if no CITATION file available, use DESCRIPTION file instead
  if(class(gh_cit)[[1]] == "try-error") {
    ret <- citation_from_description(repo = repo, package = package,
                                     .token = .token)
  } else {
    ret <- citation_from_citation(gh_cit = gh_cit)
  }
  
  return(ret)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment