---
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
.Renviron
as 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 theuserid
parameter 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")
<- str_split(params$ignore_lang, ",")[[1]] ignore_lang
Function to create a donut chart
# source: https://rfortherestofus.com/2022/09/how-to-make-a-donut-chart-in-ggplot
<- function(value, max_value, highlight_color, text_color) {
donut_plot # Wrangle data to get a data frame in the format we need it in to make our donut chart
<- tibble(x = 1, y = value) |>
df mutate(y_negative = max_value - y) |>
pivot_longer(cols = -x)
# Create a nicely formatted big number to go in the donut hole
<- value
big_number_text_label
# 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
<- Sys.getenv("GITHUB_TOKEN") # !!!!!
token
<- GraphqlClient$new(
con url = "https://api.github.com/graphql",
headers = list(Authorization = paste0("Bearer ", token))
)
<- list(
variables userid = params$userid
)
# query for the user contributions
$load_schema()
con<- Query$new()
qry $query(
qry"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
}
}
}
}
}
}
}
}'
)<- con$exec(qry$queries$mydata, variables)
x <- jsonlite::fromJSON(x)
res
#hihih a pun
<- gh::gh("/users/{username}/starred", username = params$userid, .accept = "application/vnd.github.v3.star+json", per_page = 100) stargaze
Prepare the Data
<- res$data$user$contributionsCollection$contributionCalendar$weeks
contrib <- map_dfr(seq_len(nrow(contrib)), \(x) contrib[x, ][[1]])
tbl_contributions
<- flatten_dfc(res$data$user$contributionsCollection$commitContributionsByRepository)
contrib_by_repo
<- count(bind_rows(contrib_by_repo$edges)$node, name) |>
most_lang filter(!name %in% ignore_lang) |>
top_n(3, n) |>
arrange(-n) |>
mutate(n = n / sum(n))
<- paste0("img/logos/", most_lang$name, ".svg")
most_lang_img
$wday <- lubridate::wday(tbl_contributions$date, label = TRUE, abbr = TRUE)
tbl_contributions$week <- lubridate::week(tbl_contributions$date)
tbl_contributions
<- 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)
fill_breaks[<- min(Sys.Date(), as.Date("2023-12-31"))
end <- (tbl_contributions$contributionCount[tbl_contributions$date <= end] != 0) + 0
streak <- rle(streak)
y <- max(y$lengths[y$values == 1])
streak_len <- max(y$lengths[y$values == 0])
gap_len <- sum(
wkend $contributionCount[tbl_contributions$wday %in% c("Sun", "Sat")]
tbl_contributions/ sum(tbl_contributions$contributionCount)
)
<- map_dfr(stargaze, \(x) tibble(
stars starred_at = as.POSIXct(x$starred_at),
repo = x$repo$full_name,
descr = x$repo$description
|>
)) filter(lubridate::year(starred_at) == 2023) |>
nrow()
<- ifelse(stars == 100, "100+", stars) stars
Render the Dashboard
<h1 style="text-align:center;margin-bottom:0.15em"> 🚀🚀🚀 GitHub Wrapped 2023 🚀🚀🚀 </h1>
<span style="text-align:center">
![](`r res$data$user$avatarUrl`){width=10em}
</span>
<h1 style="text-align:center;margin-top:0.15em"> `r params$userid` </h1>
<center>
![](`r most_lang_img[1]`){width=3em}
![](`r most_lang_img[2]`){width=3em}
![](`r most_lang_img[3]`){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
<- tbl_contributions |>
p1 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"
))<- donut_plot(streak_len, streak_len, params$highlight, "white") + labs(title = "longest streak")
p2 <- donut_plot(gap_len, gap_len, params$anti_color, "white") + labs(title = "longest gap")
p3 <- donut_plot(round(100 * wkend), 100, "#216E39", "white") + labs(title = "weekend contribs (%)")
p4 <- donut_plot(sum(streak), 365, "#216E39", "white") + labs(title = "active days")
p5 <- (p2 | p3 | p5 | p4) / p1 +
p plot_annotation(theme = theme(
plot.background = element_rect(fill = params$bg_color, color = params$bg_color),
panel.background = element_rect(fill = params$bg_color)
)) p
|>
contrib_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(
== min(count) ~ "min",
count == max(count) ~ "max",
count 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(
== min(count) ~ "min",
count == max(count) ~ "max",
count 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