├── .github ├── .gitignore └── FUNDING.yml ├── cv_data.xlsx ├── .gitignore ├── examples ├── formats.png ├── cv-2-column.jpg ├── cv-2-column.pdf ├── cv-academic.jpg ├── cv-academic.pdf └── cv-website.jpg ├── .Rbuildignore ├── pagedownCV.Rproj ├── R ├── save_gsheet_as_excel_workbook.R └── utility-functions.R ├── css ├── website-cv.css ├── two-col-cv.css └── academic_cv.css ├── README.md ├── cv-2-column.Rmd ├── cv-website.Rmd └── cv-academic.Rmd /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /cv_data.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulyngs/pagedownCV/HEAD/cv_data.xlsx -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ulyngs 4 | -------------------------------------------------------------------------------- /examples/formats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulyngs/pagedownCV/HEAD/examples/formats.png -------------------------------------------------------------------------------- /examples/cv-2-column.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulyngs/pagedownCV/HEAD/examples/cv-2-column.jpg -------------------------------------------------------------------------------- /examples/cv-2-column.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulyngs/pagedownCV/HEAD/examples/cv-2-column.pdf -------------------------------------------------------------------------------- /examples/cv-academic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulyngs/pagedownCV/HEAD/examples/cv-academic.jpg -------------------------------------------------------------------------------- /examples/cv-academic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulyngs/pagedownCV/HEAD/examples/cv-academic.pdf -------------------------------------------------------------------------------- /examples/cv-website.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulyngs/pagedownCV/HEAD/examples/cv-website.jpg -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^_pkgdown\.yml$ 4 | ^docs$ 5 | ^pkgdown$ 6 | ^\.github$ 7 | ^README\.Rmd$ 8 | -------------------------------------------------------------------------------- /pagedownCV.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /R/save_gsheet_as_excel_workbook.R: -------------------------------------------------------------------------------- 1 | #' save my google sheet as an excel file 2 | library(openxlsx) 3 | library(googlesheets4) 4 | gs4_deauth() 5 | 6 | # the link to my CV on google sheets 7 | my_cv_url <- "https://docs.google.com/spreadsheets/d/1ta71CAGkcLqm-W1UdVRA_JJSddWV2TsrRZsCnQlmOis/edit?usp=sharing" 8 | 9 | # read in the two tabs we want 10 | cv_entries <- read_sheet(my_cv_url, sheet = "cv_entries") 11 | publications <- read_sheet(my_cv_url, sheet = "publications") 12 | 13 | # write it to an excel file 14 | write.xlsx(list("cv_entries" = cv_entries, "publications" = publications), "cv_data.xlsx") 15 | 16 | -------------------------------------------------------------------------------- /css/website-cv.css: -------------------------------------------------------------------------------- 1 | thead { 2 | display: none; 3 | } 4 | 5 | table.dataTable.no-footer { 6 | border-bottom: none !important; 7 | } 8 | 9 | .cv-entries tbody td { 10 | border-bottom: none !important; 11 | border-top: none !important; 12 | padding: 0.2em 0em 0.4em 0.2em; 13 | font-size: 1em; 14 | } 15 | 16 | .cv-entry-title { 17 | font-weight: bold; 18 | } 19 | 20 | .tab-content .tab-pane { 21 | padding: 0; 22 | margin: 0; 23 | } 24 | 25 | .tab-content label { 26 | padding: 1em 0em 0.8em 0.2em; 27 | } 28 | .tab-content .tab-pane td { 29 | padding: 0.2em 0em 0.4em 0.2em; 30 | } 31 | .tab-content table { 32 | margin-top: 1em; 33 | margin-bottom: 1.5em; 34 | } 35 | 36 | .tab-content #talks table, 37 | .tab-content #posters table { 38 | margin-bottom: 0.3em; 39 | } 40 | 41 | .tab-content #talks p, 42 | .tab-content #posters p { 43 | margin-bottom: 1px; 44 | } 45 | 46 | 47 | 48 | h1 { 49 | font-size: 2.2em; 50 | } 51 | h2 { 52 | font-size: 1.7em; 53 | margin-bottom: 0.5em; 54 | margin-top: 1em; 55 | } 56 | h3 { 57 | font-size: 1.3em; 58 | } 59 | .domain-header { 60 | margin-top: 2.5em; 61 | } 62 | thead { 63 | display: none !important; 64 | } 65 | table{ 66 | margin-bottom: 3em; 67 | table-layout: fixed; 68 | width: 100%; 69 | } 70 | .datatables.html-widget { 71 | margin-bottom: 3em !important; 72 | } 73 | tbody td:first-child { 74 | padding-right: 1.5em; 75 | padding-left: 0; 76 | vertical-align: top; 77 | width: 18%; 78 | } 79 | 80 | /* the talks table has three columns */ 81 | #talks-table-dt tbody td:nth-child(1), 82 | #talks-table-dt tbody td:nth-child(2) { 83 | width: 9%; 84 | vertical-align: top; 85 | } 86 | #talks-table-dt tbody td:nth-child(3) { 87 | width: 82%; 88 | } 89 | 90 | td { 91 | padding-bottom: 0.2em; 92 | } 93 | .work-title { 94 | font-size: 1.2em; 95 | } 96 | .datatables { 97 | margin-bottom: 2em; 98 | } 99 | 100 | .datatables .paginate_button.current { 101 | border: none !important; 102 | background: none !important; 103 | font-weight: bold; 104 | } 105 | .datatables a.paginate_button:hover { 106 | background: rgba(0,0,0,.04) !important; 107 | color: black !important; 108 | border: none !important; 109 | } 110 | .datatables .dataTables_length select { 111 | background-color: #fff; 112 | padding: 3px; 113 | border-radius: 3px; 114 | border: 1px solid rgba(0,0,0,.05); 115 | } 116 | 117 | .talk-translation { 118 | font-style: italic; 119 | } 120 | 121 | /**** HYPERLINKS ******/ 122 | /* by default black, underlined and no line on hover */ 123 | a { 124 | color: black; 125 | border-bottom: 1px solid lightgray; 126 | } 127 | a:hover { 128 | text-decoration: none; 129 | color: black; 130 | border-bottom: none; 131 | } 132 | -------------------------------------------------------------------------------- /R/utility-functions.R: -------------------------------------------------------------------------------- 1 | # add a column 'number' that counts entries in order of date -- adds leading zero, and optionally a letter 2 | add_leading_zeroes_and_letter <- function(cv_entries_tibble, letter_to_pad = ""){ 3 | if(nrow(cv_entries_tibble) > 9){ 4 | cv_entries_tibble |> 5 | dplyr::arrange(date) |> 6 | dplyr::mutate(number = stringr::str_pad(row_number(), width = 2, side = "left", pad = "0"), 7 | number = stringr::str_c(letter_to_pad, number)) |> 8 | dplyr::arrange(desc(date)) 9 | } else { 10 | cv_entries_tibble |> 11 | dplyr::arrange(date) |> 12 | dplyr::mutate(number = stringr::str_pad(row_number(), width = 1, side = "left", pad = "0"), 13 | number = stringr::str_c(letter_to_pad, number)) |> 14 | dplyr::arrange(desc(date)) 15 | } 16 | } 17 | 18 | 19 | # Turn year into an empty string when it's the same year as the previous entry 20 | blank_year_when_repeated <- function(cv_entry_tibble){ 21 | cv_entry_tibble |> 22 | dplyr::mutate(row_number = dplyr::row_number()) |> 23 | dplyr::mutate(year = as.character(year), 24 | year = dplyr::case_when( 25 | row_number == 1 ~ year, 26 | year == dplyr::lag(year) ~ "", 27 | year != dplyr::lag(year) ~ year 28 | )) %>% 29 | select(-row_number) 30 | } 31 | 32 | 33 | # Adds a page break after a row in a column that contains a specified text 34 | # publications |> 35 | # manual_page_break_after_row("title", "Before and after GDPR") 36 | manual_page_break_after_row <- function(some_tibble, a_column_name, text_to_detect, use_glue = TRUE){ 37 | if(use_glue){ 38 | some_tibble |> 39 | dplyr::mutate({{a_column_name}} := if_else(stringr::str_detect(.data[[a_column_name]], text_to_detect), 40 | glue::glue("{.data[[a_column_name]]}
"), 41 | glue::glue("{.data[[a_column_name]]}") 42 | )) 43 | } else { 44 | some_tibble |> 45 | dplyr::mutate({{a_column_name}} := if_else(stringr::str_detect(.data[[a_column_name]], text_to_detect), 46 | paste(.data[[a_column_name]], "
"), 47 | paste(.data[[a_column_name]]) 48 | )) 49 | } 50 | } 51 | 52 | 53 | #' Take an author name and replace the first name with an initial and dot 54 | #' e.g. When given "Ulrik Lyngs" it returns "U. Lyngs" 55 | replace_first_name_with_initial_and_dot <- function(author_name) { 56 | stringr::str_replace(author_name, "(?<=\\S)\\S+", "\\.") 57 | } 58 | 59 | 60 | #' Take a comma-separated string of authors and replace the first names with an initial and a dot 61 | # When given "Ulrik Lyngs, Kai Lukoff" it returns "U. Lyngs, K. Lukoff" 62 | replace_first_names_in_author_list_with_initial_and_dot <- function(authors){ 63 | authors_split <- stringr::str_split(authors, ",") %>% 64 | map(stringr::str_trim) 65 | 66 | authors_split[[1]] %>% purrr::map_chr(replace_first_name_with_initial_and_dot) %>% 67 | paste0(collapse = ", ") 68 | } 69 | -------------------------------------------------------------------------------- /css/two-col-cv.css: -------------------------------------------------------------------------------- 1 | /* THIS CREATES BASIC PAGED LAYOUT IN COMBINATION WITH PAGED.HTML */ 2 | @page{ 3 | size: A4 portrait; 4 | } 5 | 6 | body { 7 | font-family: "Latin Modern Roman", "Book Antiqua", Palatino, serif 8 | } 9 | 10 | :root{ 11 | --page-width: 8.5in; 12 | --pagedjs-margin-right: 0.6in; 13 | --pagedjs-margin-left: 0.6in; 14 | --pagedjs-margin-top: 0.4in; 15 | --pagedjs-margin-bottom: 0.3in; 16 | --root-font-size: 12pt; 17 | --viewer-pages-spacing: 12px; 18 | --viewer-shadow-color: #313131; /* this marks the pages */ 19 | } 20 | 21 | 22 | /* Paged.js viewer */ 23 | @media screen { 24 | body { 25 | background-color: var(--viewer-background-color); 26 | margin: 0; /* for mobile */ 27 | width: calc(var(--pagedjs-width) + 2 * var(--viewer-pages-spacing)); /* for mobile */ 28 | } 29 | .pagedjs_pages { 30 | max-width: var(--pagedjs-width); 31 | margin: 0 auto; 32 | display: flex; 33 | flex-direction: column; 34 | } 35 | .pagedjs_page { 36 | box-shadow: 0 0 calc(0.66667 * var(--viewer-pages-spacing)) var(--viewer-shadow-color); 37 | margin: var(--viewer-pages-spacing) 0; 38 | } 39 | } 40 | @media screen and (min-width: 8.5in) { 41 | /* not a mobile */ 42 | body { 43 | margin: auto; 44 | width: unset; 45 | } 46 | } 47 | 48 | /* 49 | if we get the second page started already on the first page 50 | then make sure we ain't got a border showing up! 51 | */ 52 | .pagedjs_first_page .pagetwo h1 { 53 | border-bottom: none !important; 54 | } 55 | 56 | 57 | /* create ability to insert pagebreaks with br.pagebreak */ 58 | br.pageBreak { 59 | page-break-after: always; 60 | } 61 | 62 | p, li { 63 | font-size: var(--root-font-size); 64 | line-height: 115%; 65 | } 66 | 67 | a { 68 | text-decoration: none; 69 | color: black; 70 | } 71 | 72 | /* title */ 73 | h1.title { 74 | text-align: center; 75 | font-weight: normal; 76 | font-size: 2.3em; 77 | line-height: 110%; 78 | } 79 | 80 | /* section headers */ 81 | .row h1 { 82 | color: #710C0C; 83 | border-bottom: 1px solid black; 84 | margin-top: 0.5em; 85 | margin-bottom: 0.2em; 86 | font-size: 1.5em; 87 | font-weight: normal; 88 | } 89 | 90 | 91 | /* PAGE 1's summary and info box */ 92 | h1.box { 93 | margin-top: 0.1em; 94 | margin-bottom: 0.4em; 95 | } 96 | .summary p { 97 | margin-top: 0.2em; 98 | margin-bottom: 0.5em; 99 | } 100 | 101 | .info-box { 102 | background-color: #F5DD9E; 103 | padding: 14px 14px; 104 | margin: 0.2em 0 0 0; 105 | line-height: 125%; 106 | border-radius: 2px; 107 | font-size: 0.9em; 108 | } 109 | .info-box thead { 110 | display: none; 111 | } 112 | .info-box col:first-child{ 113 | width: 12%; 114 | text-align: center; 115 | } 116 | .info-box a { 117 | text-decoration: none; 118 | color: black; 119 | } 120 | 121 | /* style content tables */ 122 | .col-6 thead { 123 | display: none; 124 | } 125 | 126 | /* set the width of the first column in the tables */ 127 | .col-6 td:first-child { 128 | width: 18%; 129 | } 130 | 131 | .col-6 td { 132 | font-size: 0.95em; 133 | vertical-align: top; 134 | line-height: 120%; 135 | padding: 3px 0; 136 | } 137 | 138 | td { 139 | vertical-align: top; 140 | text-align: left; 141 | } 142 | 143 | .cv-entry-title { 144 | font-weight: bold; 145 | } 146 | 147 | 148 | /* if you use links to footnotes*/ 149 | .footnote-ref sup { 150 | vertical-align: top; 151 | margin-left: 2px; 152 | } 153 | 154 | /*links at the bottom */ 155 | .footnotes p { 156 | margin-block-start: 0.8em; 157 | margin-block-end: 0.8em; 158 | line-height: 0.9; 159 | } 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `pagedownCV` 2 | 3 | This repo provides three R Markdown templates for creating a 4 | dynamically generated CV using `R Markdown`. The templates use the 5 | [`pagedown`](https://github.com/rstudio/pagedown) library for the paged versions. 6 | 7 | ## How to use 8 | First, download or clone this repo. 9 | 10 | ### Knitting 11 | Open and knit one of the .Rmd files: 12 | 13 | - [**cv-2-column.Rmd**](https://ulyngs.github.io/pagedownCV/examples/cv-2-column.pdf) (creates a PDF when knit) \ 14 | 15 | 16 | - [**cv-academic.Rmd**](https://ulyngs.github.io/pagedownCV/examples/cv-academic.pdf) (creates a PDF when knit; traditional one-column format, appropriate for a traditionally laid out academic CV) \ 17 | 18 | 19 | - [**cv-website.Rmd**](https://ulyngs.github.io/pagedownCV/examples/cv-website.html) (creates an HTML file when knit; example of a CV laid out as it might be used on a website) \ 20 | 21 | 22 | ## Fiddling with page breaks 23 | 24 | In both the Academic CV and the 2-column CV, you can write 25 | `
` in your R Markdown source file to 26 | add a page break at this location. 27 | 28 | In the Academic CV, cell content that’s overflowing 29 | to the next page might get placed in the wrong column on the subsequent page 30 | (I’ve flagged it in [this 31 | issue](https://github.com/rstudio/pagedown/issues/299) for `pagedown` — 32 | hopefully there’s a solution soon). If you encounter this, you can work 33 | around it by manually inserting a page break in the row before. There’s 34 | a convenience function for this: 35 | 36 | ```r 37 | # Adds a page break after a row that contains a specified text in a specified column 38 | a_data_frame |> 39 | manual_page_break_after_row("name-of-a-column", "Text in that column") 40 | ``` 41 | 42 | If you look through **cv-academic.Rmd** then you'll see examples of this throughout. 43 | 44 | Similarly, you'll find convenience functions for replacing authors' first names with an initial followed by a dot, and for replacing a repeated year with an empty string. 45 | 46 | The convenience functions live in **R/utility-functions.R** 47 | 48 | ## Motivation 49 | 50 | The [`pagedown`](https://github.com/rstudio/pagedown) package lets you 51 | output an R Markdown file to paged HTML content which can then be saved 52 | as PDF. This means you can use R Markdown to programmatically pull out 53 | content from some spreadsheet with our CV data, output it to a paged 54 | format, do the styling with CSS, then save as PDF and/or host it online. 55 | Brilliant! 56 | 57 | Nick Strayer’s excellent [data-driven 58 | cv](http://nickstrayer.me/datadrivencv/)) package, as well as the base 59 | format for resumés provided by `pagedown` (`pagedown::html_resume`), 60 | make this easy to do. 61 | 62 | However, their available layouts don’tsuit my taste (especially for an 63 | academic CV). 64 | 65 | ## Where do the layouts come from? 66 | 67 | When I designed the Academic CV, I was particularly inspired by the 68 | resumés of [Matthew Kay](http://www.mjskay.com/) and [Elena 69 | Agapie](https://eagapie.com/pubs/cv.pdf). I use this format for academic 70 | purposes. 71 | 72 | The two-column one, I originally designed in Microsoft Word, with the 73 | intention of using it for brief, professional 2-page resumés where you 74 | need to squeeze content into less space. 75 | 76 | ## How does it work? 77 | 78 | Each resumé is generated by an R Markdown file that pulls in content 79 | from the included excel file **cv_data.xlsx**. 80 | 81 | The sample content comes from [this google 82 | sheet](https://docs.google.com/spreadsheets/d/1ta71CAGkcLqm-W1UdVRA_JJSddWV2TsrRZsCnQlmOis/edit?usp=sharing), 83 | which holds my CV information along with most of the content for [my 84 | website](https://ulriklyngs.com/). 85 | 86 | Enjoy!! 87 | -------------------------------------------------------------------------------- /css/academic_cv.css: -------------------------------------------------------------------------------- 1 | /* THIS CREATES BASIC PAGED LAYOUT IN COMBINATION WITH PAGED.HTML */ 2 | @page{ 3 | size: A4 portrait; 4 | } 5 | 6 | :root{ 7 | --page-width: 8.5in; 8 | --pagedjs-margin-right: 0.7in; 9 | --pagedjs-margin-left: 0.7in; 10 | --pagedjs-margin-top: 0.5in; 11 | --pagedjs-margin-bottom: 0.5in; 12 | --root-font-size: 11pt; 13 | --viewer-pages-spacing: 12px; 14 | --viewer-shadow-color: #313131; /* this marks the pages */ 15 | --my-darkgray: #929598; 16 | --indentation: 18%; 17 | } 18 | 19 | /* Paged.js viewer */ 20 | @media screen { 21 | body { 22 | background-color: var(--viewer-background-color); 23 | margin: 0; /* for mobile */ 24 | width: calc(var(--pagedjs-width) + 2 * var(--viewer-pages-spacing)); /* for mobile */ 25 | } 26 | .pagedjs_pages { 27 | max-width: var(--pagedjs-width); 28 | margin: 0 auto; 29 | display: flex; 30 | flex-direction: column; 31 | } 32 | .pagedjs_page { 33 | box-shadow: 0 0 calc(0.66667 * var(--viewer-pages-spacing)) var(--viewer-shadow-color); 34 | margin: var(--viewer-pages-spacing) 0; 35 | } 36 | } 37 | @media screen and (min-width: 8.5in) { 38 | /* not a mobile */ 39 | body { 40 | margin: auto; 41 | width: unset; 42 | } 43 | } 44 | 45 | /* create ability to insert pagebreaks with br.pagebreak */ 46 | br.pageBreak { 47 | page-break-after: always; 48 | } 49 | 50 | body { 51 | font-family: "Avenir"; 52 | } 53 | 54 | p, li { 55 | font-size: var(--root-font-size); 56 | line-height: 125%; 57 | } 58 | 59 | /* section headers */ 60 | h2 { 61 | margin-top: 0.6em; 62 | margin-bottom: 0.4em; 63 | font-size: 1.5em; 64 | font-weight: bold; 65 | margin-left: var(--indentation); 66 | } 67 | h3 { 68 | margin-left: var(--indentation); 69 | margin-bottom: 0.6em; 70 | } 71 | .conference-presentations-omitted { 72 | margin-left: var(--indentation); 73 | font-style: italic; 74 | } 75 | 76 | /****** Page numbers ******/ 77 | @page { 78 | @top-left { 79 | content: none; 80 | } 81 | @top-right { 82 | content: none; 83 | } 84 | @bottom-right { 85 | content: counter(page) var(--last-updated); 86 | } 87 | } 88 | 89 | .pagedjs_margin-bottom-right { 90 | text-align: left !important; 91 | margin-left: var(--indentation); 92 | color: var(--my-darkgray); 93 | font-size: 0.8em; 94 | margin-bottom: 20px; 95 | } 96 | 97 | /***** STYLE HEADING *******/ 98 | .title-and-contact { 99 | display: grid; 100 | grid-template-columns: 70% 30%; 101 | border-style: none none groove none; 102 | margin-bottom: 1.8em; 103 | padding-bottom: 0.5em; 104 | } 105 | 106 | .title h1 { 107 | text-align: left; 108 | font-size: 2.5em; 109 | padding: 0; 110 | margin: 0% 0% 0% 24.5%; 111 | } 112 | 113 | .contact-info a { 114 | color: black; 115 | text-decoration: none; 116 | } 117 | .contact-info { 118 | font-size: 0.5em; 119 | text-align: right; 120 | } 121 | 122 | /***** STYLE INTERESTS STATEMENT *******/ 123 | /* research interests */ 124 | .research-interests { 125 | display: grid; 126 | grid-template-columns: 18.5% 81.5%; 127 | } 128 | .interests p { 129 | line-height: 135%; 130 | } 131 | .interests a { 132 | text-decoration: none; 133 | } 134 | 135 | 136 | /***** STYLE CV ENTRIES *******/ 137 | /* overall styling for cv-entries within a table */ 138 | .cv-entries table { 139 | border-spacing: 0; 140 | margin: 0.1em 0 1.5em 0; 141 | width: 100%; 142 | } 143 | 144 | .cv-entries thead { 145 | display: none; 146 | } 147 | 148 | .cv-entries td { 149 | vertical-align: top; 150 | line-height: 125%; 151 | padding-bottom: 0.5em; 152 | font-size: var(--root-font-size); 153 | } 154 | 155 | /* make titles bold */ 156 | .cv-entry-title { 157 | font-weight: bold; 158 | } 159 | 160 | .cv-entries a, 161 | .footnotes a { 162 | color: #005EA3; 163 | } 164 | 165 | /* footnote links */ 166 | a.footnote-ref { 167 | text-decoration: none; 168 | } 169 | 170 | .footnote-ref sup { 171 | vertical-align: top; 172 | margin-left: 1px; 173 | font-weight: normal; 174 | } 175 | 176 | /* basic styling for first column in two-column cv entries */ 177 | .cv-entries td:first-child { 178 | width: var(--indentation); 179 | text-align: left; 180 | color: var(--my-darkgray); 181 | font-size: 10pt; 182 | } 183 | 184 | /* basic styling for two first columns in three-column cv entries */ 185 | .cv-entries .three-split td:first-child { 186 | width: 9%; 187 | text-align: left; 188 | color: var(--my-darkgray); 189 | } 190 | .cv-entries .three-split td:nth-child(2) { 191 | width: 9%; 192 | text-align: left; 193 | color: var(--my-darkgray); 194 | font-size: 10pt; 195 | } 196 | 197 | /*** style the publication entries ***/ 198 | /* set width for the publication info statement */ 199 | .grid-container-publications { 200 | display: grid; 201 | grid-template-columns: 49% 51%; 202 | } 203 | .conference-publication-heading h3 { 204 | margin-block-start: 0 !important; 205 | margin-top: 15px !important; 206 | margin-left: 37%; 207 | } 208 | .conference-note h3 { 209 | margin-block-start: 0 !important; 210 | margin-top: 21px !important; 211 | color: var(--my-darkgray); 212 | font-size: 0.8em; 213 | } 214 | 215 | .review-note { 216 | color: var(--my-darkgray); 217 | font-size: 0.8em; 218 | } 219 | 220 | /* make award texts red */ 221 | .publication-award { 222 | color: #b8162b; 223 | } 224 | 225 | 226 | /* don't show a horizontal rule before the final links */ 227 | hr { 228 | display: none; 229 | } 230 | 231 | span.talk-title { 232 | padding-top: 0.2em; 233 | } 234 | 235 | .cv-entry-translation { 236 | font-style: italic; 237 | } 238 | -------------------------------------------------------------------------------- /cv-2-column.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Ulrik Lyngs
Curriculum vitae" 3 | output: 4 | pagedown::html_paged: 5 | css: ["css/bootstrap-5.2.1.min.css", "css/two-col-cv.css"] 6 | number_sections: false 7 | knit: pagedown::chrome_print 8 | --- 9 | 10 | ```{r setup, include=FALSE} 11 | knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE) 12 | library(tidyverse) 13 | library(knitr) 14 | library(glue) 15 | library(fontawesome) 16 | library(readxl) 17 | library(lubridate) 18 | source("R/utility-functions.R") 19 | 20 | cv_entries <- read_excel("cv_data.xlsx", sheet = "cv_entries") |> 21 | mutate(year = year(date)) 22 | publications <- read_excel("cv_data.xlsx", sheet = "publications") |> 23 | mutate(year = year(date)) 24 | ``` 25 | 26 | 27 | ::: {.row} 28 | 29 | :::::: {.col-8 .summary} 30 | 31 | # Research summary 32 | **Aims**---to develop and evaluate design patterns that help people self-regulate their use of smartphones and laptops.\ 33 | **Methods**---controlled studies, user surveys, interviews, co-design workshops, web scraping, behaviour logging. 34 | 35 | **I am passionate about** open and transparent research. Since 2018, I have shared materials, analysis scripts, and data for all my first-authored work, and written my papers as reproducible documents in [R Markdown](https://rmarkdown.rstudio.com). 36 | 37 | :::::: 38 | 39 | :::::: {.col-4} 40 | 41 | # Contact {.box} 42 | 43 | ::::::::: {.info-box} 44 | 45 | | Center | Left | 46 | |:------:|:-----| 47 | | `r fa("map-marker-alt")`| Dept. of Computer Science, University of Oxford | 48 | | `r fa("envelope")` | ulrik.lyngs@cs.ox.ac.uk | 49 | | `r fa("globe")` | [ulriklyngs.com](https://ulriklyngs.com) | 50 | | `r fa("github")` | [ulyngs](https://github.com/ulyngs) | 51 | 52 | ::::::::: 53 | 54 | :::::: 55 | 56 | ::: 57 | 58 | ::: {.row} 59 | 60 | :::::: {.col-6 .left} 61 | 62 | # Awards and honours 63 | 64 | ```{r} 65 | cv_entries |> 66 | filter(type == 'awards', short_cv == "y") |> 67 | mutate(what = glue("{what}, {where}")) |> 68 | select(year, what) |> 69 | kable() 70 | ``` 71 | 72 | # Selected major grants 73 | 74 | ```{r} 75 | cv_entries |> 76 | filter(type == 'major_grants', short_cv == "y") |> 77 | mutate(what = if_else(is.na(additional_info), 78 | glue("{what} ({additional_info2}), {where}"), 79 | glue("{what} ({additional_info2}), {where}"))) |> 80 | select(year, what) |> 81 | kable() 82 | ``` 83 | 84 | :::::: 85 | 86 | :::::: {.col-6 .right} 87 | 88 | # Education 89 | 90 | ```{r} 91 | cv_entries |> 92 | filter(type == 'education') |> 93 | mutate(what = glue("{what}, {where}")) |> 94 | select(year, what) |> 95 | kable() 96 | ``` 97 | 98 | # Selected teaching experience 99 | ```{r} 100 | cv_entries |> 101 | filter(type == 'teaching', short_cv == "y") |> 102 | mutate(what = glue("{what}, {where}
", 103 | "{additional_info}")) |> 104 | select(year, what) |> 105 | kable() 106 | ``` 107 | 108 | :::::: 109 | 110 | ::: 111 | 112 | 113 |
114 | 115 | ::: {.row .pagetwo} 116 | 117 | :::::: {.col-6 .left} 118 | 119 | # Selected talks {.talks} 120 | ```{r} 121 | cv_entries |> 122 | filter(type == 'talk', short_cv == "y") |> 123 | # add commas as appropriate 124 | mutate(where = if_else(!is.na(where) & !is.na(institution), glue(", {where}"), where), 125 | department = if_else(!is.na(department), glue(", {department}"), department), 126 | additional_info = if_else(!is.na(additional_info), glue(", {additional_info}"), "")) |> 127 | # add slide and video links 128 | mutate(slides = if_else(!is.na(slides), glue("Slides"), ""), 129 | video = if_else(!is.na(video), glue("Video"), "")) |> 130 | # put it all together 131 | mutate(what = glue("{institution}{where}{department}{additional_info}
", 132 | "{what}", 133 | .na = "")) |> 134 | select(year, what) |> 135 | kable() 136 | ``` 137 | 138 | :::::: 139 | 140 | :::::: {.col-6 .right} 141 | 142 | # Selected media & panels 143 | ```{r} 144 | cv_entries |> 145 | filter(type == 'media' | type == 'talk-podcast' | type == 'talk-panel') |> 146 | filter(short_cv == "y") |> 147 | # format the translation 148 | mutate(what_translation = if_else(!is.na(what_translation), glue("({what_translation})"), what_translation)) |> 149 | replace_na(list(what_translation = "")) |> 150 | # put it all together 151 | mutate(what = glue("{where}, {what} *{what_translation}*")) |> 152 | select(year, what) |> 153 | kable() 154 | ``` 155 | 156 | # Professional development {.prof-dev} 157 | ```{r} 158 | cv_entries |> 159 | filter(type == 'prof-dev') |> 160 | mutate(what = glue("{what}, {where}")) |> 161 | select(year, what) |> 162 | kable() 163 | ``` 164 | 165 | # Selected service 166 | ```{r} 167 | cv_entries |> 168 | filter(type == 'service', short_cv == "y") |> 169 | mutate(where = if_else(!is.na(url), glue("[{where}]({url})"), where)) |> 170 | mutate(what = glue("{what} {where}")) |> 171 | arrange(desc(date_end), desc(date)) |> 172 | select(year, what) |> 173 | kable() 174 | ``` 175 | 176 | :::::: 177 | ::: 178 | -------------------------------------------------------------------------------- /cv-website.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | html_document: 4 | css: css/website-cv.css 5 | toc: true 6 | toc_float: true 7 | --- 8 | 9 | ```{r setup, include=FALSE} 10 | knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE) 11 | library(tidyverse) 12 | library(knitr) 13 | library(glue) 14 | library(fontawesome) 15 | library(DT) 16 | library(lubridate) 17 | library(readxl) 18 | source("R/utility-functions.R") 19 | 20 | cv_entries <- read_excel("cv_data.xlsx", sheet = "cv_entries") |> 21 | mutate(year = year(date)) 22 | publications <- read_excel("cv_data.xlsx", sheet = "publications") |> 23 | mutate(year = year(date)) 24 | ``` 25 | 26 | # Summary 27 | 28 | I am passionate about using insights from the behavioural neurosciences to **design digital technology that is sensitive to human limitations and biases**, particularly in relation to attention and self-regulation. 29 | 30 | My **academic and professional background** is highly interdisciplinary, and I try to integrate best practices from many work spheres, such as using tools from large-scale event management to structure academic projects. 31 | 32 | My **personal interests** include backpacking, samatha meditation, dancing (Cuban salsa and swing), surfing, and playing music (often with my concept band the [Karaoke Collective](https://karaokecollective.com)). 33 | 34 | [`r fa("download")` CV as PDF](https://ulriklyngs.com/pdfs/academic_cv.pdf) 35 | 36 | # Academia {.domain-header} 37 | ## Research positions 38 | ```{r} 39 | cv_entries |> 40 | filter(type == 'research_positions', is.na(exclude)) |> 41 | mutate(what = str_c("", what, "
", 42 | where)) |> 43 | select(year, what) |> 44 | kable(escape = FALSE) 45 | ``` 46 | 47 | ## Education 48 | ```{r} 49 | cv_entries |> 50 | filter(type == 'education') |> 51 | mutate(what = str_c("", what, ", ", where, 52 | "
", additional_info, 53 | "
", additional_info2)) |> 54 | select(year, what) |> 55 | kable(escape = FALSE) 56 | 57 | ``` 58 | 59 | ## Awards and honours 60 | ```{r} 61 | cv_entries |> 62 | filter(type == 'awards') |> 63 | filter(!(website == "n") | is.na(website)) |> 64 | mutate(what = if_else( 65 | !is.na(where), 66 | str_c("", what, ", ", where), 67 | str_c("", what, "")) 68 | ) |> 69 | blank_year_when_repeated() |> 70 | select(year, what) |> 71 | kable(align = c("l", "l"), escape = FALSE) 72 | ``` 73 | 74 | ## Grants and funding 75 | ```{r} 76 | cv_entries |> 77 | filter(type == 'minor_grants' | type == 'major_grants', 78 | is.na(exclude)) |> 79 | filter(!(website == "n") | is.na(website)) |> 80 | mutate(what = if_else( 81 | !is.na(additional_info), 82 | str_c("", what, ", ", where, "
", additional_info), 83 | str_c("", what, ", ", where)) 84 | ) |> 85 | arrange(date_end) |> 86 | blank_year_when_repeated() |> 87 | select(year, what) |> 88 | kable(align = c("l", "l"), escape = FALSE) 89 | ``` 90 | 91 | 92 | ## Research dissemination {.tabset .output-header} 93 | ### Talks 94 | ```{r} 95 | cv_entries |> 96 | filter(type == 'talk') |> 97 | mutate(where = str_replace(where, "\\[([^]]*)\\]\\(([^\\s^\\)]*)[\\s\\)]", "\\1")) |> 98 | mutate(what = glue::glue("{what}, {where}", .na = "")) |> 99 | select(date, what) |> 100 | arrange(desc(date)) |> 101 | mutate(date = str_c(year(date), 102 | ", ", 103 | month(date, label = TRUE))) |> 104 | datatable(rownames = FALSE, escape = FALSE, class = 'row-border', options = list( 105 | pageLength = 10 106 | )) 107 | 108 | ``` 109 | 110 | ### Posters {.posters} 111 | ```{r} 112 | cv_entries |> 113 | filter(type == 'poster') |> 114 | mutate(what = str_c("*", what, "*, ", where)) |> 115 | select(year, what) |> 116 | arrange(desc(year)) |> 117 | kable() 118 | ``` 119 | 120 | ### Media {.media} 121 | ```{r} 122 | cv_entries |> 123 | filter(type == 'media') |> 124 | select(year, what) |> 125 | kable() 126 | ``` 127 | 128 | 129 | 130 | ## Teaching experience 131 | 132 | ```{r} 133 | cv_entries |> 134 | filter(type == 'teaching') |> 135 | mutate(what = ifelse(!is.na(additional_info), 136 | str_c("", what, "
", where, "
", additional_info), 137 | str_c("", what, "
", where))) |> 138 | select(year, what) |> 139 | kable(escape = FALSE) 140 | 141 | ``` 142 | 143 | ## Service 144 | ```{r} 145 | cv_entries |> 146 | filter(type == 'service') |> 147 | mutate(where = ifelse(!is.na(url), str_c("[", where, "](", url, ")"), where)) |> 148 | mutate(what = ifelse(!is.na(additional_info), 149 | str_c("", what, 150 | "
", 151 | where), 152 | str_c("", what, 153 | "
", 154 | where))) |> 155 | arrange(desc(date_end), desc(date)) |> 156 | select(year, what) |> 157 | kable(escape = FALSE) 158 | 159 | ``` 160 | 161 | 162 | # Work 163 | ## Selected employments 164 | ```{r} 165 | cv_entries |> 166 | filter(type == 'work', is.na(website)) |> 167 | mutate(additional_info = replace_na(additional_info, "")) |> 168 | mutate(what = str_c("", what, "
", where, "
", additional_info)) |> 169 | arrange(desc(date)) |> 170 | select(year, what) |> 171 | kable(escape = FALSE) 172 | ``` 173 | 174 | ## Professional development 175 | ```{r} 176 | cv_entries |> 177 | filter(type == 'prof-dev') |> 178 | mutate(what = str_c("", what, ", ", where)) |> 179 | select(year, what) |> 180 | kable(align = c("l", "l"), escape = FALSE) 181 | ``` 182 | 183 | # Technical 184 | ## Skills 185 | ```{r} 186 | cv_entries |> 187 | filter(type == 'technical') |> 188 | mutate(icon = case_when( 189 | str_detect(what, "data analysis") ~ "chart-bar", 190 | str_detect(what, "research") ~ "flask", 191 | str_detect(what, "Web") ~ "laptop" 192 | )) |> 193 | rowwise() |> 194 | mutate(what = str_c(fa(icon), " ", what, " ", additional_info)) |> 195 | select(type, what) |> 196 | mutate(type = "") |> 197 | kable(escape=FALSE) 198 | ``` 199 | 200 | ## R packages 201 | ```{r} 202 | cv_entries |> 203 | filter(type == 'software-rstats' & is.na(exclude)) |> 204 | replace_na(list(where = "", additional_info = "")) |> 205 | mutate(what = glue("{what}, {where}")) |> 206 | arrange(desc(year)) |> 207 | mutate(row_number = row_number()) |> 208 | select(year, what) |> 209 | kable(escape=FALSE) 210 | ``` 211 | 212 | ## Apps 213 | ```{r} 214 | cv_entries |> 215 | filter(type == 'software-browser-extensions' & is.na(exclude)) |> 216 | replace_na(list(where = "", additional_info = "")) |> 217 | mutate(what = glue("{what}, {where}")) |> 218 | arrange(desc(year)) |> 219 | mutate(row_number = row_number()) |> 220 | select(year, what) |> 221 | kable(escape=FALSE) 222 | ``` 223 | 224 | 225 | # Personal 226 | ## Skills & volunteering 227 | ```{r} 228 | cv_entries |> 229 | filter(type == 'volunteering') |> 230 | mutate(what = str_c("", what, "
", where)) |> 231 | mutate(what = ifelse(!is.na(additional_info), str_c(what, "
", additional_info), what)) |> 232 | arrange(desc(date_end)) |> 233 | select(year, what) |> 234 | kable(escape = FALSE) 235 | ``` 236 | -------------------------------------------------------------------------------- /cv-academic.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | pagedown::html_paged: 4 | number_sections: false 5 | css: ["css/academic_cv.css"] 6 | links-to-footnotes: true 7 | knit: pagedown::chrome_print 8 | --- 9 | 10 | 15 | 16 | ```{r setup, include=FALSE} 17 | knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE) 18 | library(tidyverse) 19 | library(knitr) 20 | library(glue) 21 | library(fontawesome) 22 | library(readxl) 23 | library(lubridate) 24 | source("R/utility-functions.R") 25 | 26 | cv_entries <- read_excel("cv_data.xlsx", sheet = "cv_entries") |> 27 | # provide range for entries with a year_end (e.g. '2013-2016') 28 | mutate(year_end = if_else(date_end == "present", "present", str_sub(date_end, start = 1, end = 4)), 29 | year = if_else(!is.na(year_end) & year(date) != year_end, 30 | str_c(year(date), " --- ", year_end), 31 | as.character(year(date)))) 32 | 33 | publications <- read_excel("cv_data.xlsx", sheet = "publications") |> 34 | mutate(year = year(date), 35 | venue_abbrev = if_else(is.na(venue_abbrev), "", str_c(venue_abbrev, ": "))) 36 | 37 | ``` 38 | 39 | ::: {.title-and-contact} 40 | 41 | :::::: {.title} 42 |

Ulrik Lyngs

43 | :::::: 44 | 45 | :::::: {.contact-info} 46 | ulrik.lyngs@cs.ox.ac.uk \ 47 | https://ulriklyngs.com 48 | :::::: 49 | 50 | ::: 51 | 52 | ## Research summary 53 | ::: {.research-interests} 54 | 55 | :::::: {.spacer} 56 | :::::: 57 | 58 | :::::: {.interests} 59 | **Aims---**to develop and evaluate design patterns that help people self-regulate their use of smartphones and laptops. 60 |
61 | **Methods---**controlled studies, user surveys, interviews, co-design workshops, web scraping, behaviour logging. 62 | 63 | **I am passionate about** open and transparent research. 64 | Since 2018, I have shared materials, analysis scripts, and data for all my first-authored work, and written my papers as reproducible documents in [R Markdown](https://rmarkdown.rstudio.com). 65 | :::::: 66 | ::: 67 | 68 | ::: {.cv-entries} 69 | ## Research positions 70 | ```{r} 71 | cv_entries |> 72 | filter(type == 'research_positions', is.na(exclude)) |> 73 | mutate(what = glue("{what}
", 74 | "{where}" 75 | )) |> 76 | select(year, what) |> 77 | kable() 78 | ``` 79 | 80 | 81 | 82 | ## Education 83 | ```{r} 84 | cv_entries |> 85 | filter(type == 'education') |> 86 | mutate(what = glue("{what}, {where}
", 87 | "{additional_info}
", 88 | "{additional_info2}")) |> 89 | select(year, what) |> 90 | kable() 91 | ``` 92 | 93 | ## Major grants and funding 94 | ```{r} 95 | cv_entries |> 96 | filter(type == 'major_grants') |> 97 | mutate(what = if_else(is.na(additional_info), 98 | glue("{what} ({additional_info2}), {where}"), 99 | glue("{what} ({additional_info2}), {where}.
{additional_info}"))) |> 100 | select(year, what) |> 101 | manual_page_break_after_row("what", "Impact Acceleration") |> 102 | kable() 103 | ``` 104 | 105 | ## Awards & honours 106 | ```{r} 107 | cv_entries |> 108 | filter(type == 'awards') |> 109 | mutate(what = glue("{what}, {where}")) |> 110 | blank_year_when_repeated() |> 111 | select(year, what) |> 112 | kable() 113 | ``` 114 | 115 | 116 | ## Publications {.three-split} 117 | 118 | :::::: {.grid-container-publications} 119 | ::::::::: {.conference-publication-heading} 120 |

Conference publications
(fully reviewed, archival)

121 | ::::::::: 122 | 123 | ::::::::: {.conference-note} 124 |

In computer science, top-tier conferences (<30% acceptance rate) are as, or more impactful than journals, see doi.org/fgjt2h

125 | ::::::::: 126 | :::::: 127 | 128 |
129 | 130 | ```{r} 131 | publications_formatted <- publications |> 132 | mutate(authors = map_chr(authors, replace_first_names_in_author_list_with_initial_and_dot), # e.g. U. Lyngs instead of Ulrik Lyngs 133 | authors = str_replace(authors, "U\\. Lyngs", "*U\\. Lyngs*")) # make my name italic 134 | 135 | publications_formatted |> 136 | filter(type == "conference paper") |> 137 | # insert appropriate icon for paper awards (and start with a line break) 138 | mutate(award = case_when( 139 | !is.na(award) & str_detect(award, "honourable") ~ glue("
", fa("award"), " {award}"), 140 | !is.na(award) ~ glue("
", fa("trophy"), " {award}"), 141 | TRUE ~ "" 142 | )) |> 143 | # create citations 144 | mutate(citation = glue("{title}
", 145 | "{authors}
", 146 | "{venue_abbrev}{venue}{award}")) |> 147 | # number entries with a prefix 148 | add_leading_zeroes_and_letter("C") |> 149 | blank_year_when_repeated() |> 150 | select(year, number, citation) |> 151 | manual_page_break_after_row("citation", "Third party tracking") |> 152 | knitr::kable() 153 | ``` 154 | 155 | 156 |

Journal articles (fully reviewed, archival)

157 | 158 | ```{r} 159 | publications_formatted |> 160 | filter(type == "journal article") |> 161 | mutate(citation = glue("{title}
", 162 | "{authors}
", 163 | "{venue_abbrev}{venue}")) |> 164 | add_leading_zeroes_and_letter("J") |> 165 | blank_year_when_repeated() |> 166 | select(year, number, citation) |> 167 | knitr::kable() 168 | ``` 169 | 170 | 171 | 172 | ### Book chapters 173 | 174 | ```{r} 175 | publications_formatted |> 176 | filter(type == "book chapter") |> 177 | mutate(citation = glue("{title}
", 178 | "{authors}
", 179 | "{venue_abbrev}{venue}")) |> 180 | add_leading_zeroes_and_letter("B") |> 181 | blank_year_when_repeated() |> 182 | select(year, number, citation) |> 183 | knitr::kable() 184 | 185 | ``` 186 | 187 |

Extended abstracts and workshop papers
(lightly reviewed)

188 | 189 | ```{r} 190 | publications_formatted |> 191 | filter(type == "extended abstract" | type == "workshop paper") |> 192 | mutate(citation = glue("{title}
", 193 | "{authors}
", 194 | "{venue_abbrev}{venue}")) |> 195 | add_leading_zeroes_and_letter("A") |> 196 | blank_year_when_repeated() |> 197 | select(year, number, citation) |> 198 | manual_page_break_after_row("citation", "Designing to Support Autonomy") |> 199 | knitr::kable() 200 | ``` 201 | 202 | ## Publicly available research code & data 203 | ```{r} 204 | pubs_w_number <- publications |> 205 | select(type, authors, date, year, title, materials) |> 206 | mutate(inclusive_type = if_else(type == "workshop paper" | type == "extended abstract", "abstract", type)) |> 207 | group_by(inclusive_type) |> 208 | arrange(date) |> 209 | mutate(number = row_number(), 210 | number = case_when( 211 | inclusive_type == "conference paper" ~ glue("[C{number}]"), 212 | inclusive_type == "journal article" ~ glue("[J{number}]"), 213 | inclusive_type == "abstract" ~ glue("[A{number}]"), 214 | TRUE ~ "" 215 | )) 216 | 217 | public_code <- cv_entries |> 218 | filter(type == 'open_research' & is.na(exclude)) |> 219 | select(what, additional_info) |> 220 | rename(title = additional_info) |> 221 | left_join(pubs_w_number) 222 | 223 | public_code |> 224 | mutate(what = glue("{what} {number}
", 225 | "{authors}
", 226 | "{materials}")) |> 227 | arrange(desc(date)) |> 228 | select(year, what) |> 229 | blank_year_when_repeated() |> 230 | manual_page_break_after_row("what", "Before and after GDPR") |> 231 | kable() 232 | ``` 233 | 234 | ## Research dissemination 235 | ### Press 236 | ```{r} 237 | cv_entries |> 238 | filter(type == 'media') |> 239 | mutate(what = if_else(is.na(what_translation), 240 | glue("{where}, {what}"), 241 | glue("{where}, {what} (*{what_translation}*)"))) |> 242 | #manual_page_break_after_row("what", "Kortsluttet") |> 243 | select(year, what) |> 244 | kable() 245 | ``` 246 | 247 | 248 | 249 | ### Podcasts {.three-split} 250 | ```{r} 251 | cv_entries |> 252 | filter(type == 'talk-podcast') |> 253 | mutate(what_translation = ifelse(!is.na(what_translation), str_c("(", what_translation, ")"), what_translation)) |> 254 | replace_na(list(what_translation = "")) |> 255 | mutate(what = glue("{where}, {what} {what_translation}")) |> 256 | add_leading_zeroes_and_letter("Pod") |> 257 | blank_year_when_repeated() |> 258 | select(year, number, what) |> 259 | knitr::kable() 260 | 261 | ``` 262 | 263 | 264 |
265 | 266 | ### Talks {.three-split} 267 | 268 | :::::: {.conference-presentations-omitted} 269 | All first-authored conference publications listed above were also presented as talks at their respective conferences and are not listed again in this section. 270 | :::::: 271 | 272 | ```{r} 273 | cv_entries |> 274 | filter(type == 'talk', is.na(exclude)) |> 275 | # add commas as appropriate 276 | mutate(where = if_else(!is.na(where) & !is.na(institution), glue(", {where}"), where), 277 | department = if_else(!is.na(department), glue(", {department}"), department), 278 | slides = if_else(!is.na(slides), glue("Slides"), ""), 279 | video = if_else(!is.na(video), glue("Video"), "")) |> 280 | mutate(additional_info = if_else(!is.na(additional_info), glue(", {additional_info}"), "")) |> 281 | mutate(what_translation = ifelse(!is.na(what_translation), str_c("(", what_translation, ")"), what_translation)) |> 282 | replace_na(list(what_translation = "")) |> 283 | mutate(what = glue("{institution}{where}{department}{additional_info}
", 284 | "{what} {what_translation}", 285 | .na = "")) |> 286 | add_leading_zeroes_and_letter("T") |> 287 | blank_year_when_repeated() |> 288 | select(year, number, what) |> 289 | manual_page_break_after_row("what", "dvanced Visual Interfaces") |> 290 | manual_page_break_after_row("what", "nline lecture series") |> 291 | manual_page_break_after_row("what", "Distraction and Self-Regulation in Social Machines") |> 292 | knitr::kable() 293 | 294 | ``` 295 | 296 | ### Discussion panels {.three-split} 297 | ```{r} 298 | cv_entries |> 299 | filter(type == 'talk-panel') |> 300 | mutate(additional_info = if_else(is.na(additional_info), "", additional_info)) |> 301 | mutate(what = glue("{where}, {what}, {additional_info}")) |> 302 | add_leading_zeroes_and_letter("D") |> 303 | blank_year_when_repeated() |> 304 | select(year, number, what) |> 305 | knitr::kable() 306 | 307 | ``` 308 | 309 | 310 | 311 | ### Poster presentations {.three-split} 312 | ```{r} 313 | cv_entries |> 314 | filter(type == 'poster') |> 315 | mutate(where = if_else(!is.na(where) & !is.na(institution), glue(", {where}"), where), 316 | department = if_else(!is.na(department), glue(", {department}"), department)) |> 317 | mutate(additional_info = if_else(!is.na(additional_info), glue(", {additional_info}"), "")) |> 318 | mutate(what = glue("{institution}{where}{department}{additional_info}
", 319 | "{what}", 320 | .na = "")) |> 321 | add_leading_zeroes_and_letter("P") |> 322 | blank_year_when_repeated() |> 323 | select(year, number, what) |> 324 | manual_page_break_after_row("what", "2018 CHI Conference") |> 325 | knitr::kable() 326 | 327 | ``` 328 | 329 | 330 | ### Blog posts {.three-split} 331 | ```{r} 332 | cv_entries |> 333 | filter(type == 'pop-writing') |> 334 | mutate(what = glue("{where}, {what}")) |> 335 | add_leading_zeroes_and_letter("B") |> 336 | blank_year_when_repeated() |> 337 | select(year, number, what) |> 338 | knitr::kable() 339 | ``` 340 | 341 | 342 | 343 | 344 | ## Teaching experience 345 | ```{r} 346 | cv_entries |> 347 | filter(type == 'teaching') |> 348 | mutate(what = glue("{what}, {where}
", 349 | "{additional_info}")) |> 350 | select(year, what) |> 351 | manual_page_break_after_row("what", "Instructor, Digital Humanities") |> 352 | kable() 353 | ``` 354 | 355 | 356 | 357 | ## Software 358 | ### R packages 359 | ```{r} 360 | cv_entries |> 361 | filter(type == 'software-rstats' & is.na(exclude)) |> 362 | replace_na(list(where = "", additional_info = "")) |> 363 | mutate(what = glue("{what}, {where}")) |> 364 | arrange(desc(year)) |> 365 | mutate(row_number = row_number()) |> 366 | select(year, what) |> 367 | kable() 368 | ``` 369 | 370 | 371 | 372 | ### Apps 373 | ```{r} 374 | cv_entries |> 375 | filter(type == 'software-browser-extensions' & is.na(exclude)) |> 376 | replace_na(list(where = "", additional_info = "")) |> 377 | mutate(what = glue("{what}, {where}")) |> 378 | arrange(desc(date_end), desc(date)) |> 379 | mutate(row_number = row_number()) |> 380 | select(year, what) |> 381 | kable() 382 | 383 | ``` 384 | 385 | 386 | ## Service 387 | ```{r} 388 | cv_entries |> 389 | filter(type == 'service') |> 390 | mutate(where = if_else(!is.na(url), glue("[{where}]({url})"), where)) |> 391 | mutate(what = glue("{what} {where}")) |> 392 | arrange(desc(date_end), desc(date)) |> 393 | select(year, what) |> 394 | # manual_page_break_after_row("what", "DPhil representative in the Computer Science Graduate Society") |> 395 | kable() 396 | ``` 397 | 398 | 399 | 400 | 401 | ## Selected work experience 402 | ```{r} 403 | cv_entries |> 404 | filter(type == 'work', is.na(one_column_paged)) |> 405 | mutate(additional_info = replace_na(additional_info, "")) |> 406 | mutate(what = glue("{what}, {where}
", 407 | "{additional_info}")) |> 408 | select(year, what) |> 409 | #manual_page_break_after_row("what", "RoboTIPS") |> 410 | kable() 411 | ``` 412 | 413 |
414 | 415 | ## Professional development 416 | ```{r} 417 | cv_entries |> 418 | filter(type == 'prof-dev') |> 419 | mutate(what = glue("{what}, {where}")) |> 420 | select(year, what) |> 421 | kable() 422 | ``` 423 | 424 | ## Minor grants and funding 425 | ```{r} 426 | cv_entries |> 427 | filter(type == 'minor_grants') |> 428 | mutate(what = if_else(!is.na(additional_info), 429 | glue("{what} ({additional_info2}), {where}.
", 430 | "{additional_info}"), 431 | glue("{what} ({additional_info2}), {where}"))) |> 432 | blank_year_when_repeated() |> 433 | select(year, what) |> 434 | # manual_page_break_after_row("what", "US research travel") |> 435 | kable() 436 | ``` 437 | 438 | 439 | 440 | ## Personal skills & volunteering {.packages} 441 | ```{r} 442 | cv_entries |> 443 | filter(type == 'volunteering') |> 444 | mutate(what = glue("{what}, {where}")) |> 445 | mutate(what = if_else(!is.na(additional_info), glue("{what}
{additional_info}"), what)) |> 446 | arrange(desc(date), desc(date_end)) |> 447 | select(year, what) |> 448 | manual_page_break_after_row("what", "Head of Entertainment") |> 449 | kable() 450 | ``` 451 | 452 | ::: 453 | 454 | 455 | 456 | # Links 457 | --------------------------------------------------------------------------------