---
page-title: "<YourGithubUser> - GitHub Wrapped 2023"
format:
dashboard:
scrolling: true
theme: darkly
execute:
echo: false
params:
userid: "<YourGithubUser>"
highlight: "#DBAC34"
anti_color: "#DB5934"
bg_color: "#2D2D2D"
ignore_lang: "CSS,SCSS,HTML,TeX,Less,Makefile" #the languages you want to ignore
---Create a personal GitHub wrapped using Quarto Dashboards
The time has come to create a personal GitHub wrapped using Quarto Dashboards. This is a great way to showcase your work and projects.
Setup
- Create a Github token (needs read:user access)
- add it to
.Renvironas GITHUB_TOKEN, you can do that by runningusethis::edit_r_environ() - Install a version of Quarto that allows to create dashboards (>1.4.0)
- In
index.qmd, change theuseridparameter to your GitHub username. quarto render
If you are missing a logo of a programming language, add a svg version to img/logos
Then, we need to create a new Quarto project. We can do this by running the following command in the terminal:
Load Packages
library("ghql")
library("jsonlite")
library("tidyverse")
library("patchwork")
library("reactable")
ignore_lang <- str_split(params$ignore_lang, ",")[[1]]Function to create a donut chart
# source: https://rfortherestofus.com/2022/09/how-to-make-a-donut-chart-in-ggplot
donut_plot <- function(value, max_value, highlight_color, text_color) {
# Wrangle data to get a data frame in the format we need it in to make our donut chart
df <- tibble(x = 1, y = value) |>
mutate(y_negative = max_value - y) |>
pivot_longer(cols = -x)
# Create a nicely formatted big number to go in the donut hole
big_number_text_label <- value
# Create our plot
ggplot(
df,
aes(
x = x,
y = value,
fill = name
)
) +
geom_col(show.legend = FALSE) +
coord_polar(
theta = "y",
direction = -1
) +
xlim(c(-2, 2)) +
scale_fill_manual(values = c(highlight_color, "grey66")) +
theme_void() +
annotate("text",
label = big_number_text_label,
fontface = "bold",
color = text_color,
size = 12,
x = -2,
y = 0
) +
theme(plot.title = element_text(color = text_color))
}Setup Data
token <- Sys.getenv("GITHUB_TOKEN") # !!!!!
con <- GraphqlClient$new(
url = "https://api.github.com/graphql",
headers = list(Authorization = paste0("Bearer ", token))
)
variables <- list(
userid = params$userid
)
# query for the user contributions
con$load_schema()
qry <- Query$new()
qry$query(
"mydata", ' query getContrib($userid: String!){
user(login:$userid){
avatarUrl
login
contributionsCollection(
from: "2023-01-01T00:00:00.000Z"
to: "2024-01-01T00:00:00.000Z") {
totalCommitContributions
totalIssueContributions
totalRepositoryContributions
totalRepositoriesWithContributedCommits
totalPullRequestContributions
totalPullRequestReviewContributions
contributionCalendar {
totalContributions
weeks {
contributionDays {
contributionCount
date
}
}
}
commitContributionsByRepository {
contributions {
totalCount
}
repository {
name
owner {
login
}
isPrivate
languages(first: 5, orderBy: {field: SIZE, direction: DESC}) {
edges {
size
node {
color
name
id
}
}
}
}
}
}
}
}'
)
x <- con$exec(qry$queries$mydata, variables)
res <- jsonlite::fromJSON(x)
#hihih a pun
stargaze <- gh::gh("/users/{username}/starred", username = params$userid, .accept = "application/vnd.github.v3.star+json", per_page = 100)Prepare the Data
contrib <- res$data$user$contributionsCollection$contributionCalendar$weeks
tbl_contributions <- map_dfr(seq_len(nrow(contrib)), \(x) contrib[x, ][[1]])
contrib_by_repo <- flatten_dfc(res$data$user$contributionsCollection$commitContributionsByRepository)
most_lang <- count(bind_rows(contrib_by_repo$edges)$node, name) |>
filter(!name %in% ignore_lang) |>
top_n(3, n) |>
arrange(-n) |>
mutate(n = n / sum(n))
most_lang_img <- paste0("img/logos/", most_lang$name, ".svg")
tbl_contributions$wday <- lubridate::wday(tbl_contributions$date, label = TRUE, abbr = TRUE)
tbl_contributions$week <- lubridate::week(tbl_contributions$date)
fill_breaks <- pretty(tbl_contributions$contributionCount)
fill_breaks[1] <- ifelse(fill_breaks[1] == 0, 1, fill_breaks[1])
fill_breaks[length(fill_breaks)] <- max(tbl_contributions$contributionCount)
end <- min(Sys.Date(), as.Date("2023-12-31"))
streak <- (tbl_contributions$contributionCount[tbl_contributions$date <= end] != 0) + 0
y <- rle(streak)
streak_len <- max(y$lengths[y$values == 1])
gap_len <- max(y$lengths[y$values == 0])
wkend <- sum(
tbl_contributions$contributionCount[tbl_contributions$wday %in% c("Sun", "Sat")]
) / sum(tbl_contributions$contributionCount)
stars <- map_dfr(stargaze, \(x) tibble(
starred_at = as.POSIXct(x$starred_at),
repo = x$repo$full_name,
descr = x$repo$description
)) |>
filter(lubridate::year(starred_at) == 2023) |>
nrow()
stars <- ifelse(stars == 100, "100+", stars)Render the Dashboard
<h1 style="text-align:center;margin-bottom:0.15em"> 🚀🚀🚀 GitHub Wrapped 2023 🚀🚀🚀 </h1>
<span style="text-align:center">
{width=10em}
</span>
<h1 style="text-align:center;margin-top:0.15em"> `r params$userid` </h1>
<center>
{width=3em}
{width=3em}
{width=3em}
</center>
Row
list(
icon = "stars",
color = params$highlight,
value = res$data$user$contributionsCollection$totalCommitContributions
)list(
icon = "journal-arrow-up",
color = "#9BA7C0",
value = res$data$user$contributionsCollection$totalRepositoryContributions
)list(
icon = "bullseye",
color = "#9BA7C0",
value = res$data$user$contributionsCollection$totalIssueContributions
)list(
icon = "sign-merge-right",
color = "#9BA7C0",
value = res$data$user$contributionsCollection$totalPullRequestContributions
)list(
icon = "star-fill",
color = "#9BA7C0",
value = stars
)Row
p1 <- tbl_contributions |>
mutate(contributionCount = ifelse(contributionCount == 0, NA, contributionCount)) |>
ggplot(aes(x = week, y = fct_rev(wday))) +
geom_tile(aes(fill = contributionCount), size = 1, color = "white") +
# coord_fixed() +
scale_fill_gradient(breaks = fill_breaks, low = "#9BE9A8", high = "#216E39", name = "", na.value = "grey66") +
scale_x_continuous(breaks = NULL, name = "", limits = c(0, 53), expand = c(0, 0)) +
scale_y_discrete(labels = c("", "Fri", "", "Wed", "", "Mon", "")) +
theme_minimal() +
theme(
legend.position = "bottom",
legend.justification = "right",
legend.text = element_text(color = "white"),
axis.text = element_text(color = "white"),
axis.title.y = element_blank(),
panel.grid.major = element_blank(),
axis.ticks.y = element_blank()
) +
guides(fill = guide_legend(
label.position = "bottom",
direction = "horizontal"
))
p2 <- donut_plot(streak_len, streak_len, params$highlight, "white") + labs(title = "longest streak")
p3 <- donut_plot(gap_len, gap_len, params$anti_color, "white") + labs(title = "longest gap")
p4 <- donut_plot(round(100 * wkend), 100, "#216E39", "white") + labs(title = "weekend contribs (%)")
p5 <- donut_plot(sum(streak), 365, "#216E39", "white") + labs(title = "active days")
p <- (p2 | p3 | p5 | p4) / p1 +
plot_annotation(theme = theme(
plot.background = element_rect(fill = params$bg_color, color = params$bg_color),
panel.background = element_rect(fill = params$bg_color)
))
pcontrib_by_repo |>
select(-edges) |>
filter(!isPrivate) |>
select(repo = name, login, contributions = totalCount) |>
reactable(theme = reactableTheme(backgroundColor = params$bg_color))Row
tbl_contributions |>
group_by(wday) |>
summarise(count = sum(contributionCount)) |>
mutate(type = case_when(
count == min(count) ~ "min",
count == max(count) ~ "max",
TRUE ~ "regular"
)) |>
ggplot(aes(x = wday, y = count)) +
geom_col(aes(fill = type), show.legend = FALSE) +
geom_text(aes(y = count / 2, label = count), color = params$bg_color) +
scale_fill_manual(values = c("min" = params$anti_color, "max" = params$highlight, "regular" = "white")) +
scale_x_discrete(labels = c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")) +
theme_minimal() +
theme(
plot.background = element_rect(fill = params$bg_color, color = "white"),
panel.background = element_rect(fill = params$bg_color),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_text(color = "white"),
axis.title = element_blank()
)tbl_contributions |>
mutate(month = lubridate::month(date, label = TRUE)) |>
group_by(month) |>
summarise(count = sum(contributionCount)) |>
mutate(type = case_when(
count == min(count) ~ "min",
count == max(count) ~ "max",
TRUE ~ "regular"
)) |>
ggplot(aes(x = month, y = count)) +
geom_col(aes(fill = type), show.legend = FALSE) +
geom_text(aes(y = count / 2, label = count), color = params$bg_color) +
scale_fill_manual(values = c("min" = params$anti_color, "max" = params$highlight, "regular" = "white")) +
theme_minimal() +
theme(
plot.background = element_rect(fill = params$bg_color, color = "white"),
panel.background = element_rect(fill = params$bg_color),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_text(color = "white"),
axis.title = element_blank()
)What it looks like
And here you go! You have created a personal GitHub wrapped using Quarto Dashboards. This is a great way to showcase your work and projects. You can also add more sections to the dashboard, such as a section for your most used programming languages, a section for your most starred repositories, and a section for your most active days. You can also customize the dashboard to fit your personal style and preferences. Have fun creating your own GitHub wrapped!
All the code is available in this GitHub repository