Resume Build System

Resume generation system using Jinja to build multiple formats from a YAML database

Contents:

I developed a system to parse my resume from YAML-formatted data files and use templates to generate PDF, HTML, plain text, Sphinx, and Markdown output. I track all of my data files and templates in Git, which allows me to easily keep track of changes over time, tag versions customized for specific applications, etc. It is partly inspired by Ming-Ho Yee’s resume project. You can view the public portions of the project source code on its GitHub page, and you can view generated copies of my resume.

Motivation

For a long time, I maintained my resume in Microsoft Word, but that was a pain because

  • Much of the formatting information was hidden in the WYSIWYG interface, which made debugging formatting problems very frustrating, particularly on something like a resume where precise formatting is important. I used Word’s support for styles, tab stops, etc., and I am very familiar with Word, but it’s just a frustrating and buggy program.

  • Word does not support Linux, so when I switched from Windows to Linux, I had to use Word in a Windows virtual machine. I tried using LibreOffice before switching to my current approach, but LibreOffice has most of Word’s other problems.

  • There was no good way to track my changes, view differences between versions, etc., due to Word’s binary format. Of course, Word has built-in reviewing and diffing functionality, but it’s designed for reviewing documents, not long-term history tracking, and it doesn’t integrate nicely with Git.

  • There was no good way to produce other file formats (except PDF), which were necessary for some job applications and my personal website.

Now, I have complete control of formatting without it being hidden behind a GUI, I can build my resume on Linux, I can use Git for version control with understandable diffs, and I can easily produce whatever file format I need.

How it Works

A Python script takes as arguments the desired output format, the template to use, the output filename, and the source data file(s). It uses the PyYAML library to parse the YAML-formatted data file(s) into an in-memory data structure. If multiple data files are provided, the script merges the data together, which allows e.g. for separation between data to be published online and data to be included only on hard copies (such as an address or phone number).

Next, the script parses the section of the YAML-formatted configuration file corresponding to the desired output format. This includes

  • replacements to be applied to the raw data strings for escaping problematic characters (e.g. replacing & with & in HTML), applying simple markup (e.g. //emphasis// for emphasis), etc.

  • the delimiters to be used by the template system (because the default Jinja2 delimiters don’t work well with LaTeX)

  • the desired line endings for the output file (generally \n, but \r\n for the plain text file for MS Notepad users)

Finally, the script applies the replacements to the raw data strings and then uses Jinja2 to fill the data into the template. The script provides some custom filters to use in the templates for line wrapping, alignment, etc.

A Makefile provides instructions to build individual files or just rebuild all of them, so a simple call to make builds everything at once.

Input Files

This is a portion of a resume data file. It illustrates the YAML-formatted data structure and some of the markup specified in the configuration file. For example, -- is used to represent en dashes in date ranges, ~ is used to represent non-breaking spaces, and //emphasis// is used to emphasize award names.

summary:
  Mechanical engineering Ph.D.~student with research and industry experience in
  reinforcement learning, nonlinear dynamical systems, mechanical simulation,
  electromechanical controls, product development, testing and verification,
  data analysis, and software development.
education:
  - degree: Ph.D.~Mechanical Engineering
    university: Duke University
    location: Durham,~NC
    grade: 4.00~GPA
    date: 2015--present
    multicols: 2
    description:
      - "//2017 National Defense Science and Engineering Graduate (NDSEG) Fellowship:// Merit-based, national, full-ride fellowship"
      - "//2015 James~B.~Duke Fellowship:// Merit-based, four-year fellowship"
      - "//2015 Pratt/Gardner Fellowship:// Merit-based fellowship"
  - degree: B.S.~Mechanical Engineering
    university: North Carolina State University
    location: Raleigh,~NC
    grade: Valedictorian, 4.00~GPA
    date: 2011--2015
    multicols: 2
    description:
      - "//Minors:// Spanish & Computer Programming"
      - "//2015 NCSU Mech.~& Aero.~Engineering Senior Award for Leadership//"
      - "//2014 Goldwater Scholar:// Merit-based, national scholarship"
      - "//2011 NCSU Park Scholar:// Merit-based, full-ride scholarship"

This is a portion of a configuration file for LaTeX output:

latex:
  replacements:
    - ['&', '\\&']
    - ['%', '\\%']
    - ['\$', '\\$']
    - ['LaTeX', '\\LaTeX']
    - ['//(.*?)//', '\\emph{\1}']
  jinja2_delim: ['%<', '>%', '<<', '>>', '%%<', '>%%']
  line_endings: "\n"

This is a portion of a template for plain text output. It illustrates some of the features of the Jinja2 templating language, including filters, conditionals, and loops.

{{ "#" * (contact.name.first + " " + contact.name.last)|length }}
{{ contact.name.first }} {{ contact.name.last }}
{{ "#" * (contact.name.first + " " + contact.name.last)|length }}

{% if contact.address is defined and contact.address.street is defined and contact.address.city is defined %}
Address: {{ contact.address.street }}{{ contact.address.city }}
{% elif contact.address is defined and contact.address.city is defined %}
Address: {{ contact.address.city }}
{% endif %}
{% if contact.address is defined and contact.address.country is defined %}
         {{ contact.address.country }}
{% endif %}
{% if contact.phone is defined %}
Phone:   {{ contact.phone }}
{% endif %}
{% if contact.email is defined %}
Email:   {{ contact.email }}
{% endif %}
{% if contact.linkedin is defined %}
Social:  linkedin.com/in/{{ contact.linkedin }}
{% endif %}
{% if contact.web is defined %}
Web:     {{ contact.web }}
{% endif %}


Summary
=======

{{ summary|hard_wrap(WIDTH) }}


Education
=========

{{ entrylist(education) }}

Publications & Presentations
============================

{% for item in publications %}
{{ item.text|hard_wrap(WIDTH,0,"",2) }}

{% endfor %}

More Information

If you’d like to see the specifics of usage, dependencies, license, etc., see the GitHub page. The project is published under fairly open licenses, so you can adapt it for your own use.

You can view the Markdown output for my resume integrated into this site, or you can download my resume as PDF or my resume as plain text.