Skip to content

Commit 4ebdf20

Browse files
authored
Merge pull request #29 from ropensci-review-tools/prs
add 'cm-data-gh-prs.R' for #23
2 parents dada43c + fc1069a commit 4ebdf20

File tree

4 files changed

+260
-8
lines changed

4 files changed

+260
-8
lines changed

DESCRIPTION

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: repometrics
22
Title: Metrics for Your Code Repository
3-
Version: 0.1.1.054
3+
Version: 0.1.1.059
44
Authors@R:
55
person("Mark", "Padgham", , "mark.padgham@email.com", role = c("aut", "cre"),
66
comment = c(ORCID = "0000-0003-2172-5265"))
@@ -16,6 +16,7 @@ Imports:
1616
dplyr,
1717
fs,
1818
gert,
19+
gh,
1920
git2r,
2021
httr2,
2122
memoise,

R/cm-data-gh-prs.R

+195
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#' The GitHub GraphQL query to extract information from all pull requests.
2+
#'
3+
#' The Rest API pull request just dumps meta-information about each PR. Getting
4+
#' details on individual PRs then requires looping over each PR number and
5+
#' making a separate call. This GraphQL query does everything in a single call.
6+
#'
7+
#' @param org The GitHub organization.
8+
#' @param repo The GitHub repository.
9+
#' @param end_cursor The end cursor from the previous query.
10+
#'
11+
#' @return The GraphQL query to pass to a `gh::gh_gql()` call.
12+
#' @noRd
13+
gh_prs_qry <- function (org = "ropensci-review-tools",
14+
repo = "repometrics",
15+
n_per_page = 100L,
16+
end_cursor = NULL) {
17+
18+
checkmate::assert_integerish (n_per_page)
19+
20+
after_txt <- ""
21+
if (!is.null (end_cursor)) {
22+
after_txt <- paste0 (", after:\"", end_cursor, "\"")
23+
}
24+
25+
q <- paste0 ("{
26+
repository(owner:\"", org, "\", name:\"", repo, "\") {
27+
pullRequests (first: ", n_per_page, after_txt, ") {
28+
pageInfo {
29+
hasNextPage
30+
endCursor
31+
}
32+
nodes {
33+
number
34+
author {
35+
login
36+
}
37+
state
38+
closed
39+
title
40+
reviewDecision
41+
reviews (first: 100) {
42+
nodes {
43+
author {
44+
login
45+
}
46+
submittedAt
47+
state
48+
body
49+
}
50+
}
51+
merged
52+
mergedBy {
53+
login
54+
}
55+
mergeCommit {
56+
oid
57+
}
58+
assignees (first: 100) {
59+
nodes {
60+
name
61+
}
62+
}
63+
createdAt
64+
closedAt
65+
updatedAt
66+
closingIssuesReferences (first: 100) {
67+
nodes {
68+
number
69+
}
70+
}
71+
commits (first: 100) {
72+
nodes {
73+
commit {
74+
oid
75+
}
76+
}
77+
}
78+
additions
79+
changedFiles
80+
deletions
81+
participants (first: 100) {
82+
nodes {
83+
login
84+
}
85+
}
86+
labels (first: 100) {
87+
nodes {
88+
name,
89+
}
90+
}
91+
body
92+
totalCommentsCount
93+
comments (last: 100) {
94+
nodes {
95+
createdAt,
96+
author {
97+
login
98+
},
99+
body
100+
}
101+
}
102+
}
103+
}
104+
}
105+
}")
106+
107+
return (q)
108+
}
109+
110+
prs_from_gh_api <- function (path, n_per_page = 30L) {
111+
112+
is_test_env <- Sys.getenv ("REPOMETRICS_TESTS") == "true"
113+
if (is_test_env) {
114+
n_per_page <- 2L
115+
}
116+
117+
or <- org_repo_from_path (path)
118+
end_cursor <- pr_data <- NULL
119+
has_next_page <- TRUE
120+
121+
while (has_next_page) {
122+
123+
q <- gh_prs_qry (org = or [1], repo = or [2], end_cursor = end_cursor, n_per_page = n_per_page)
124+
dat <- gh::gh_gql (query = q)
125+
126+
pr_data <- c (pr_data, dat$data$repository$pullRequests$nodes)
127+
128+
has_next_page <- dat$data$repository$pullRequests$pageInfo$hasNextPage
129+
end_cursor <- dat$data$repository$pullRequests$pageInfo$endCursor
130+
if (is_test_env) {
131+
has_next_page <- FALSE
132+
}
133+
}
134+
135+
commit_oids <- vapply (pr_data, function (i) {
136+
oids <- vapply (i$commits$nodes, function (j) {
137+
j$commit$oid
138+
}, character (1L))
139+
paste0 (oids, collapse = ",")
140+
}, character (1L))
141+
participants <- vapply (pr_data, function (i) {
142+
p <- vapply (i$participants$nodes, function (j) j$login, character (1L))
143+
paste0 (p, collapse = ",")
144+
}, character (1L))
145+
comments <- lapply (pr_data, function (i) {
146+
created_at <- vapply (i$comments$nodes, function (j) j$createdAt, character (1L))
147+
author <- vapply (i$comments$nodes, function (j) j$author$login, character (1L))
148+
body <- vapply (i$comments$nodes, function (j) j$body, character (1L))
149+
data.frame (
150+
author = author,
151+
created_at = created_at,
152+
body = body
153+
)
154+
})
155+
closing_issue_refs <- lapply (pr_data, function (i) {
156+
vapply (i$closingIssuesReferences$nodes, function (j) j$number, integer (1L))
157+
})
158+
reviews <- lapply (pr_data, function (i) {
159+
login <- vapply (i$reviews$nodes, function (j) j$author$login, character (1L))
160+
state <- vapply (i$reviews$nodes, function (j) j$state, character (1L))
161+
submitted_at <- vapply (i$reviews$nodes, function (j) j$submittedAt, character (1L))
162+
body <- vapply (i$reviews$nodes, function (j) j$body, character (1L))
163+
data.frame (
164+
login = login,
165+
state = state,
166+
submitted_at = submitted_at,
167+
body = body
168+
)
169+
})
170+
171+
data.frame (
172+
number = vapply (pr_data, function (i) i$number, integer (1L)),
173+
user_login = vapply (pr_data, function (i) i$author$login, character (1L)),
174+
state = vapply (pr_data, function (i) i$state, character (1L)),
175+
merged = vapply (pr_data, function (i) i$merged, logical (1L)),
176+
merged_by = vapply (pr_data, function (i) null2na_char (i$mergedBy$login), character (1L)),
177+
merge_commit = vapply (pr_data, function (i) null2na_char (i$mergeCommit$oid), character (1L)),
178+
closed = vapply (pr_data, function (i) i$closed, logical (1L)),
179+
title = vapply (pr_data, function (i) i$title, character (1L)),
180+
review_decision = vapply (pr_data, function (i) null2na_char (i$reviewDecision), character (1L)),
181+
created_at = vapply (pr_data, function (i) i$createdAt, character (1L)),
182+
closed_at = vapply (pr_data, function (i) null2na_char (i$closedAt), character (1L)),
183+
updated_at = vapply (pr_data, function (i) i$updatedAt, character (1L)),
184+
additions = vapply (pr_data, function (i) i$additions, integer (1L)),
185+
deletions = vapply (pr_data, function (i) i$deletions, integer (1L)),
186+
changed_files = vapply (pr_data, function (i) i$changedFiles, integer (1L)),
187+
commit_oids = commit_oids,
188+
closing_issue_refs = I (closing_issue_refs),
189+
total_comments = vapply (pr_data, function (i) i$totalCommentsCount, integer (1L)),
190+
participants = participants,
191+
body = vapply (pr_data, function (i) i$body, character (1L)),
192+
comments = I (comments),
193+
reviews = I (reviews)
194+
)
195+
}

codemeta.json

+18-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"codeRepository": "https://github.com/ropensci-review-tools/repometrics",
99
"issueTracker": "https://github.com/ropensci-review-tools/repometrics/issues",
1010
"license": "https://spdx.org/licenses/GPL-3.0",
11-
"version": "0.1.1.054",
11+
"version": "0.1.1.059",
1212
"programmingLanguage": {
1313
"@type": "ComputerLanguage",
1414
"name": "R",
@@ -182,6 +182,18 @@
182182
"sameAs": "https://CRAN.R-project.org/package=gert"
183183
},
184184
"6": {
185+
"@type": "SoftwareApplication",
186+
"identifier": "gh",
187+
"name": "gh",
188+
"provider": {
189+
"@id": "https://cran.r-project.org",
190+
"@type": "Organization",
191+
"name": "Comprehensive R Archive Network (CRAN)",
192+
"url": "https://cran.r-project.org"
193+
},
194+
"sameAs": "https://CRAN.R-project.org/package=gh"
195+
},
196+
"7": {
185197
"@type": "SoftwareApplication",
186198
"identifier": "git2r",
187199
"name": "git2r",
@@ -193,7 +205,7 @@
193205
},
194206
"sameAs": "https://CRAN.R-project.org/package=git2r"
195207
},
196-
"7": {
208+
"8": {
197209
"@type": "SoftwareApplication",
198210
"identifier": "httr2",
199211
"name": "httr2",
@@ -205,7 +217,7 @@
205217
},
206218
"sameAs": "https://CRAN.R-project.org/package=httr2"
207219
},
208-
"8": {
220+
"9": {
209221
"@type": "SoftwareApplication",
210222
"identifier": "memoise",
211223
"name": "memoise",
@@ -217,7 +229,7 @@
217229
},
218230
"sameAs": "https://CRAN.R-project.org/package=memoise"
219231
},
220-
"9": {
232+
"10": {
221233
"@type": "SoftwareApplication",
222234
"identifier": "pbapply",
223235
"name": "pbapply",
@@ -229,15 +241,15 @@
229241
},
230242
"sameAs": "https://CRAN.R-project.org/package=pbapply"
231243
},
232-
"10": {
244+
"11": {
233245
"@type": "SoftwareApplication",
234246
"identifier": "pkgstats",
235247
"name": "pkgstats",
236248
"sameAs": "https://github.com/ropensci-review-tools/pkgstats"
237249
},
238250
"SystemRequirements": {}
239251
},
240-
"fileSize": "142.573KB",
252+
"fileSize": "179.388KB",
241253
"readme": "https://github.com/ropensci-review-tools/repometrics/blob/main/README.md",
242254
"contIntegration": [
243255
"https://github.com/ropensci-review-tools/repometrics/actions?query=workflow%3AR-CMD-check",

tests/testthat/test-cm-data-github.R

+45-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ test_that ("cm data gh issues", {
9999
}
100100
})
101101

102-
test_that ("cm data gh issue commentss", {
102+
test_that ("cm data gh issue comments", {
103103

104104
Sys.setenv ("REPOMETRICS_TESTS" = "true")
105105

@@ -131,3 +131,47 @@ test_that ("cm data gh issue commentss", {
131131
expect_type (cmts [[n]], type)
132132
}
133133
})
134+
135+
test_that ("cm data gh prs", {
136+
137+
Sys.setenv ("REPOMETRICS_TESTS" = "true")
138+
139+
path <- generate_test_pkg ()
140+
prs <- with_mock_dir ("gh_api_prs", {
141+
prs_from_gh_api (path, n_per_page = 2L)
142+
})
143+
144+
fs::dir_delete (path)
145+
146+
expect_s3_class (prs, "data.frame")
147+
expect_equal (nrow (prs), 2L)
148+
expect_equal (ncol (prs), 22L)
149+
nms <- c (
150+
"number", "user_login", "state", "merged", "merged_by", "merge_commit",
151+
"closed", "title", "review_decision", "created_at", "closed_at",
152+
"updated_at", "additions", "deletions", "changed_files", "commit_oids",
153+
"closing_issue_refs", "total_comments", "participants", "body",
154+
"comments", "reviews"
155+
)
156+
expect_equal (names (prs), nms)
157+
158+
int_nms <- c (
159+
"number", "additions", "deletions", "changed_files", "total_comments"
160+
)
161+
logical_nms <- c ("merged", "closed")
162+
list_nms <- c ("closing_issue_refs", "comments", "reviews")
163+
non_char <- c (int_nms, logical_nms, list_nms)
164+
char_nms <- names (prs) [which (!names (prs) %in% non_char)]
165+
for (n in names (prs)) {
166+
if (n %in% int_nms) {
167+
type <- "integer"
168+
} else if (n %in% logical_nms) {
169+
type <- "logical"
170+
} else if (n %in% list_nms) {
171+
type <- "list"
172+
} else {
173+
type <- "character"
174+
}
175+
expect_type (prs [[n]], type)
176+
}
177+
})

0 commit comments

Comments
 (0)