├── .gitignore
├── NEWS.md
├── README.md
├── githubdashboard.Rproj
├── README.Rmd
├── issue_detail.Rmd
└── github-dashboard.Rmd
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .RData
4 | .Ruserdata
5 | github-dashboard_cache
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | ### 2017-02-18
2 |
3 | * Added issue title to overview list view
4 | * Added recent issue (<48 hrs old) detail page
5 | * Updated `index.html` example
6 |
7 | ### 2017-02-16
8 |
9 | * Implemented crazy idea
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | GitHub Dashboard in R
2 | ================
3 |
4 | Example
5 | -------
6 |
7 | -
8 |
9 | R flexdashboard expectations
10 | ----------------------------
11 |
12 | - expects `GITHUB_USER` in the environment
13 | - expects `GITHUB_PAT` in the environment
14 |
--------------------------------------------------------------------------------
/githubdashboard.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 |
--------------------------------------------------------------------------------
/README.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "GitHub Dashboard in R"
3 | output: github_document
4 | ---
5 |
6 | ```{r setup, include=FALSE}
7 | knitr::opts_chunk$set(echo = TRUE)
8 | ```
9 |
10 | ## Example
11 |
12 | -
13 |
14 | ## R flexdashboard expectations
15 |
16 | - expects `GITHUB_USER` in the environment
17 | - expects `GITHUB_PAT` in the environment
18 |
--------------------------------------------------------------------------------
/issue_detail.Rmd:
--------------------------------------------------------------------------------
1 | ```{r include=FALSE}
2 | # TODO Make this smarter
3 | body <- recent_issues$body[i]
4 | body <- gsub("![", "[=> Image link ... ", body, fixed=TRUE)
5 | ```
6 |
7 | Row
8 | -----------------------------------------------------------------------
9 |
10 | ### `r sprintf("%s > %s (#%s)", recent_issues$repo_name[i], recent_issues$Title[i], recent_issues$number[i])`
11 |
12 | `r body`
13 |
14 | > `r sprintf('Issue Link', recent_issues$html_url[i], recent_issues$number[i])`
15 |
16 |
--------------------------------------------------------------------------------
/github-dashboard.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "`r sprintf('GitHub Dashboard for [%s] on [%s]', Sys.getenv('GITHUB_USER'), Sys.Date())`"
3 | output:
4 | flexdashboard::flex_dashboard:
5 | theme: yeti
6 | orientation: rows
7 | vertical_layout: scroll
8 | ---
9 | ```{r setup, include=FALSE}
10 | library(flexdashboard)
11 | library(gh) # devtools::install_github("r-pkgs/gh")
12 | library(anytime)
13 | library(tidyverse)
14 | library(DT)
15 | library(knitr)
16 | ```
17 |
18 | ```{r dev, echo=FALSE, include=FALSE}
19 | USER <- Sys.getenv('GITHUB_USER')
20 |
21 | user <- gh("/users/:user", user=USER)
22 | repos <- gh("/users/:user/repos", user=USER, .limit = Inf)
23 | issues <- gh("/user/issues", .limit = Inf)
24 | ```
25 |
26 | ```{r dev2, echo=FALSE, include=FALSE}
27 | map_df(repos, ~.[c("name", "html_url", "stargazers_count", "forks_count", "has_issues",
28 | "open_issues", "updated_at", "pushed_at")]) %>%
29 | mutate(updated_at = anytime(pushed_at, asUTC=TRUE), pushed_at = NULL) -> repos_df
30 |
31 | map_df(issues, function(x) {
32 | c(list(repo_name = x$repository$full_name,
33 | repo_url = x$repository$html_url,
34 | user_login = x$user$login,
35 | user_url = x$user$url),
36 | x[c("html_url", "number", "state", "updated_at", "created_at", "title", "body")])
37 | }) %>%
38 | mutate(updated_at = anytime(created_at, asUTC=TRUE), created_at = NULL) -> issues_df
39 | ```
40 |
41 | ```{r include=FALSE}
42 | options(
43 | DT.options =
44 | list(
45 | pageLength = 25,
46 | language = list(search = 'Filter:'),
47 | dom = 'Bfrtip',
48 | bInfo = FALSE)
49 | )
50 |
51 | pretty_diff <- function(rel) {
52 | map_chr(rel, function(x) {
53 | x <- Sys.time() - as.POSIXct(x, tz=Sys.timezone())
54 | y <- unclass(x)
55 | attr(y, "units") <- NULL
56 | # sprintf("%3.2f %s", abs(y), attr(x, "units"))
57 | paste(format(abs(y), digits = 0, nsmall = 2), attr(x, "units"))
58 | })
59 | }
60 |
61 | repos_df %>%
62 | mutate(Repository = sprintf('%s', html_url, name)) %>%
63 | rename(Stars = stargazers_count, Forks = forks_count,
64 | `Issues` = open_issues) %>%
65 | select(Repository, everything(), -name, -html_url, -has_issues) -> repos_df
66 |
67 | issues_df %>%
68 | rename(Title=title) %>%
69 | mutate(Repository = sprintf('%s', repo_url, repo_name),
70 | `Submitted by` = sprintf('%s', user_url, user_login),
71 | `Issue #` = sprintf('#%s', html_url, number),
72 | Age = pretty_diff(updated_at)) -> issues_df
73 | ```
74 |
75 | Overview
76 | =====================================
77 |
78 | Row
79 | -----------------------------------------------------------------------
80 |
81 | ### Total Repos
82 |
83 | ```{r}
84 | valueBox(scales::comma(nrow(repos_df)),
85 | icon = "fa-github")
86 | ```
87 |
88 | ### Open Issues
89 |
90 | ```{r}
91 | valueBox(scales::comma(nrow(filter(issues_df, state == "open"))),
92 | icon = "fa-exclamation-triangle")
93 | ```
94 |
95 | ### Total Stars
96 |
97 | ```{r}
98 | valueBox(scales::comma(sum(repos_df$Stars)),
99 | icon = "fa-star")
100 | ```
101 |
102 |
103 | Row
104 | -----------------------------------------------------------------------
105 |
106 | ### Top 10 Repos (sorted initially by stargazers)
107 |
108 | ```{r}
109 | arrange(repos_df, desc(Stars)) %>%
110 | head(10) %>%
111 | select(-updated_at) %>%
112 | datatable(options = list(bFilter=FALSE, paging=FALSE), escape=FALSE, filter="none")
113 | ```
114 |
115 | Row
116 | -----------------------------------------------------------------------
117 |
118 | ### Repos by time of last activity
119 |
120 | ```{r}
121 | arrange(repos_df, desc(updated_at)) %>%
122 | select(Repository, Age=updated_at, Stars, Forks, Issues) %>%
123 | mutate(Age=pretty_diff(Age)) %>%
124 | datatable(escape=FALSE,
125 | options = list(columnDefs = list(list(className = 'dt-right', targets = 2:5))))
126 | ```
127 |
128 | Row
129 | -----------------------------------------------------------------------
130 |
131 | ### Open issues by date
132 |
133 | ```{r}
134 | filter(issues_df, state == "open") %>%
135 | arrange(desc(updated_at)) %>%
136 | select(Repository, `Submitted by`, Title, `Issue #`, Age) %>%
137 | datatable(escape=FALSE,
138 | options = list(columnDefs = list(list(className = 'dt-right', targets = c(4:5)))))
139 | ```
140 |
141 | > `r sprintf("%s total open issues", scales::comma(nrow(filter(issues_df, state == "open"))))`
142 |
143 | Recent Issue Detail
144 | =====================================
145 |
146 | ```{r issue_detail, include=FALSE}
147 | options(knitr.duplicate.label = 'allow')
148 |
149 | issues_df %>%
150 | mutate(hr_diff = as.numeric(difftime(Sys.time(),
151 | issues_df$updated_at,
152 | units = "hours"))) %>%
153 | filter(hr_diff <= 48) %>%
154 | arrange(hr_diff) -> recent_issues
155 |
156 | if (nrow(recent_issues) == 0) {
157 | out <- "### No new issues in the past 48 hours"
158 | } else {
159 | out <- NULL
160 | cat("", file = "/tmp/k.txt")
161 | for (i in 1:nrow(recent_issues)) {
162 | res <- knit_child('issue_detail.Rmd', envir=parent.frame())
163 | cat(res, file="/tmp/k.txt", append = TRUE)
164 | out <- c(out, res)
165 | }
166 |
167 | }
168 |
169 | options(knitr.duplicate.label = 'no way')
170 | ```
171 |
172 | `r paste(out, sep=" ", collapse="\n")`
173 |
174 |
--------------------------------------------------------------------------------