CV autogeneration

For some time I’ve tried to make my CV’s in both languages (English and Russian) to be generated as automatically as possible.

So I’ve got two CVs: English and Russian version. They should represent pretty much the same information, so every time I update one of them I should mirror the changes into another one. It’s not that convenient and could lead to mistakes and differences between them. So I want to update them automatically and as simple as possible. Say, using a Python script to generate CV from some information (for each language) - essentially, a templating. The task is to load template from file, then load CV data and apply template to data in order to produce resulting CV.

Python comes with built-in template engine in string.Template module and even more powerful templating engines exits (such as, Jinja etc.). But for this task they might be to complex as all I need is to just subsitute some values in template. So I went with Python3 format function, which supports nested structures, arrays etc:

Tech skills: {skills.cpp}
Professional experience:
{job.position} at {job.organization}
* {job.achievement.feature}

Looks simple and usage is even simplier:

print(TEMPLATE.format(name, skills, job))

Now I need some way to store data. I considered different text storage format (XML, JSON etc.), but ended up using YAML as it is more easy to read and edit than JSON.

name: Name
	cpp: C++
	position: Developer
	organization: Organization
		feature: Added new feature to system.

Reading is simple (I use pyyaml module):

with open("data.yml", "r") as f:
	data = yaml.loads(f)

But result is a Python dict, not structure (as needed by format function). The most easy way to convert dict to structure is to use Python object’s __dict__ variable:

class Struct:

struct = new Struct()
struct.__dict__ = data

But we need to do the same thing for each level of dictionary. And it’s already too much code for simple needs. More convenient way is to use collections.namedtuple class:

Struct = namedtuple(data.keys())
struct = Struct(*data.values())

And do it recursively for each level of nesting:

def to_struct(data):
	if isinstance(data, dict):
		return namedtuple('Struct', data.keys())(*[to_struct(x) for x in data.values()])
	elif isinstance(data, list):
		return [to_struct(x) for x in data]
	return data

It gives Struct object with each complex member being either list of data (Struct or another objects) or Struct object again. Now applying template to resultin data struct looks like:


And produced output is looks like that:

Tech skills: C++
Professional experience:
Developer at Organization
* Added new feature to system.

Now data for each language can be stored in separate files. And on loading and applying template errors in data layout will be autodetected, so both data files would represent the same structure essentially.