Resumess
A big part of getting a job is having a resume, right? I mean, only very rarely does the job fairy poke her head out of her enchanted cubicle to magically bestow a career; usually, you've gotta get your name and selected marketable skills out there.
So, how does one put a resume together? Not the actual content; that, obviously, comes from a lifetime of experiences and liberal use of a thesaurus. No, instead we must ask by what means one assembles this sacred text.
Microsoft Word is great; a lot of very smart people have spent a great deal of time and energy making a semi-prescient typewriter. Microsoft Word is also not on my computer. Microsoft Word also costs the sort of money one needs a job to spend.
Google's Docs are a jaw-dropping vision of the future. An entire in-browser text editor? With tables? It might as well be a flying car. However, as staggeringly tech-mazing Docs are, they fall a bit short when it comes to styling. You can't put rims on a flying car.
Open Office is, well, ugh. Which is not entirely fair because, like, it's a tremendously ambitious undertaking and, as an open source project, what's there is pretty god damn impressive. At the same time, Open Office's tables are an inescapable hell full of scowling imps who take turns pulling out your fingernails. Ugh.
Cascading Style Sermons
Having now worked on, all modesty aside, like, a bunch of websites, I've got a pretty good handle on HTML and CSS. Okay, it's not that I understand CSS exactly, but I have a pretty decent feeling for how much I don't know and exactly how much misery working with it is going to cause me.Besides, I kinda grok the box model, so that's something.
So, there's a couple reasons for me to use HTML and CSS for my resume:
- Prior experience.
- The precision of a declarative language.
- An expressive vocabulary for styling things.
- There's an internet full of solutions to common problems.
Similarly, I could use Markdown. Hell, I'm a huge fan of it - it's what I use for this blog you're reading right here. However, adding classes to elements means dropping down into raw HTML and since I'd end up doing that pretty often, I might as well go full HTML.
Okay. So. Whatever. HTML and CSS. How do we get a PDF out of that?
Wkhtmltopdf
There are a few utilities that suck up HTML and CSS and spit out a PDF, but after trying out a couple, wkhtmltopdf seemed like the best. Importantly, it faithfully adhered to all the CSS rules rather than some random subset.It's super easy to use, too:
wkhtmltopdf tom-gibson-resume.html tom-gibson-resume.pdf
Groovy. So, easy enough to go from A to B. Write up the resume and we're good to go.
And that's just what I was doing, writing up the resume, when I started to get frustrated. See, a resume has a lot of similarly-structured pieces, right? For example, every job in your work experience will have the same format. Working with raw HTML, this meant a lot of copying and pasting.
Obviously, that's no good. It's a hassle, for one, but what's worse, if you want to change that structure - e.g., add a class - you've got to go through and edit every single instance. Blarg. If only we had a means of separating HTML structure from content.
Oh, wait. There's about eight million HTMl templating libraries that solve exactly this problem.
Jsontemplate
Even in Python alone, there are a ton of templating options. I narrowed this down with two criteria:- It could be drop-dead simple. No filters, no in-template logic.
- I wanted to use JSON for my data because, well, I'm familiar with it.
This lead to JSON Template. This is actually a really lovely solution because it's very, very simple and focused on the structure of JSON. There are basically a couple of rules:
- A property specified with
{property}
gets replaced by that property's value. {.section property}
and{.repeated section property}
are used instead ofif
and looping.{.or}
handles an absent property.
template.html
:
<div class="jobs">
{.repeated section jobs}
<div class="job">
<div class="info">
<div class="main-info">
<div class="position">{position}</div>
<div class="company">{company}</div>
</div>
<div class="duration">{duration}</div>
</div>
<ul class="description">
{.repeated section description}
<li>{@}</li>
{.end}
</ul>
</div>
{.end}
</div>
Then, in data.json
, we get the corresponding data:
{
"jobs": [{
"position": "Software Developer",
"company": "Seeker Solutions",
"city": "Victoria, BC",
"duration": "September - December 2012",
"description": [
"Built tools to aggregate comments from social media sites like Twitter and Facebook.",
"Designed a system which used natural language processing to analyze brand reputations based on trends in this user data.",
"Created an interface to explore the results of the analysis using Google Web Toolkit.",
"Coordinated Scrum-based development with JIRA and Jenkins."
]
}]
}
A little bit of python ties them together:
with open("template.html", "r") as f:
template = f.read()
with open("data.json", "r") as f:
data = json.loads(f.read())
result = jsontemplate.expand(template, data)
with open("tom-gibson-resume.html", "w") as f:
f.write(result)
os.system("wkhtmltopdf tom-gibson-resume.html tom-gibson-resume.pdf")
Boom. Electing for HTML and CSS gets us templating basically for free. How great is that? Now we can separate content from structure and style. If that doesn't put a smile on your face, you might need to see a shrink.
At this point we're ready to start writing the actual resume, right? I mean, what else?
Well, okay, it's kind of a bummer that we have to manually run that compilation script every time. Hmm. I bet we can do something about that.
Watchdog
Turns out there's a handy library called Watchdog that lets us watch for and react to file modifcations. Hardly a surprise, I guess. It's Python; there's always a library.Anyway, tweaking one of Watchdog's examples gets us something that does the job. Moving the compilation stuff into a run
function, we can write a handler that recompiles when one of the important files changes:
class RecompileHandler(FileSystemEventHandler):
def on_modified(self, event):
index = event.src_path.rfind("/") + 1
name = event.src_path[index:]
if name in ["data.json", "template.html", "style.css"]:
run()
We'll use argparse
to check for a --watch
option. If we find it, we'll go ahead and watch for modifications.
parser = argparse.ArgumentParser()
parser.add_argument("--watch", "-w", action="store_true")
args = parser.parse_args()
So, if we're watching, get a RecompileHandler
going:
run()
if args.watch:
event_handler = RecompileHandler()
observer = Observer()
observer.schedule(event_handler, ".")
observer.start()
try:
while True:
time.sleep(0.1)
except KeyboardInterrupt:
observer.stop()
observer.join()
Awesome. Now we can go edit our resume files and this script will take care of compiling everything in the background. Even cooler, Ubuntu's document viewer reloads when the PDF changes, so we can watch as the document updates in real time while we edit!
So, with a little bit of effort, we, uh, we created a dinky little Microsoft Word knockoff. Mm. Well, at least we get to use CSS? Yay.