How to create pages from a bibtex file with Quarto

quarto
Published

September 22, 2023

I have a bibtex file with all my publications downloaded for the journal websites, so it includes all the data available (title, authors, abstract, etc).

For this website, I wanted to list my publications using this file instead of doing it manually. Here are the steps I followed:

QMD template

This file will be rendered for each publication to generate one qmd file per paper. The values are passed with the params object. As we keep this file into the publications folder, we put an underscore at the start of the filename (**_pubtemplate.qmd**) so Quarto does not add it to the list of publications.

---
params:
  authors: null
  abstract: null
  ref: null
  link: null
  year: null
engine: knitr
---

`r params$abstract`

[Full publication](`r params$link`)

#### Reference

`r params$ref`

---
authors: "`r params$authors`"
year: "`r params$year`"
---

Iteration through bibtex

With bibtex and bib2df, we read and convert bibtex file to a dataframe.

library(bibtex)
library(bib2df)

# Read bibtex for references
biblio <- read.bib("publications.bib")

# Converting bibtex to DF to loop into
biblio.df <- bib2df("publications.bib")
biblio.df <- biblio.df[biblio.df$CATEGORY %in% c("ARTICLE","MISC","INCOLLECTION"),]

Then we iterate through each entry and render it

for(i in 1:nrow(biblio.df)) {
    quarto_render(
      input = "_pubtemplate.qmd", 
      output_file = paste0(biblio.df$BIBTEXKEY[i],".qmd"), 
      output_format = "markdown", 
      metadata = list(title = biblio.df$TITLE[i]),
      execute_params = list(
        authors = paste0(unlist(biblio.df$AUTHOR[i]), collapse = "; "), 
        abstract = biblio.df$ABSTRACT[i], 
        ref = format(biblio[biblio.df$BIBTEXKEY[i]], style = "text"), 
        link = biblio.df$URL[i],
        year = biblio.df$YEAR[i]
      )
    )
}

Script optimization

The script checks the last modification dates to not render again entries without modifications, so the rendering is faster.

Notice that when the flag run.again is set to TRUE, the script will always render the qmd files. Set it to FALSE to render only new entries.

Here is the final result:

library(bibtex)
library(bib2df)
library(xfun)

# This flag forces to generate again the qmds
run.again <- TRUE

# Read bibtex for references
biblio <- read.bib("publications.bib")

# Converting bibtex to DF to loop into
biblio.df <- bib2df("publications.bib")
biblio.df <- biblio.df[biblio.df$CATEGORY %in% c("ARTICLE","MISC","INCOLLECTION"),]

# bibtex time stamp
bib.mtime <- file.mtime("publications.bib")

for(i in 1:nrow(biblio.df)) {
  # We don't render qmd files if they are more recent than the bibtex file
  if (file_exists(paste0("publications/",biblio.df$BIBTEXKEY[i],".qmd"))) {
    if(bib.mtime < file.mtime(paste0("publications/",biblio.df$BIBTEXKEY[i],".qmd")) & !run.again) {
      next
    }
  }
  # quarto_render needs to be executed in the dir where the files are
  # that's why we use in_dir
  in_dir(
    "publications",
    quarto::quarto_render(
      input = "_pubtemplate.qmd", 
      output_file = paste0(biblio.df$BIBTEXKEY[i],".qmd"),
      output_format = "markdown", 
      metadata = list(title = biblio.df$TITLE[i]),
      execute_params = list(
        authors = paste0(unlist(biblio.df$AUTHOR[i]), collapse = "; "), 
        abstract = biblio.df$ABSTRACT[i], 
        ref = format(biblio[biblio.df$BIBTEXKEY[i]], style = "text"), 
        link = biblio.df$URL[i],
        year = biblio.df$YEAR[i]
      )
    )
  )
}

We save it as _build.qmds.R in the root directory.

Wrap it up

Folder structure

In the root directory, we have the following files:

  • publications.bib: bibtex where is all the info on the papers
  • publications.qmd: listing of publications (see below)
  • _build.qmds.R: the script to create qmd for each bibtex entry

In the publications directory, we have:

  • _pubtemplate.qmd: the template to render
  • one qmd file for each bibtex entry once the script is executed

To run the script before rendering the website, we modify the _quarto.yml file as follows:

project:
  type: website
  pre-render: _build.qmds.R

And create a file called publications.qmd to list publications with the following code:

---
title: "Publications"
listing:
  type: table
  contents: publications
  fields: [year,title,authors]
  sort-ui: [title,year]
  sort: "year desc"
  field-display-names:
    title: "Title"
    year: "Year"
    authors: "Authors"

---

That’s it!