Skip to content

Automating my publications page

I wanted to create a new personal website using MkDocs, as my old website was based on Academic Pages, a Ruby-based template, and I am infinitely more familiar with Python. As part of this migration, I wanted a way to automate my Publications page from a BibTeX file.

The first part of the answer was to use a plugin: mkdocs-bibtex. This allows me to write Markdown code such as

@mypaper

which the plugin automatically converts into something like:

Y.-C. Chan, A. Mukherjee, N. Moretti, M. Nakaoka, *et al.*, "A process simulation model for a histopathology laboratory," in *2024 Winter Simulation Conference (WSC)*, Orlando: IEEE, 2024, pp. 846--857. doi: [10.1109/wsc63780.2024.10838731](https://doi.org/10.1109/wsc63780.2024.10838731)

Warning

As it turns out, mkdocs-bibtex will automatically convert reference labels even in code blocks, so I cannot use the actual label for this paper or any other paper in my BibTeX file. @ followed by anything not in my BibTeX file is fine, though.

Next, I needed to point MkDocs to my bibliography file and style file, in mkdocs.yml:

plugins:
  - bibtex:
      bib_file: yinchi.bib
      csl_file: ieee-with-url.csl

Generating the Markdown file

Then, I needed a script to convert my BibTeX file into a Markdown file. The pybtex Python package seems to do the job:

from pybtex.database import parse_file
import calendar
from pathlib import Path

bib = parse_file('yinchi.bib')

To sort my references chronologically, I first needed to extract the year and months, converting both into integers first:

def month(s: str | None):
    if s is None: return 0
    if s.title() in map_short: return map_short[s]
    elif s.title() in map_long: return map_long[s]
    else: return 0

def y_m(bib, key):
    y = int(bib.entries[key].fields['year'])
    m = month(bib.entries[key].fields.get('month', None))
    return (y, m)

d = {k: y_m(bib, k) for k in bib.entries.keys() if bib.entries[k].type not in ['patent']}
d = dict(sorted(d.items(), key=lambda i: i[1], reverse=True))

It was then just a matter of writing the sorted BibTeX entries to a file:

with open('docs/publications.md', 'w', encoding='utf-8') as fp:

    # Stuff that goes before the list
    print('# Publications', file=fp)
    print(file=fp)
    print('Google Scholar profile: [link](https://scholar.google.com/citations?user=NJEB3swAAAAJ)', file=fp)
    print(file=fp)

    # The publications list
    # Just use `1.` for each entry, Markdown will handle the numbering
    for k, v in d.items():
        print(f'1. @{k}')
        print(f'1. @{k}', file=fp)

        # Link to files if present
        path = f'papers/{k}.pdf'
        if Path(f'docs/{path}').is_file():
            print(f'    - [PDF]({path})')
            print(f'    - [PDF]({path})', file=fp)
        path = f'papers/{k}_slides.pdf'
        if Path(f'docs/{path}').is_file():
            print(f'    - [Slides]({path})')
            print(f'    - [Slides]({path})', file=fp)

Displaying my name in bold

Displaying my name in bold required a bit of DOM manipulation:

    print('''\

<script>
document.addEventListener('DOMContentLoaded', function() {
    document.querySelector('.md-content ol').innerHTML = document.querySelector('.md-content ol').innerHTML.replaceAll('Y.-C. Chan', '<b>Y.-C. Chan</b>')
})
</script>
''', file=fp)

The above works since the Publications page has only one <ol> element.

That’s it! The setup still has some quirks, such as pybtex/mkdocs-bibtex not parsing months in the .bib file correctly (other than May which doesn’t use an abbreviation), but it’s… usable, I guess?