Skip to content

Commit 6c417a0

Browse files
sbfnksbfnk-bot
andcommitted
Add Projects page and update Software/Publications
- Add Projects page with GitHub repos from _data/projects.yml - Update Software page to use r-universe API + extras YAML - Update Publications with truncated author lists (first 3 + last 3) - Consistent card styling across pages (dividers, contributors, metadata) - Team contributors shown as avatars matched via GitHub username - Configurable stale filter (12 months for projects, 24 for software) - Sort by GitHub stars descending Co-Authored-By: sbfnk-bot <sbfnk-bot@users.noreply.github.com>
1 parent 395d558 commit 6c417a0

File tree

9 files changed

+413
-42
lines changed

9 files changed

+413
-42
lines changed

_data/projects.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# List of GitHub repositories to display on the Projects page
2+
# Descriptions are pulled from GitHub repo settings
3+
# Add optional 'url' field to link to a project website instead of GitHub
4+
5+
- repo: epiforecasts/delayscompare
6+
- repo: epiforecasts/llm-epi-composition
7+
- repo: epiforecasts/eval-by-method
8+
- repo: epiforecasts/evalwwforecasts
9+
- repo: EpiAware/ComposableProbabilisticIDModels
10+
- repo: epiforecasts/evalvariantnowcasthub
11+
- repo: epiforecasts/mme-review
12+
- repo: toshiakiasakura/sc_model_comparison
13+
- repo: willgreen93/attenuated-renewal

_data/software-extras.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Additional software packages not in epiforecasts r-universe
2+
# These are merged with the r-universe list on the software page
3+
4+
- repo: epinowcast/epinowcast
5+
- repo: epinowcast/baselinenowcast

_project-item.Rmd

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
:::: {.project-card}
2+
3+
#### [{{name}}]({{url}})
4+
5+
:::: {style="display: grid; grid-template-columns: 1fr 240px; grid-column-gap: 20px;"}
6+
7+
::: {}
8+
9+
{{description}}
10+
11+
```{r, results='asis'}
12+
# Metadata line
13+
meta <- c()
14+
if ("{{language}}" != "" && "{{language}}" != "NA") {
15+
meta <- c(meta, "{{language}}")
16+
}
17+
if ("{{updated}}" != "" && "{{updated}}" != "NA") {
18+
meta <- c(meta, paste0("Updated ", format(as.Date("{{updated}}"), "%b %Y")))
19+
}
20+
if (length(meta) > 0) {
21+
cat('<div class="project-meta">', paste(meta, collapse = " · "), '</div>\n')
22+
}
23+
```
24+
25+
:::
26+
27+
::: {.project-contributors}
28+
29+
{{contributors_section}}
30+
31+
:::
32+
33+
::::
34+
35+
::::
36+

_quarto.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ website:
2121
href: blog.qmd
2222
- text: "Software"
2323
href: software.qmd
24+
- text: "Projects"
25+
href: projects.qmd
2426
- text: "Publications"
2527
href: pubs.qmd
2628
- text: "Join us"

_software-item.Rmd

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
1-
### [{{Package}}]({{github_url}}): {{Title}}.
1+
:::: {.project-card}
22

3+
#### [{{Package}}]({{github_url}})
4+
5+
:::: {style="display: grid; grid-template-columns: 1fr 240px; grid-column-gap: 20px;"}
6+
7+
::: {}
8+
9+
{{description}}
310

411
```{r, results='asis'}
5-
desc <- tempfile()
6-
7-
download.file(
8-
"https://raw.githubusercontent.com/{{owner}}/{{Package}}/HEAD/DESCRIPTION",
9-
desc
10-
)
11-
12-
desc::desc_get(
13-
"Description",
14-
desc
15-
) %>%
16-
stringr::str_squish() %>%
17-
stringr::str_remove_all(" <doi:10\\.\\d{4,5}\\/[\\S]+[^;,.\\s]>") %>%
18-
cat()
12+
# Metadata line
13+
meta <- c()
14+
if ("{{language}}" != "" && "{{language}}" != "NA") {
15+
meta <- c(meta, "{{language}}")
16+
}
17+
if ("{{updated}}" != "" && "{{updated}}" != "NA") {
18+
meta <- c(meta, paste0("Updated ", format(as.Date("{{updated}}"), "%b %Y")))
19+
}
20+
if (length(meta) > 0) {
21+
cat('<div class="project-meta">', paste(meta, collapse = " · "), '</div>\n')
22+
}
1923
```
2024

25+
:::
26+
27+
::: {.project-contributors}
28+
29+
{{contributors_section}}
30+
31+
:::
32+
33+
::::
34+
35+
::::
2136

projects.qmd

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
---
2+
title: "Projects"
3+
description: |
4+
Ongoing analysis projects from the group
5+
---
6+
7+
Ongoing analysis and research projects from the group. For completed work, see our [publications](pubs.qmd). For reusable tools, see our [software](software.qmd).
8+
9+
```{r setup, include=FALSE}
10+
knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning = FALSE)
11+
library(magrittr)
12+
13+
# Hide projects not updated in this many months (set to NULL to disable)
14+
stale_months <- 12
15+
```
16+
17+
```{r load-team}
18+
# Load team members and create lookup by GitHub username
19+
team <- fs::dir_ls("_data/team", regexp = "\\w+\\-\\w+\\.yml") |>
20+
purrr::map(yaml::read_yaml)
21+
22+
team_by_github <- team %>%
23+
purrr::keep(~ !is.null(.x$github) && .x$github != "") %>%
24+
purrr::set_names(purrr::map_chr(., "github"))
25+
```
26+
27+
```{r, results='asis'}
28+
projects <- yaml::read_yaml("_data/projects.yml")
29+
30+
projects %>%
31+
purrr::map(function(p) {
32+
# Fetch repo info
33+
repo_info <- tryCatch(
34+
gh::gh("/repos/{repo}", repo = p$repo),
35+
error = function(e) list(
36+
name = basename(p$repo),
37+
description = "No description available",
38+
html_url = paste0("https://github.com/", p$repo),
39+
language = NA,
40+
stargazers_count = NA,
41+
pushed_at = NA
42+
)
43+
)
44+
45+
# Fetch contributors
46+
contributors <- tryCatch(
47+
gh::gh("/repos/{repo}/contributors", repo = p$repo, per_page = 100),
48+
error = function(e) list()
49+
)
50+
51+
# Match contributors to team members
52+
team_contributors <- contributors %>%
53+
purrr::keep(~ .x$login %in% names(team_by_github)) %>%
54+
purrr::map(function(c) {
55+
tm <- team_by_github[[c$login]]
56+
list(
57+
name = tm$name,
58+
github = c$login,
59+
avatar_url = c$avatar_url,
60+
contributions = c$contributions
61+
)
62+
})
63+
64+
list(
65+
name = repo_info$name,
66+
description = p$description %||% repo_info$description %||% "No description available",
67+
url = p$url %||% repo_info$html_url,
68+
repo = p$repo,
69+
language = repo_info$language,
70+
stars = repo_info$stargazers_count %||% 0,
71+
updated = if (!is.null(repo_info$pushed_at)) substr(repo_info$pushed_at, 1, 10) else NA,
72+
team_contributors = team_contributors
73+
)
74+
}) %>%
75+
# Filter out stale projects
76+
77+
purrr::keep(~ !is.null(.x)) %>%
78+
purrr::keep(~ {
79+
if (is.null(stale_months) || is.na(.x$updated)) return(TRUE)
80+
as.Date(.x$updated) > Sys.Date() - (stale_months * 30)
81+
}) %>%
82+
# Sort by stars descending
83+
{ .[order(purrr::map_dbl(., "stars"), decreasing = TRUE)] } %>%
84+
purrr::map_chr(function(e) {
85+
# Format contributors section as raw markdown/HTML
86+
contributors_section <- ""
87+
if (length(e$team_contributors) > 0) {
88+
contributor_items <- purrr::map_chr(e$team_contributors, function(c) {
89+
sprintf(
90+
'<a href="https://github.com/%s" title="%s"><img src="%s" alt="%s" width="32" height="32"></a>',
91+
c$github, c$name, c$avatar_url, c$name
92+
)
93+
})
94+
contributors_section <- paste(contributor_items, collapse = " ")
95+
}
96+
97+
knitr::knit_expand(
98+
"_project-item.Rmd",
99+
name = e$name,
100+
description = stringr::str_squish(e$description),
101+
url = e$url,
102+
repo = e$repo,
103+
language = e$language %||% "",
104+
stars = e$stars %||% "",
105+
updated = e$updated %||% "",
106+
contributors_section = contributors_section
107+
)
108+
}) %>%
109+
{ knitr::knit_child(text = unlist(.), quiet = TRUE) } %>%
110+
cat(sep = "\n")
111+
```

pubs.qmd

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,67 @@ title: "Publications"
44

55
```{r setup, include=FALSE}
66
knitr::opts_chunk$set(echo = FALSE)
7+
library(magrittr)
78
```
89

9-
Below is a list of selected publications (sorted by year) illustrating the type of work conducted by our team.
10+
Below is a list of selected publications (sorted by year) illustrating the type of work conducted by our team. See also our [ongoing projects](projects.qmd) and [software](software.qmd).
1011

1112
```{r}
1213
bibfile <- "_data/papers.bib"
1314
```
1415

16+
::: {.publications-list}
17+
1518
```{r, results='asis'}
16-
library(magrittr)
17-
b <- tools::bibstyle(
18-
"by_year",
19-
fmtPrefix = function(paper) "-",
20-
.init = TRUE,
21-
sortKeys = function(refs) rank(purrr::map_chr(unclass(refs), "year"))
22-
)
23-
24-
bibtex::read.bib(bibfile) %>%
25-
purrr::keep(~ attr(unclass(.x), "bibtype") == "Article") %>%
26-
sort(.bibstyle = "by_year", decreasing = TRUE) %>%
27-
format("html", .bibstyle = "by_year") %>%
28-
paste(collapse = "") %>%
29-
cat()
19+
# Custom function to truncate author list (keep first 3 + last 3)
20+
truncate_authors <- function(authors) {
21+
if (is.null(authors) || length(authors) == 0) return("Unknown")
22+
# Handle person objects from bibtex
23+
if (inherits(authors, "person")) {
24+
author_list <- format(authors, include = c("given", "family"))
25+
} else {
26+
authors <- as.character(authors)
27+
author_list <- trimws(strsplit(authors, " and ")[[1]])
28+
}
29+
n <- length(author_list)
30+
if (n > 7) {
31+
# Show first 3, ..., last 3
32+
first_three <- paste(author_list[1:3], collapse = ", ")
33+
last_three <- paste(author_list[(n-2):n], collapse = ", ")
34+
paste0(first_three, ", ..., ", last_three)
35+
} else {
36+
paste(author_list, collapse = ", ")
37+
}
38+
}
39+
40+
bib <- bibtex::read.bib(bibfile) %>%
41+
purrr::keep(~ attr(.x, "bibtype") == "Article")
42+
43+
# Sort by year descending
44+
years <- purrr::map_chr(bib, ~ as.character(.x$year %||% "0"))
45+
bib <- bib[order(years, decreasing = TRUE)]
46+
47+
# Render each publication
48+
purrr::walk(bib, function(entry) {
49+
authors <- truncate_authors(entry$author)
50+
title <- as.character(entry$title %||% "Untitled")
51+
title <- gsub("[{}]", "", title)
52+
journal <- as.character(entry$journal %||% "")
53+
journal <- gsub("[{}]", "", journal)
54+
year <- as.character(entry$year %||% "")
55+
doi <- as.character(entry$doi %||% "")
56+
57+
if (doi != "" && !is.na(doi)) {
58+
title_html <- sprintf('<a href="https://doi.org/%s">%s</a>', doi, title)
59+
} else {
60+
title_html <- title
61+
}
62+
63+
cat(sprintf(
64+
'<div class="pub-item"><strong>%s</strong> (%s). %s. <em>%s</em>.</div>\n',
65+
authors, year, title_html, journal
66+
))
67+
})
3068
```
69+
70+
:::

0 commit comments

Comments
 (0)