diff --git a/source/app/mocks/api/github/graphql/followup.repository.collaborators.mjs b/source/app/mocks/api/github/graphql/followup.repository.collaborators.mjs
new file mode 100644
index 00000000..a5f1beb4
--- /dev/null
+++ b/source/app/mocks/api/github/graphql/followup.repository.collaborators.mjs
@@ -0,0 +1,11 @@
+/**Mocked data */
+export default function({faker, query, login = faker.internet.userName()}) {
+ console.debug("metrics/compute/mocks > mocking graphql api result > followup/repository/collaborators")
+ return ({
+ repository:{
+ collaborators:{
+ nodes:["github-user"]
+ }
+ },
+ })
+}
diff --git a/source/app/mocks/api/github/graphql/followup.repository.mjs b/source/app/mocks/api/github/graphql/followup.repository.mjs
new file mode 100644
index 00000000..6baef9fe
--- /dev/null
+++ b/source/app/mocks/api/github/graphql/followup.repository.mjs
@@ -0,0 +1,14 @@
+/**Mocked data */
+export default function({faker, query, login = faker.internet.userName()}) {
+ console.debug("metrics/compute/mocks > mocking graphql api result > followup/repository")
+ return ({
+ issues_open:{issueCount:faker.datatype.number(100)},
+ issues_drafts:{issueCount:faker.datatype.number(100)},
+ issues_skipped:{issueCount:faker.datatype.number(100)},
+ issues_closed:{issueCount:faker.datatype.number(100)},
+ pr_open:{issueCount:faker.datatype.number(100)},
+ pr_drafts:{issueCount:faker.datatype.number(100)},
+ pr_closed:{issueCount:faker.datatype.number(100)},
+ pr_merged:{issueCount:faker.datatype.number(100)},
+ })
+}
diff --git a/source/app/mocks/api/github/graphql/followup.user.mjs b/source/app/mocks/api/github/graphql/followup.user.mjs
index f9d1934d..e3458ce6 100644
--- a/source/app/mocks/api/github/graphql/followup.user.mjs
+++ b/source/app/mocks/api/github/graphql/followup.user.mjs
@@ -2,12 +2,13 @@
export default function({faker, query, login = faker.internet.userName()}) {
console.debug("metrics/compute/mocks > mocking graphql api result > followup/user")
return ({
- user:{
- issues_open:{totalCount:faker.datatype.number(100)},
- issues_closed:{totalCount:faker.datatype.number(100)},
- pr_open:{totalCount:faker.datatype.number(100)},
- pr_closed:{totalCount:faker.datatype.number(100)},
- pr_merged:{totalCount:faker.datatype.number(100)},
- },
+ issues_open:{issueCount:faker.datatype.number(100)},
+ issues_drafts:{issueCount:faker.datatype.number(100)},
+ issues_skipped:{issueCount:faker.datatype.number(100)},
+ issues_closed:{issueCount:faker.datatype.number(100)},
+ pr_open:{issueCount:faker.datatype.number(100)},
+ pr_drafts:{issueCount:faker.datatype.number(100)},
+ pr_closed:{issueCount:faker.datatype.number(100)},
+ pr_merged:{issueCount:faker.datatype.number(100)},
})
}
diff --git a/source/app/web/statics/app.placeholder.js b/source/app/web/statics/app.placeholder.js
index 8d3798dd..0995ff6a 100644
--- a/source/app/web/statics/app.placeholder.js
+++ b/source/app/web/statics/app.placeholder.js
@@ -178,36 +178,59 @@
sections: options["followup.sections"].split(",").map(x => x.trim()).filter(x => ["user", "repositories"].includes(x)),
issues: {
get count() {
- return this.open + this.closed
+ return this.open + this.closed + this.drafts + this.skipped
},
open: faker.datatype.number(1000),
closed: faker.datatype.number(1000),
+ drafts: faker.datatype.number(100),
+ skipped: faker.datatype.number(100),
+ get collaborators() {
+ return {
+ open: faker.datatype.number(this.open),
+ closed: faker.datatype.number(this.closed),
+ drafts: faker.datatype.number(this.drafts),
+ skipped: faker.datatype.number(this.skipped),
+ }
+ }
},
pr: {
get count() {
- return this.open + this.merged
+ return this.open + this.closed + this.merged + this.drafts
},
open: faker.datatype.number(1000),
closed: faker.datatype.number(1000),
merged: faker.datatype.number(1000),
+ drafts: faker.datatype.number(100),
+ get collaborators() {
+ return {
+ open: faker.datatype.number(this.open),
+ closed: faker.datatype.number(this.closed),
+ merged: faker.datatype.number(this.skipped),
+ drafts: faker.datatype.number(this.drafts),
+ }
+ }
},
user: {
issues: {
get count() {
- return this.open + this.closed
+ return this.open + this.closed + this.drafts + this.skipped
},
open: faker.datatype.number(1000),
closed: faker.datatype.number(1000),
+ drafts: faker.datatype.number(100),
+ skipped: faker.datatype.number(100),
},
pr: {
get count() {
- return this.open + this.merged
+ return this.open + this.closed + this.merged + this.drafts
},
open: faker.datatype.number(1000),
closed: faker.datatype.number(1000),
merged: faker.datatype.number(1000),
+ drafts: faker.datatype.number(100),
},
},
+ indepth:options["followup.indepth"] ? {} : null
},
})
: null),
diff --git a/source/plugins/followup/index.mjs b/source/plugins/followup/index.mjs
index fb60ce56..6c08e289 100644
--- a/source/plugins/followup/index.mjs
+++ b/source/plugins/followup/index.mjs
@@ -1,5 +1,5 @@
//Setup
-export default async function({login, data, computed, imports, q, graphql, queries, account}, {enabled = false} = {}) {
+export default async function({login, data, computed, imports, q, graphql, queries, account}, {enabled = false, extras = false} = {}) {
//Plugin execution
try {
//Check if plugin is enabled and requirements are met
@@ -7,14 +7,14 @@ export default async function({login, data, computed, imports, q, graphql, queri
return null
//Load inputs
- let {sections} = imports.metadata.plugins.followup.inputs({data, account, q})
+ let {sections, indepth} = imports.metadata.plugins.followup.inputs({data, account, q})
//Define getters
const followup = {
sections,
issues:{
get count() {
- return this.open + this.closed
+ return this.open + this.closed + this.drafts + this.skipped
},
get open() {
return computed.repositories.issues_open
@@ -22,10 +22,18 @@ export default async function({login, data, computed, imports, q, graphql, queri
get closed() {
return computed.repositories.issues_closed
},
+ drafts:0,
+ skipped:0,
+ collaborators:{
+ open:0,
+ closed:0,
+ drafts:0,
+ skipped:0,
+ }
},
pr:{
get count() {
- return this.open + this.closed + this.merged
+ return this.open + this.closed + this.merged + this.drafts
},
get open() {
return computed.repositories.pr_open
@@ -36,27 +44,72 @@ export default async function({login, data, computed, imports, q, graphql, queri
get merged() {
return computed.repositories.pr_merged
},
+ drafts:0,
+ collaborators:{
+ open:0,
+ closed:0,
+ merged:0,
+ drafts:0,
+ }
},
}
+ //Extras features
+ if (extras) {
+
+ //Indepth mode
+ if (indepth) {
+ console.debug(`metrics/compute/${login}/plugins > followup > indepth`)
+ followup.indepth = {repositories:{}}
+
+ //Process repositories
+ for (const {name:repo, owner:{login:owner}} of data.user.repositories.nodes) {
+ try {
+ console.debug(`metrics/compute/${login}/plugins > followup > processing ${owner}/${repo}`)
+ followup.indepth.repositories[`${owner}/${repo}`] = {stats:{}}
+ //Fetch users with push access
+ let {repository:{collaborators:{nodes:collaborators}}} = await graphql(queries.followup["repository.collaborators"]({repo, owner}))
+ console.debug(`metrics/compute/${login}/plugins > followup > found ${collaborators.length} collaborators`)
+ followup.indepth.repositories[`${owner}/${repo}`].collaborators = collaborators.map(({login}) => login)
+ //Fetch issues and pull requests created by collaborators
+ collaborators = collaborators.map(({login}) => `-author:${login}`).join(" ")
+ const stats = await graphql(queries.followup.repository({repo, owner, collaborators}))
+ followup.indepth.repositories[`${owner}/${repo}`] = stats
+ //Aggregate global stats
+ for (const [key, {issueCount:count}] of Object.entries(stats)) {
+ const [section, type] = key.split("_")
+ followup[section].collaborators[type] += count
+ }
+ }
+ catch (error) {
+ console.debug(error)
+ console.debug(`metrics/compute/${login}/plugins > followup > an error occured while processing ${owner}/${repo}, skipping...`)
+ }
+ }
+ }
+ }
+
//Load user issues and pull requests
if ((account === "user")&&(sections.includes("user"))) {
- const {user} = await graphql(queries.followup.user({login}))
+ const search = await graphql(queries.followup.user({login}))
followup.user = {
issues:{
get count() {
- return this.open + this.closed
+ return this.open + this.closed + this.drafts + this.skipped
},
- open:user.issues_open.totalCount,
- closed:user.issues_closed.totalCount,
+ open:search.issues_open.issueCount,
+ closed:search.issues_closed.issueCount,
+ drafts:search.issues_drafts.issueCount,
+ skipped:search.issues_skipped.issueCount,
},
pr:{
get count() {
- return this.open + this.closed + this.merged
+ return this.open + this.closed + this.merged + this.drafts
},
- open:user.pr_open.totalCount,
- closed:user.pr_closed.totalCount,
- merged:user.pr_merged.totalCount,
+ open:search.pr_open.issueCount,
+ closed:search.pr_closed.issueCount,
+ merged:search.pr_merged.issueCount,
+ drafts:search.pr_drafts.issueCount,
},
}
}
diff --git a/source/plugins/followup/metadata.yml b/source/plugins/followup/metadata.yml
index 42210f2c..2c74e9b7 100644
--- a/source/plugins/followup/metadata.yml
+++ b/source/plugins/followup/metadata.yml
@@ -22,3 +22,9 @@ inputs:
values:
- repositories # Overall status of issues and pull requests on your repositories
- user # Overall status of issues and pull requests you have created on GitHub
+
+ # Compute issues and pull requests per repositories with special highlighting for maintainers and specified users
+ plugin_followup_indepth:
+ description: Indepth follow-up processing
+ type: boolean
+ default: no
\ No newline at end of file
diff --git a/source/plugins/followup/queries/repository.collaborators.graphql b/source/plugins/followup/queries/repository.collaborators.graphql
new file mode 100644
index 00000000..ab04916c
--- /dev/null
+++ b/source/plugins/followup/queries/repository.collaborators.graphql
@@ -0,0 +1,9 @@
+query FollowupRepositoryCollaborators {
+ repository(name: "$repo", owner: "$owner") {
+ collaborators {
+ nodes {
+ login
+ }
+ }
+ }
+}
diff --git a/source/plugins/followup/queries/repository.graphql b/source/plugins/followup/queries/repository.graphql
new file mode 100644
index 00000000..578251b6
--- /dev/null
+++ b/source/plugins/followup/queries/repository.graphql
@@ -0,0 +1,26 @@
+query FollowupRepository {
+ issues_open:search(query: "repo:$owner/$repo is:issue $collaborators is:open", type: ISSUE, first: 0) {
+ issueCount
+ }
+ issues_drafts:search(query: "repo:$owner/$repo is:issue $collaborators draft:true", type: ISSUE, first: 0) {
+ issueCount
+ }
+ issues_skipped:search(query: "repo:$owner/$repo is:issue $collaborators is:closed label:wontfix,duplicate", type: ISSUE, first: 0) {
+ issueCount
+ }
+ issues_closed:search(query: "repo:$owner/$repo is:issue $collaborators is:closed", type: ISSUE, first: 0) {
+ issueCount
+ }
+ pr_open:search(query: "repo:$owner/$repo is:pr $collaborators is:open draft:false", type: ISSUE, first: 0) {
+ issueCount
+ }
+ pr_drafts:search(query: "repo:$owner/$repo is:pr $collaborators draft:true", type: ISSUE, first: 0) {
+ issueCount
+ }
+ pr_closed:search(query: "repo:$owner/$repo is:pr $collaborators is:unmerged draft:false", type: ISSUE, first: 0) {
+ issueCount
+ }
+ pr_merged:search(query: "repo:$owner/$repo is:pr $collaborators is:merged", type: ISSUE, first: 0) {
+ issueCount
+ }
+}
\ No newline at end of file
diff --git a/source/plugins/followup/queries/user.graphql b/source/plugins/followup/queries/user.graphql
index 9aa5c07e..9dc8aba7 100644
--- a/source/plugins/followup/queries/user.graphql
+++ b/source/plugins/followup/queries/user.graphql
@@ -1,19 +1,26 @@
query FollowupUser {
- user(login: "$login") {
- issues_open:issues(states: OPEN) {
- totalCount
- }
- issues_closed:issues(states: CLOSED) {
- totalCount
- }
- pr_open:pullRequests(states: OPEN) {
- totalCount
- }
- pr_closed:pullRequests(states: CLOSED) {
- totalCount
- }
- pr_merged:pullRequests(states: MERGED) {
- totalCount
- }
+ issues_open:search(query: "is:issue author:$login is:open", type: ISSUE, first: 0) {
+ issueCount
+ }
+ issues_drafts:search(query: "is:issue author:$login draft:true", type: ISSUE, first: 0) {
+ issueCount
+ }
+ issues_skipped:search(query: "is:issue author:$login is:closed label:wontfix,duplicate", type: ISSUE, first: 0) {
+ issueCount
+ }
+ issues_closed:search(query: "is:issue author:$login is:closed", type: ISSUE, first: 0) {
+ issueCount
+ }
+ pr_open:search(query: "is:pr author:$login is:open draft:false", type: ISSUE, first: 0) {
+ issueCount
+ }
+ pr_drafts:search(query: "is:pr author:$login draft:true", type: ISSUE, first: 0) {
+ issueCount
+ }
+ pr_closed:search(query: "is:pr author:$login is:unmerged draft:false", type: ISSUE, first: 0) {
+ issueCount
+ }
+ pr_merged:search(query: "is:pr author:$login is:merged", type: ISSUE, first: 0) {
+ issueCount
}
}
\ No newline at end of file
diff --git a/source/templates/classic/partials/followup.ejs b/source/templates/classic/partials/followup.ejs
index b2094504..2d73100a 100644
--- a/source/templates/classic/partials/followup.ejs
+++ b/source/templates/classic/partials/followup.ejs
@@ -26,19 +26,67 @@