parent
e2ff82b759
commit
9afd5f51c5
5 changed files with 486 additions and 31 deletions
79
flake.nix
79
flake.nix
|
@ -34,57 +34,74 @@
|
|||
'';
|
||||
};
|
||||
|
||||
src = typixLib.cleanTypstSource ./.;
|
||||
commonArgs = {
|
||||
typstSource = "src/main.typ";
|
||||
typstOutput = "out/main.pdf";
|
||||
mkTypst = entry: rec {
|
||||
src = typixLib.cleanTypstSource ./.;
|
||||
commonArgs = {
|
||||
typstSource = "src/${entry}.typ";
|
||||
typstOutput = "out/${entry}.pdf";
|
||||
|
||||
fontPaths = [ ];
|
||||
fontPaths = [
|
||||
"${pkgs.inter}/share/fonts/truetype"
|
||||
];
|
||||
|
||||
virtualPaths = [ ];
|
||||
virtualPaths = [ ];
|
||||
};
|
||||
|
||||
commonBuildArgs = commonArgs // {
|
||||
XDG_CACHE_HOME = typstPackagesCache;
|
||||
};
|
||||
|
||||
build-drv = typixLib.buildTypstProject (
|
||||
commonBuildArgs
|
||||
// {
|
||||
inherit src;
|
||||
}
|
||||
);
|
||||
|
||||
build-script = typixLib.buildTypstProjectLocal (
|
||||
commonBuildArgs
|
||||
// {
|
||||
inherit src;
|
||||
}
|
||||
);
|
||||
|
||||
watch-script = typixLib.watchTypstProject commonArgs;
|
||||
};
|
||||
|
||||
commonBuildArgs = commonArgs // {
|
||||
XDG_CACHE_HOME = typstPackagesCache;
|
||||
};
|
||||
|
||||
build-drv = typixLib.buildTypstProject (
|
||||
commonBuildArgs
|
||||
// {
|
||||
inherit src;
|
||||
}
|
||||
);
|
||||
|
||||
build-script = typixLib.buildTypstProjectLocal (
|
||||
commonBuildArgs
|
||||
// {
|
||||
inherit src;
|
||||
}
|
||||
);
|
||||
|
||||
watch-script = typixLib.watchTypstProject commonArgs;
|
||||
typstMain = mkTypst "main";
|
||||
typstWork = mkTypst "work";
|
||||
in
|
||||
{
|
||||
checks = {
|
||||
inherit build-drv build-script watch-script;
|
||||
inherit (typstMain) build-drv build-script watch-script;
|
||||
# inherit (typstWork) build-drv build-script watch-script;
|
||||
};
|
||||
|
||||
packages.default = build-drv;
|
||||
packages = {
|
||||
default = typstMain.build-drv;
|
||||
work = typstWork.build-drv;
|
||||
};
|
||||
|
||||
apps = rec {
|
||||
default = watch;
|
||||
build = inputs.flake-utils.lib.mkApp {
|
||||
drv = build-script;
|
||||
drv = typstMain.build-script;
|
||||
};
|
||||
watch = inputs.flake-utils.lib.mkApp {
|
||||
drv = watch-script;
|
||||
drv = typstMain.watch-script;
|
||||
};
|
||||
build-work = inputs.flake-utils.lib.mkApp {
|
||||
drv = typstWork.build-script;
|
||||
};
|
||||
watch-work = inputs.flake-utils.lib.mkApp {
|
||||
drv = typstWork.watch-script;
|
||||
};
|
||||
};
|
||||
|
||||
devShells.default = typixLib.devShell {
|
||||
inherit (commonArgs) fontPaths virtualPaths;
|
||||
inherit (typstMain.commonArgs) fontPaths virtualPaths;
|
||||
packages = [
|
||||
watch-script
|
||||
typstMain.watch-script
|
||||
pkgs.typstyle
|
||||
pkgs.tinymist
|
||||
|
||||
|
|
247
src/lib/work.typ
Normal file
247
src/lib/work.typ
Normal file
|
@ -0,0 +1,247 @@
|
|||
#let dates-helper(
|
||||
start-date: "",
|
||||
end-date: "",
|
||||
) = {
|
||||
start-date + " - " + end-date
|
||||
}
|
||||
|
||||
#let job_entry(date, company, position, description, experiences) = {
|
||||
grid(
|
||||
columns: (22%, 33%, auto),
|
||||
gutter: 12pt,
|
||||
// Column 1: Date Range
|
||||
box(
|
||||
fill: rgb(242, 242, 242, 255),
|
||||
width: 100%,
|
||||
inset: 10pt,
|
||||
text(date),
|
||||
),
|
||||
|
||||
// Column 2: Company Info
|
||||
box()[
|
||||
#text(company) \
|
||||
#text(weight: "bold", position) \
|
||||
#text(description)
|
||||
],
|
||||
|
||||
// Column 3: Work Experiences
|
||||
for exp in experiences {
|
||||
box(
|
||||
fill: rgb(242, 242, 242, 255),
|
||||
width: 100%,
|
||||
inset: 10pt,
|
||||
[
|
||||
#text(exp.role) \
|
||||
#text(weight: "bold", exp.heading) \
|
||||
#text(size: 10pt, exp.description)
|
||||
],
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#let resume(
|
||||
name: "",
|
||||
position: "",
|
||||
address: none,
|
||||
birth_date: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
profile-picture: image,
|
||||
company-logo: image,
|
||||
font: "Inter",
|
||||
paper: "a4",
|
||||
body,
|
||||
) = {
|
||||
set document(author: name, title: name)
|
||||
|
||||
set text(font: font, lang: "en")
|
||||
show heading: set text(rgb(0, 194, 255, 255))
|
||||
|
||||
grid(
|
||||
columns: 100%,
|
||||
align: (right),
|
||||
[
|
||||
#company-logo
|
||||
\ \ \ \
|
||||
]
|
||||
)
|
||||
|
||||
grid(
|
||||
columns: (60%, 180pt),
|
||||
align: (top, right),
|
||||
[
|
||||
#text(size: 30pt, weight: "bold", name) \
|
||||
\
|
||||
#text(size: 13pt, weight: "bold", position) \
|
||||
\ \
|
||||
],
|
||||
)
|
||||
|
||||
set text()
|
||||
grid(
|
||||
columns: (50%, auto),
|
||||
gutter: 100pt,
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#heading(level: 2, "Personal Data")
|
||||
|
||||
#grid(
|
||||
columns: (50%, 50%),
|
||||
gutter: 20pt,
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#text(weight: "bold", "Name:") \
|
||||
#text(name)
|
||||
],
|
||||
),
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#text(weight: "bold", "Birth date:") \
|
||||
#text(birth_date)
|
||||
],
|
||||
),
|
||||
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#text(weight: "bold", "Address:") \
|
||||
#text(address.street)
|
||||
#text(address.city)
|
||||
],
|
||||
),
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#text(weight: "bold", "Contact:") \
|
||||
#text(email)
|
||||
#{
|
||||
if phone != "" {
|
||||
text(phone)
|
||||
}
|
||||
}
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
profile-picture,
|
||||
)
|
||||
|
||||
linebreak()
|
||||
linebreak()
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
#let work_experience(body) = {
|
||||
set text(fill: black)
|
||||
|
||||
set text(fill: rgb(0, 194, 255, 255))
|
||||
grid(
|
||||
columns: (60%, auto),
|
||||
heading(level: 2, "Work Experience"), heading(level: 2, "Case Studies"),
|
||||
)
|
||||
|
||||
set text(fill: black)
|
||||
body
|
||||
}
|
||||
|
||||
#let education_entry(date, university, degree, subject) = {
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#text(weight: "bold", date) \
|
||||
#text(weight: "bold", university) \
|
||||
#text(degree + " in " + subject)
|
||||
\ \
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#let certification_entry(date, issuer, certificate) = {
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#text(weight: "bold", date) \
|
||||
#text(weight: "bold", certificate) \
|
||||
#text(weight: "bold", issuer)
|
||||
\ \
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#let language_levels = (
|
||||
(0, 25, "Beginner"),
|
||||
(26, 50, "Intermediate"),
|
||||
(51, 75, "Advanced"),
|
||||
(76, 99, "Fluent"),
|
||||
(100, 100, "Native"),
|
||||
)
|
||||
|
||||
#let lang_level_text = score => {
|
||||
let result = "Unknown"
|
||||
|
||||
for (min, max, label) in language_levels {
|
||||
if score >= min and score <= max {
|
||||
result = label
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#let language_entry(lang, score) = {
|
||||
grid(
|
||||
columns: (40%, auto),
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#text(weight: "bold")[#lang]
|
||||
],
|
||||
),
|
||||
box(
|
||||
width: 100%,
|
||||
[
|
||||
#lang_level_text(score)
|
||||
#box(
|
||||
width: 105pt,
|
||||
height: 6pt,
|
||||
fill: rgb(220, 220, 220),
|
||||
inset: 0pt,
|
||||
[
|
||||
#rect(
|
||||
width: (105pt * score / 100),
|
||||
height: 6pt,
|
||||
fill: rgb(0, 194, 255, 255),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#let bottom_grid(sections) = {
|
||||
linebreak()
|
||||
linebreak()
|
||||
linebreak()
|
||||
|
||||
set text(fill: rgb(0, 194, 255, 255))
|
||||
grid(
|
||||
columns: (60%, auto),
|
||||
gutter: 3pt,
|
||||
..sections.map(s => {
|
||||
if s != none {
|
||||
heading(level: 2, s.title)
|
||||
|
||||
set text(fill: black)
|
||||
s.content
|
||||
}
|
||||
}),
|
||||
)
|
||||
set text(fill: black)
|
||||
}
|
BIN
src/photo.jpg
Normal file
BIN
src/photo.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 307 KiB |
BIN
src/upsquared.png
Normal file
BIN
src/upsquared.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 9.8 KiB |
191
src/work.typ
Normal file
191
src/work.typ
Normal file
|
@ -0,0 +1,191 @@
|
|||
#import "lib/work.typ": *
|
||||
|
||||
#let name = "Felix Schröter"
|
||||
#let position = "Senior Software Engineer & Tech Lead"
|
||||
#let address = (street: "Salzwedeler Str. 1", city: "21339 Lüneburg")
|
||||
#let birth_date = "26.09.1995"
|
||||
#let email = "fs@upsquared.com"
|
||||
|
||||
#let profile-picture = image("photo.jpg", width: 115pt, height: 135pt)
|
||||
#let company-logo = image("upsquared.png", width: 220pt, height: auto)
|
||||
|
||||
#show: resume.with(
|
||||
name: name,
|
||||
position: position,
|
||||
address: address,
|
||||
birth_date: birth_date,
|
||||
email: email,
|
||||
profile-picture: profile-picture,
|
||||
company-logo: company-logo,
|
||||
)
|
||||
|
||||
#show: work_experience.with()
|
||||
|
||||
#job_entry(
|
||||
dates-helper(start-date: "Jun 2024", end-date: "Present"),
|
||||
"upsquared GmbH",
|
||||
"Senior Software Engineer & Tech Lead",
|
||||
"Leading Cloud-native Development, Infrastructure & DevOps solutions",
|
||||
(
|
||||
(
|
||||
role: "Tech Lead",
|
||||
heading: "Grow engineering team",
|
||||
description: "Onboard, mentor & lead new team of engineers.",
|
||||
),
|
||||
(
|
||||
role: "Software & Cloud Architect",
|
||||
heading: "Cloud-native solutions",
|
||||
description: "Architect solutions built on Rust, Kubernetes, OpenTofu & GCP",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#job_entry(
|
||||
dates-helper(start-date: "Oct 2019", end-date: "May 2024"),
|
||||
"upsquared GmbH",
|
||||
"Senior Software Engineer",
|
||||
"Full-Stack Architecture, Development, Infrastructure & DevOps for Cloud-native solutions",
|
||||
(
|
||||
(
|
||||
role: "Software & Cloud Architect",
|
||||
heading: "Cloud-native solutions",
|
||||
description: "Architect solutions built on Rust, C#, Kubernetes, Terraform & GCP",
|
||||
),
|
||||
(
|
||||
role: "DevOps Engineer",
|
||||
heading: "Full-stack Observability",
|
||||
description: "Enhanced Observability with Sentry, Rust, C#, React & GKE",
|
||||
),
|
||||
(
|
||||
role: "Frontend Engineer",
|
||||
heading: "Plant Analyzer Web",
|
||||
description: "Built web-based frontend for digital phenotyping platform",
|
||||
),
|
||||
(
|
||||
role: "DevOps Engineer",
|
||||
heading: "Reproducible CI/CD",
|
||||
description: "Established reproducible builds & dev environments using Nix",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#job_entry(
|
||||
dates-helper(start-date: "Apr 2019", end-date: "Sep 2019"),
|
||||
"Digital Spring",
|
||||
"Senior Software Engineer",
|
||||
"Full-Stack Architecture, Development, Infrastructure & DevOps for Cloud-native solutions",
|
||||
(
|
||||
(
|
||||
role: "DevOps Engineer",
|
||||
heading: "GitLab CI/CD",
|
||||
description: "Set up GitLab CI/CD pipeline for .NET microservices",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#job_entry(
|
||||
dates-helper(start-date: "Oct 2016", end-date: "Mar 2019"),
|
||||
"Digital Spring",
|
||||
"Software Engineer",
|
||||
"Full-Stack Development, Infrastructure & DevOps for Cloud-native solutions",
|
||||
(
|
||||
(
|
||||
role: "Backend & Cloud Architect",
|
||||
heading: "Cloud-native solutions",
|
||||
description: "Built microservices & Kubernetes cluster from scratch",
|
||||
),
|
||||
(
|
||||
role: "Backend & Cloud Engineer",
|
||||
heading: "Serverless solutions",
|
||||
description: "Built serverless backends with AWS Lambda",
|
||||
),
|
||||
(
|
||||
role: "Frontend Engineer",
|
||||
heading: "Modern web apps",
|
||||
description: "Implemented frontends in React & Angular for different projects",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#job_entry(
|
||||
dates-helper(start-date: "Jun 2015", end-date: "Sep 2016"),
|
||||
"Werum IT Solutions GmbH (now Körber Pharma Software GmbH)",
|
||||
"Software Engineer",
|
||||
"",
|
||||
(
|
||||
(
|
||||
role: "Software Engineer",
|
||||
heading: "Interface development",
|
||||
description: "DCS/PCS and ERP interface development with .NET & PL/SQL",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#job_entry(
|
||||
dates-helper(start-date: "Aug 2012", end-date: "Jun 2015"),
|
||||
"Werum IT Solutions GmbH (now Körber Pharma Software GmbH)",
|
||||
"Trainee",
|
||||
"",
|
||||
(
|
||||
(
|
||||
role: "Software Engineer",
|
||||
heading: "Process automation",
|
||||
description: "Process automation (DCS/PCS) interface development with .NET",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
#let education = (
|
||||
(
|
||||
dates-helper(start-date: "May 2024", end-date: "Present"),
|
||||
"University of Colorado Boulder",
|
||||
"Master of Science",
|
||||
"Computer Science",
|
||||
),
|
||||
(
|
||||
dates-helper(start-date: "Aug 2012", end-date: "Jun 2015"),
|
||||
"Berufsbildende Schulen I Lüneburg",
|
||||
"Computer Science Expert",
|
||||
"Software Development",
|
||||
),
|
||||
);
|
||||
|
||||
#let certifications = (
|
||||
(
|
||||
"Feb 2024",
|
||||
"CompTIA",
|
||||
"CompTIA Cloud+",
|
||||
),
|
||||
(
|
||||
"Feb 2024",
|
||||
"Cloud Security Alliance",
|
||||
"Certificate of Cloud Security Knowledge v.4",
|
||||
),
|
||||
);
|
||||
|
||||
#let languages = (
|
||||
("English", 90),
|
||||
("German", 100),
|
||||
)
|
||||
|
||||
#let sections = (
|
||||
(
|
||||
title: "Education",
|
||||
content: education.map(e => education_entry(..e)).join("\n"),
|
||||
),
|
||||
(
|
||||
title: "Languages",
|
||||
content: languages.map(l => language_entry(..l)).join("\n"),
|
||||
),
|
||||
none,
|
||||
// (
|
||||
// title: "Certifications",
|
||||
// content: certifications.map(c => certification_entry(..c)).join("\n"),
|
||||
// ),
|
||||
(
|
||||
title: "Interests",
|
||||
content: "reading, tea, Nix, NixOS, Linux, homelab, Doctor Who",
|
||||
),
|
||||
)
|
||||
|
||||
#bottom_grid(sections)
|
Loading…
Add table
Add a link
Reference in a new issue