Compare commits

...

9 commits

Author SHA1 Message Date
72d40e5094
ref(smartypants): Move smartypants application to lib 2025-03-19 12:53:39 +01:00
df480875df
ref(lib): Rename skill_item to sidebar_entry
We use the function for all entries in the sidebar (volunteering,
languages, etc) not just skills.
2025-03-19 12:53:38 +01:00
c831b008b9
ref(resume): Move by_client and by_experience_type to lib
In the continuous process of moving everything away from the main resume
body, move these experience functions away.
2025-03-19 12:53:38 +01:00
37c59e71db
docs: Update README
Give light hints to how the section arrays work.
2025-03-19 12:53:37 +01:00
1a62b90df7
feat(resume): Add ability to reorder sections
Both in main and in sidebar, sections can be reordered simply by
changing the order they appar in the array adding them.

`#resume.with(main:("education", "experience"))` displays education
before experience items.
2025-03-19 12:53:37 +01:00
f60991344d
feat(resume): Choose experience display by client,type,chronological
Can be set by setting the point in 'main' array to
'experience_by_client', 'experience_by_type' or just 'experience'
respectively.
2025-03-19 12:53:36 +01:00
0dd715d806
fix(content): Rephrase ZeitRaum support consultations 2025-03-19 12:53:36 +01:00
948510fb13 Move element styling and smartypants to lib 2025-03-17 12:07:40 +01:00
b333e2f2db
ref(letter): Remove quarto letter 2025-03-17 12:07:40 +01:00
6 changed files with 170 additions and 176 deletions

View file

@ -18,6 +18,28 @@ Is called like the following:
```
This is the default invocation, though sidebar and main body sections can be exchanged at will.
The following sections currently exist:
- education
- experience, subdivided in "experience", "experience_by_client" and "experience_by_type"
- languages
- skills
- summary
- volunteering
Sections in the main body or sidebar can be reordered at will.
## Advanced experience settings
The experience section has 3 forms:
Purely chronological ("experience"), which is the default;
separated by client worked for ("experience_by_client");
separated by the type of work undertaken, then further separated by client worked for ("experience_by_client").
These options are intended especially for self-employed / entrepreneurial CVs.
They let you subdivide your work experience whichever way works best,
and additionally divide work undertaken for your own employ or salaried positions for example.
TODO:
@ -42,4 +64,4 @@ TODO:
- [x] Can be switched between Ger/En
- [x] Try sidebar version
- [ ] Generalize sidebar version through abstraction/extractions
- [ ] unify items: experience/education/(thesis?)
- [x] unify items: experience/education/(thesis?)

View file

@ -386,8 +386,8 @@ volunteering:
en: Transferring Digital Competence in Aging
- de: Wöchentlicher Workshop zur Entwicklung von Selbstvertrauen und Kompetenz mit Smartphones
en: Weekly workshop on developing confidence and competence with smartphones
- de: Personalisierte technische Hilfsmeetings und technische Support-Beratungen
en: Personalized tech assistance appointments and technical support consultations
- de: Personalisierte technische Unterstützung und personalisierte Einzelberatungen
en: Personalized tech assistance appointments and individual technical support consultations
- title:
de: Verpixelt
en: Verpixelt

10
cv.typ
View file

@ -1,6 +1,8 @@
#import "lib.typ": *
#let cv(contents, use_sidebar: false) = {
show: style
show: smartypants
set text(lang: lang)
let date_formatting = {
@ -63,7 +65,7 @@
section(
title: title,
{
skill_item(item: contents.skills)
sidebar_entry(item: contents.skills)
},
)
}
@ -73,7 +75,7 @@
section(
title: title,
{
skill_item(item: contents.languages)
sidebar_entry(item: contents.languages)
},
)
}
@ -95,14 +97,14 @@
if "languages" in contents {
let title = (en: "Languages", de: "Sprachen").at(lang)
[== #title]
skill_item(item: contents.languages, is_sidebar: true)
sidebar_entry(item: contents.languages, is_sidebar: true)
[\ ]
}
if "skills" in contents {
let title = (en: "Qualifications", de: "Kenntnisse").at(lang)
[== #title]
skill_item(item: contents.skills, is_sidebar: true)
sidebar_entry(item: contents.skills, is_sidebar: true)
}
}

View file

@ -1,53 +0,0 @@
---
backaddress: Marty Oehme, Körnerstraße 54, 04107 Leipzig
fromname: Marty Oehme
fromaddress: |
Körnerstraße 54
04107 Leipzig
fromphone: "0177 / 377 49 49"
place: Leipzig, DAY MONTH YEAR
placeseparator: " "
sendto: |
NAME OF COMPANY
ITS ADDRESS STREET AND NUMBER
LOCATION ZIPCODE
lang: en
subject: Bewerbung für JOBSTELLE
signature: Marty Oehme
opening: Dear CONTACTNAME
closing: Sincerely
format:
pdf:
template: templates/letter.latex
---
After two years of cooperation, I recently finished the final in a row of projects undertaken as consultant for UNU-WIDER and the ILO.
I am glad to have happened upon your job posting for the position of project officer as part-time worker with minimum 32 hours, as for me it may allow a more medium term stability than the previous short-term freelance positions did.
Over the previous two years, I have primarily provided research assistance for three major projects under the helm of the UN, the ILO and Roskilde University:
In addition to the descriptive analysis undertaken for all of these projects,
my consultancy for UNU-WIDER required the creation of a time-series visualization on the basis of empirical analysis over roughly 200.000 observations within the UN's WIID dataset.
The previous work for the ILO and Roskilde University instead consisted of deep research for the creation of wide-ranging scoping reviews,
which necessitated collecting, organizing and cleaning datasets of around 2000 source potentials.
I accomplished both of these tasks more efficiently with the help of Python and its data analysis modules which is also where my primary statistical programming focus lies.
Additionally, I have long been helping publish academic, particularly empirical, works,
first in the role of academic assistant and later on as editorial consultant.
Perhaps most personally beneficial for the current position was the publication of a large academic anthology under the SPIWORK project:
During this time I worked with clients located internationally,
collaborating directly to create clear and concise manuscripts for each individual topic,
before merging the divergent aspects into a final coherent whole while also fulfilling publisher's formal requirements.
I would love to bring both the analytical and editorial skillsets together in my new work,
to be able to push them forward in tandem.
Lastly, my skills of communication, especially international communication skills, have been honed throughout this time as I was both participant and leader of small (from 3 people) to medium-sized (up to 8 people) teams.
Collaboration in this way was both exciting and straightforward for me,
and I firmly believe concise and clear communication both engenders easier productivity flows
and is fundamental to the ability to precisely frame topics as complex as energy transition.
My interactions with processes of both long-term energy planning and renewables-based electrification of end-use sectors primarily stem from research undertaken in Vietnam and Benin under the helm of the Agence française de développement,
where the eventual distributional impact of access to clean water and new energy grids provided the project's focus.
I have, however, both the willingness and curiosity to dive deeper into these topics and their related issues of regulatory frameworks, financing and policy framing.
I will be happy to hear back from you and am confident that I could be a good match.
Should you agree that my profile is a good fit for the offered position then do not hesitate to contact me and I would be delighted to arrange a meeting.

91
lib.typ
View file

@ -1,10 +1,20 @@
#show heading: set text(font: "New Computer Modern")
#show link: underline
// smartypants and latex compatibility
#show "--": [#sym.dash.en]
#show "---": [#sym.dash.em]
#show "\&": [#sym.amp]
// transform md-similes to actual symbols
#let smartypants(it) = {
// smartypants and latex compatibility
show "--": [#sym.dash.en]
show "---": [#sym.dash.em]
show "\&": [#sym.amp]
it
}
// set some styles
#let style(it) = {
show heading: set text(font: "New Computer Modern")
show link: underline
show: smartypants
it
}
// Choose the compiled language through cli by doing
//
@ -81,6 +91,24 @@
}
}
#let sidebar_entry(item: (), is_sidebar: false) = {
let side_list(body) = if is_sidebar { list(body) } else { par(body) }
for skill in item {
side_list({
[*#skill.name.at(lang)*]
if is_sidebar [\ ] else [ (]
for (i, v) in skill.items.enumerate() {
[#v.at(lang)]
if i < skill.items.len() - 1 {
[, ]
}
}
if not is_sidebar [)]
})
}
}
#let horizon_line() = {
v(-3pt)
line(length: 100%)
@ -103,6 +131,39 @@
}
};
// TODO: make it _return_ the data, not display it on its own
#let by_client(experience: ()) = {
let by_client = (:)
for item in experience {
let client = item.place.at(lang)
if client not in by_client {
by_client.insert(client, ())
}
by_client.at(client).push((title: item.title.at(lang), date: item.date.at(lang)))
}
for (client, jobs) in by_client {
[*#client*:]
for j in jobs {
[- #j.title #h(1fr) #j.date]
}
}
}
#let by_experience_type(type: (), experience: ()) = {
let by_ty = (:)
for (id, desc) in type {
let matching_exp_items = experience.filter(item => int(item.typeid) == int(id))
if matching_exp_items.len() == 0 {
return
}
[=== _#desc.at(lang)_]
by_client(experience: matching_exp_items)
}
}
// Slightly re-styled entry with PLACE first and TITLE second
#let education_entry(item: ()) = {
assert(
@ -119,21 +180,3 @@
[*#item.title.at(lang)* #item.place.at(lang) #h(1fr)]
[#par(item.abstract.at(lang))]
}
// skill-specific entry, changing its style for sidebar
#let skill_item(item: (), is_sidebar: false) = {
let side_list(body) = if is_sidebar { list(body) } else { par(body) }
for skill in item {
side_list({
[*#skill.name.at(lang)*]
if is_sidebar [\ ] else [ (]
for (i, v) in skill.items.enumerate() {
[#v.at(lang)]
if i < skill.items.len() - 1 {
[, ]
}
}
if not is_sidebar [)]
})
}
}

View file

@ -1,39 +1,7 @@
#import "lib.typ": *
// TODO: make it _return_ the data, not display it on its own
#let by_client(experience: ()) = {
let by_client = (:)
for item in experience {
let client = item.place.at(lang)
if client not in by_client {
by_client.insert(client, ())
}
by_client.at(client).push((item.title.at(lang), item.date.at(lang)))
}
for (client, jobs) in by_client {
[*#client*:]
for j in jobs {
[- #j.at(0) #h(1fr) #j.at(1)]
}
}
}
#let by_experience_type(type: (), experience: ()) = {
let by_ty = (:)
for (id, desc) in type {
let matching_exp_items = experience.filter(item => int(item.typeid) == int(id))
if matching_exp_items.len() == 0 {
return
}
[=== _#desc.at(lang)_]
by_client(experience: matching_exp_items)
}
}
#let resume(contents, main: ("experience", "education"), sidebar: ("volunteering", "languages", "skills")) = {
#let resume(contents, main: ("experience_by_type", "education"), sidebar: ("volunteering", "languages", "skills")) = {
show: style
set text(lang: lang)
let date_formatting = {
@ -66,75 +34,88 @@
header(contents.about)
let body = {
if "summary" in main and "summary" in contents {
section(
title: "",
{
contents.summary.at(lang)
},
)
}
for item in main {
if item == "summary" and "summary" in contents {
section(
title: "",
{
contents.summary.at(lang)
},
)
}
if "experience" in main and "experience" in contents {
let title = (en: "Professional Experience", de: "Berufserfahrung").at(lang)
section(title: title)[]
by_experience_type(experience: contents.experience, type: contents.experience_types)
}
if item == "experience_by_type" and "experience" in contents {
let title = (en: "Professional Experience", de: "Berufserfahrung").at(lang)
section(title: title)[]
by_experience_type(experience: contents.experience, type: contents.experience_types)
}
if item == "experience_by_client" and "experience" in contents {
let title = (en: "Professional Experience", de: "Berufserfahrung").at(lang)
section(title: title)[]
by_client(experience: contents.experience)
}
if item == "experience" and "experience" in contents {
let title = (en: "Professional Experience", de: "Berufserfahrung").at(lang)
section(title: title, entries: contents.experience)[]
}
if "education" in main and "education" in contents {
let title = (en: "Education", de: "Ausbildung").at(lang)
section(title: title, entries: contents.thesis + contents.education)[]
}
if item == "education" and "education" in contents {
let title = (en: "Education", de: "Ausbildung").at(lang)
section(title: title, entries: contents.thesis + contents.education)[]
}
if "volunteering" in main and "volunteering" in contents {
let title = (en: "Volunteer Work", de: "Ehrenamt").at(lang)
section(title: title, entries: contents.volunteering)[]
}
if item == "volunteering" and "volunteering" in contents {
let title = (en: "Volunteer Work", de: "Ehrenamt").at(lang)
section(title: title, entries: contents.volunteering)[]
}
if "skills" in main and "skills" in contents {
let title = (en: "Qualifications", de: "Qualifikationen").at(lang)
section(
title: title,
{
skill_item(item: contents.skills)
},
)
}
if item == "skills" and "skills" in contents {
let title = (en: "Qualifications", de: "Qualifikationen").at(lang)
section(
title: title,
{
sidebar_entry(item: contents.skills)
},
)
}
if "languages" in main and "languages" in contents {
let title = (en: "Languages", de: "Sprachen").at(lang)
section(
title: title,
{
skill_item(item: contents.languages)
},
)
if item == "languages" and "languages" in contents {
let title = (en: "Languages", de: "Sprachen").at(lang)
section(
title: title,
{
sidebar_entry(item: contents.languages)
},
)
}
}
}
let sidebar = {
if "volunteering" in sidebar and "volunteering" in contents {
let title = (en: "Volunteer Work", de: "Ehrenamt").at(lang)
[== #title]
for e in contents.at("volunteering") {
[
- *#e.title.at(lang)* (#e.date.at(lang))
#par(e.bullets.at(0).at(lang)) \
]
for item in sidebar {
if item == "volunteering" and "volunteering" in contents {
let title = (en: "Volunteer Work", de: "Ehrenamt").at(lang)
[== #title]
for e in contents.at("volunteering") {
[
- *#e.title.at(lang)* (#e.date.at(lang))
#par(e.bullets.at(0).at(lang)) \
]
}
}
}
if "languages" in sidebar and "languages" in contents {
let title = (en: "Languages", de: "Sprachen").at(lang)
[== #title]
skill_item(item: contents.languages, is_sidebar: true)
[\ ]
}
if item == "languages" and "languages" in contents {
let title = (en: "Languages", de: "Sprachen").at(lang)
[== #title]
sidebar_entry(item: contents.languages, is_sidebar: true)
[\ ]
}
if "skills" in sidebar and "skills" in contents {
let title = (en: "Qualifications", de: "Kenntnisse").at(lang)
[== #title]
skill_item(item: contents.skills, is_sidebar: true)
if item == "skills" and "skills" in contents {
let title = (en: "Qualifications", de: "Kenntnisse").at(lang)
[== #title]
sidebar_entry(item: contents.skills, is_sidebar: true)
}
}
}
@ -168,7 +149,6 @@
align(
right,
block(
fill: luma(250),
width: 90%,
{
v(15pt)