diff options
| author | quentin@aristote.fr <quentin@aristote.fr> | 2023-08-21 11:36:24 +0200 |
|---|---|---|
| committer | quentin@aristote.fr <quentin@aristote.fr> | 2023-08-21 17:44:49 +0200 |
| commit | 731a43a83e2e2b61d11c5ac33fe96f92cef41bb5 (patch) | |
| tree | ea32a6c6ae5c036cab7b390543f9cb71e9444095 | |
initial commit
| -rw-r--r-- | .gitignore | 317 | ||||
| -rw-r--r-- | default.nix | 33 | ||||
| -rw-r--r-- | flake.lock | 82 | ||||
| -rw-r--r-- | flake.nix | 49 | ||||
| -rw-r--r-- | lib/default.nix | 5 | ||||
| -rw-r--r-- | lib/latex.nix | 133 | ||||
| -rw-r--r-- | src/default.nix | 22 | ||||
| -rw-r--r-- | src/education/default.nix | 21 | ||||
| -rw-r--r-- | src/experience/default.nix | 22 | ||||
| -rw-r--r-- | src/header.tex | 10 | ||||
| -rw-r--r-- | src/languages/default.nix | 22 | ||||
| -rw-r--r-- | src/publications/default.nix | 19 | ||||
| -rw-r--r-- | src/sections.nix | 15 |
13 files changed, 750 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b08f2b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,317 @@ +# Created by https://www.toptal.com/developers/gitignore/api/latex +# Edit at https://www.toptal.com/developers/gitignore?templates=latex + +### LaTeX ### +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib + +### LaTeX Patch ### +# LIPIcs / OASIcs +*.vtc + +# glossaries +*.glstex + +# End of https://www.toptal.com/developers/gitignore/api/latex + +### Nix ### +result*
\ No newline at end of file diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..6385c9d --- /dev/null +++ b/default.nix @@ -0,0 +1,33 @@ +{ pkgs, latex, data }: + +let + commonArgs = { + inherit data latex make; + inherit (pkgs) lib; + }; + make = path: overrides: + let f = import path; + in f ((builtins.intersectAttrs (builtins.functionArgs f) commonArgs) + // overrides); + + cvTEX = builtins.toFile "cv.tex" (make ./src { }); + source = pkgs.callPackage ({ noto-fonts-emoji, + # Source files + cv-tex ? cvTEX, files ? data.files }: + pkgs.runCommand "cv-src" { } '' + mkdir -p "$out" && cd $_ + ln -sT ${cv-tex} cv.tex + ln -sT ${files} files + ln -sT ${noto-fonts-emoji}/share/fonts/noto fonts + '') { }; +in { + src = source; + pdf = pkgs.callPackage ({ cv-src ? source, texlive }: + pkgs.runCommand "cv.pdf" { + buildInputs = [ texlive.combined.moderncv ]; + } '' + export HOME=$(pwd) + latexmk -pdflua -cd "${cv-src}"/cv.tex --output-directory=$(pwd) + mv cv.pdf "$out" + '') { }; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6e87763 --- /dev/null +++ b/flake.lock @@ -0,0 +1,82 @@ +{ + "nodes": { + "data": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692544293, + "narHash": "sha256-H/uRnMyKwoqI+KBFy40HFn5vnJXKD2bjYSeIICeS23Y=", + "owner": "qaristote", + "repo": "info", + "rev": "1b58988cc70068e43c479809f3b3fa34bc5ff11d", + "type": "github" + }, + "original": { + "owner": "qaristote", + "repo": "info", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1692494774, + "narHash": "sha256-noGVoOTyZ2Kr5OFglzKYOX48cx3hggdCPbXrYMG2FDw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3476a10478587dec90acb14ec6bde0966c545cc0", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "data": "data", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9d25023 --- /dev/null +++ b/flake.nix @@ -0,0 +1,49 @@ +{ + description = "Quentin Aristote's CV."; + + inputs = { + data = { + # url = "github:qaristote/info"; + url = "github:qaristote/info"; + inputs = { + nixpkgs.follows = "/nixpkgs"; + flake-utils.follows = "/flake-utils"; + }; + }; + }; + + outputs = { self, nixpkgs, flake-utils, data }: + { + lib = import ./lib { inherit (nixpkgs) lib; }; + } // flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ + (self: super: { + texlive = super.lib.recursiveUpdate super.texlive { + combined.moderncv = super.texlive.combine { + inherit (super.texlive) + scheme-basic biber biblatex latexmk luatex luatexbase + moderncv fontspec fontawesome5 academicons pgf multirow + arydshln emoji; + }; + }; + }) + ]; + }; + in { + packages = { + texlive.combined.moderncv = pkgs.texlive.combined.moderncv; + cv = import ./default.nix { + inherit pkgs; + inherit (self.lib.pp) latex; + data = data.formatWith."${system}" self.lib.pp.latex; + }; + }; + defaultPackage = self.packages."${system}".cv.pdf; + devShell = pkgs.mkShell { + packages = with pkgs; [ nixfmt texlive.combined.moderncv ]; + }; + }); +} diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..57653ad --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,5 @@ +{ lib }: + +{ + pp.latex = import ./latex.nix { inherit lib; }; +} diff --git a/lib/latex.nix b/lib/latex.nix new file mode 100644 index 0000000..99cd783 --- /dev/null +++ b/lib/latex.nix @@ -0,0 +1,133 @@ +{ lib, ... }: + +let + lines = content: + if lib.isList content then + (if content == [ ] then + "" + else + lines (builtins.head content) + "\n" + lines (builtins.tail content)) + else + content; + sortByFun = cmp: f: lib.sort (x: y: cmp (f x) (f y)); + sortByPath = cmp: keys: sortByFun cmp (lib.getAttrFromPath keys); + sortByKey = cmp: key: sortByPath cmp [ key ]; + for = iterable: f: + if lib.isList iterable then + builtins.map f iterable + else + lib.mapAttrsToList f iterable; + + tryOverride = f: arg: + if lib.isAttrs arg then + tryOverride (attrs: content: f (arg // attrs) content) + else + f { } arg; + + setOptionalArg = name: value: "${name}=${value}"; + expandArgsPrefixed = numArgs: prefix: + if numArgs <= 0 then + prefix + else + arg: + let + appendToPrefix = if lib.isAttrs arg then + "[" + lib.concatStringsSep "," (lib.mapAttrsToList setOptionalArg arg) + + "]" + else + "{${arg}}"; + newNumArgs = if lib.isAttrs arg then numArgs else numArgs - 1; + in expandArgsPrefixed newNumArgs (prefix + appendToPrefix); + macroWithOpts = name: numArgs: expandArgsPrefixed numArgs "\\${name}"; + macro = name: content: + let + contentExpanded = if lib.isList content then + lib.concatStringsSep "}{" content + else + content; + in "\\${name}{${contentExpanded}}"; + sectionMacro = type: name: content: '' + \${type}{${name}} + + ${lines content} + ''; + environment = name: content: '' + \begin{${name}} + ${lines content} + \end{${name}} + ''; + + latex = { + inherit macro environment; + + comment = content: "% ${content}"; + document = environment "document"; + section = sectionMacro "section"; + + title = macroWithOpts "title" 1; + + url = macroWithOpts "url" 1; + href = macroWithOpts "href" 2; + bold = macroWithOpts "textbf" 1; + + cite = macroWithOpts "cite" 1; + moderncv = { + name = macroWithOpts "name" 2; + email = macroWithOpts "email" 1; + extrainfo = macroWithOpts "extrainfo" 1; + photo = macroWithOpts "photo" 1; + makecvtitle = macro "makecvtitle" [ ]; + cventry = macroWithOpts "cventry" 6; + cvlistitem = macroWithOpts "cvlistitem" 1; + cvline = macroWithOpts "cvline" 2; + cvitemwithcomment = macroWithOpts "cvitemwithcomment" 3; + cvdoubleitem = macroWithOpts "cvdoubleitem" 4; + }; + }; + + file = path: "files/${path}"; + href = latex.href; + timerange = startdate: enddate: + let + print = builtins.mapAttrs (name: x: builtins.toString x); + # let str = builtins.toString x; + # in if name == "year" then + # builtins.substring 2 4 str + # else + # (if name == "month" && x < 10 then "0${str}" else str)); + start = print startdate; + end = print enddate; + months = { + "1" = "jan"; + "2" = "feb"; + "3" = "mar"; + "4" = "apr"; + "5" = "may"; + "6" = "jun"; + "7" = "jul"; + "8" = "aug"; + "9" = "sep"; + "10" = "oct"; + "11" = "nov"; + "12" = "dec"; + }; + in "${months."${start.month}"}." + + lib.optionalString (start.year != end.year) " ${start.year}" + + " -- ${months."${end.month}"}. ${end.year}"; +in latex // { + inherit for file href lines timerange; + code = x: x; + sort = let + lt = x: y: x < y; + gt = x: y: x > y; + in { + byKey = sortByKey lt; + byPath = sortByPath lt; + byFun = sortByFun lt; + reverse = { + byKey = sortByKey gt; + byPath = sortByPath gt; + byFun = sortByFun gt; + }; + }; +} diff --git a/src/default.nix b/src/default.nix new file mode 100644 index 0000000..421367b --- /dev/null +++ b/src/default.nix @@ -0,0 +1,22 @@ +{ latex, data, make, ... }: + +with latex; +let sections = sort.byKey "priority" (make ./sections.nix { }); +in with data.basics; +lines [ + (builtins.readFile ./header.tex) + (comment "-------------------- EXTRA --------------------") + (for sections (section: section.extraHeader)) + (comment "-------------------- DATA --------------------") + (moderncv.name name.first name.last) + (moderncv.email email.personal) + (moderncv.extrainfo (latex.url url)) + (moderncv.photo { "" = "128pt"; } avatar) + "" + (document [ + (title institution.position) + moderncv.makecvtitle + description + (for sections (section: section.content)) + ]) +] diff --git a/src/education/default.nix b/src/education/default.nix new file mode 100644 index 0000000..5b79323 --- /dev/null +++ b/src/education/default.nix @@ -0,0 +1,21 @@ +{ latex, data, lib, ... }: + +let + education = data.education; + sortByStartDate = latex.sort.reverse.byFun + (x: with x.date.start; day + 100 * month + 10000 * year); +in { + title = "Education"; + priority = 10; + content = with latex; + for (sortByStartDate education) (item: + with item; + [ + (moderncv.cventry (latex.timerange date.start date.end) studyType + (with institution; href url name) institution.location "" description) + ] ++ lib.optional (item ? "years") (for (sortByStartDate years) (year: + with year; + moderncv.cvlistitem "${with program; bold (href url acronym)} (${ + timerange date.start date.end + }). ${program.studyType}. {\\small ${description}}"))); +} diff --git a/src/experience/default.nix b/src/experience/default.nix new file mode 100644 index 0000000..500fd9e --- /dev/null +++ b/src/experience/default.nix @@ -0,0 +1,22 @@ +{ latex, data, lib, ... }: + +let experience = data.experience; +in { + title = "Experience"; + priority = 0; + content = with latex; + for + (sort.reverse.byFun (x: with x.date.start; day + 100 * month + 10000 * year) + experience) (item: + with item; + moderncv.cventry (latex.timerange date.start date.end) + institution.position (with institution; href url name) + institution.location (if item ? supervisors then + "supervised by " + lib.concatStringsSep " \\& " + (for supervisors (supervisor: with supervisor; href url name)) + else + "") (description + lib.optionalString (item ? assets) (" " + cite + (lib.concatStringsSep "," + (for (lib.filter (asset: asset.type == "Publications") assets) + (lib.getAttr "id")))))); +} diff --git a/src/header.tex b/src/header.tex new file mode 100644 index 0000000..487888d --- /dev/null +++ b/src/header.tex @@ -0,0 +1,10 @@ +\documentclass[11pt, a4paper, sans, colorlinks = True, citecolor = blue, +urlcolor = blue]{moderncv} + +% --------------------- STYLE -------------------- +\moderncvstyle{casual} +\moderncvcolor{green} +\nopagenumbers{} + +% -------------------- MARGINS -------------------- +\usepackage[scale=0.75]{geometry} diff --git a/src/languages/default.nix b/src/languages/default.nix new file mode 100644 index 0000000..d030818 --- /dev/null +++ b/src/languages/default.nix @@ -0,0 +1,22 @@ +{ latex, data, lib, ... }: + +let + languages = data.languages; + sortByProficiency = lib.sort (lang1: lang2: + let + prof1 = lang1.proficiency; + prof2 = lang2.proficiency; + in (prof2 == "basic") || (prof1 == "native") + || (prof2 == "intermediate" && prof1 == "fluent")); +in { + title = "Languages"; + priority = 20; + extraHeader = '' + \usepackage{emoji} + \setemojifont{NotoColorEmoji.ttf}[Path=./fonts/] + ''; + content = with latex; + for (sortByProficiency languages) (lang: + with lang; + moderncv.cvline "${name} \\emoji{${icon.shortcode}}" proficiency); +} diff --git a/src/publications/default.nix b/src/publications/default.nix new file mode 100644 index 0000000..1d6232e --- /dev/null +++ b/src/publications/default.nix @@ -0,0 +1,19 @@ +{ latex, data, lib, ... }: + +let + publications = data.publications; + publicationsBIB = builtins.toFile "publications.bib" (latex.lines + (builtins.map (entry: entry.cite.biblatex) + (latex.sort.reverse.byPath [ "issued" "date-parts" ] publications))); +in { + title = "Publications"; + priority = 30; + extraHeader = '' + \usepackage[sorting=ydnt]{biblatex} + \addbibresource{${publicationsBIB}} + ''; + content = '' + \nocite{*} + \printbibliography[heading=none] + ''; +} diff --git a/src/sections.nix b/src/sections.nix new file mode 100644 index 0000000..faed484 --- /dev/null +++ b/src/sections.nix @@ -0,0 +1,15 @@ +{ latex, make, ... }: + +let + sectionTemplate = section: { + inherit (section) title priority; + extraHeader = if section ? extraHeader then section.extraHeader else ""; + content = latex.section section.title section.content; + }; + makeSection = path: sectionTemplate (make path { }); +in builtins.map makeSection [ + ./experience + ./education + ./languages + ./publications +] |
