Add user follow-up section (#250)

This commit is contained in:
Simon Lecoq
2021-04-22 18:03:39 +02:00
committed by GitHub
parent 42753e1c44
commit a0c2b39c37
14 changed files with 180 additions and 69 deletions

View File

@@ -36,6 +36,7 @@
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)},
releases:{totalCount:faker.datatype.number(100)},
forkCount:faker.datatype.number(100),

View File

@@ -25,6 +25,7 @@
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)},
releases:{totalCount:faker.datatype.number(100)},
forkCount:faker.datatype.number(100),

View File

@@ -0,0 +1,13 @@
/**Mocked data */
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)},
},
})
}

View File

@@ -67,6 +67,7 @@
issues_open:faker.datatype.number(1000),
issues_closed:faker.datatype.number(1000),
pr_open:faker.datatype.number(1000),
pr_closed:{totalCount:faker.datatype.number(100)},
pr_merged:faker.datatype.number(1000),
forks:faker.datatype.number(1000),
releases:faker.datatype.number(1000),
@@ -161,8 +162,13 @@
//Follow-up
...(set.plugins.enabled.followup ? ({
followup:{
sections:options["followup.sections"].split(",").map(x => x.trim()).filter(x => ["user", "repositories"].includes(x)),
issues:{get count() { return this.open + this.closed }, open:faker.datatype.number(1000), closed:faker.datatype.number(1000)},
pr:{get count() { return this.open + this.merged }, open:faker.datatype.number(1000), merged:faker.datatype.number(1000)},
pr:{get count() { return this.open + this.merged }, open:faker.datatype.number(1000), closed:faker.datatype.number(1000), merged:faker.datatype.number(1000)},
user:{
issues:{get count() { return this.open + this.closed }, open:faker.datatype.number(1000), closed:faker.datatype.number(1000)},
pr:{get count() { return this.open + this.merged }, open:faker.datatype.number(1000), closed:faker.datatype.number(1000), merged:faker.datatype.number(1000)},
}
}
}) : null),
//Notable

View File

@@ -34,6 +34,9 @@ query BaseRepositories {
pr_open: pullRequests(states: OPEN) {
totalCount
}
pr_closed: pullRequests(states: CLOSED) {
totalCount
}
pr_merged: pullRequests(states: MERGED) {
totalCount
}

View File

@@ -33,6 +33,9 @@ query BaseRepository {
pr_open: pullRequests(states: OPEN) {
totalCount
}
pr_closed: pullRequests(states: CLOSED) {
totalCount
}
pr_merged: pullRequests(states: MERGED) {
totalCount
}

View File

@@ -9,7 +9,7 @@
const {"config.animations":animations, "config.timezone":_timezone, "debug.flags":dflags} = imports.metadata.plugins.core.inputs({data, account, q})
//Init
const computed = {commits:0, sponsorships:0, licenses:{favorite:"", used:{}}, token:{}, repositories:{watchers:0, stargazers:0, issues_open:0, issues_closed:0, pr_open:0, pr_merged:0, forks:0, forked:0, releases:0}}
const computed = {commits:0, sponsorships:0, licenses:{favorite:"", used:{}}, token:{}, repositories:{watchers:0, stargazers:0, issues_open:0, issues_closed:0, pr_open:0, pr_closed:0, pr_merged:0, forks:0, forked:0, releases:0}}
const avatar = imports.imgb64(data.user.avatarUrl)
data.computed = computed
console.debug(`metrics/compute/${login} > formatting common metrics`)
@@ -57,7 +57,7 @@
//Iterate through user's repositories
for (const repository of data.user.repositories.nodes) {
//Simple properties with totalCount
for (const property of ["watchers", "stargazers", "issues_open", "issues_closed", "pr_open", "pr_merged", "releases"])
for (const property of ["watchers", "stargazers", "issues_open", "issues_closed", "pr_open", "pr_closed", "pr_merged", "releases"])
computed.repositories[property] += repository[property].totalCount
//Forks
computed.repositories.forks += repository.forkCount

View File

@@ -5,6 +5,9 @@ The *followup* plugin displays the ratio of open/closed issues and the ratio of
<table>
<td align="center">
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.followup.svg">
<details><summary>Created by user version</summary>
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.followup.user.svg">
</details>
<img width="900" height="1" alt="">
</td>
</table>
@@ -18,5 +21,6 @@ The *followup* plugin displays the ratio of open/closed issues and the ratio of
with:
# ... other options
plugin_followup: yes
plugin_followup_sections: repositories, user #
```

View File

@@ -1,5 +1,5 @@
//Setup
export default async function({data, computed, imports, q, account}, {enabled = false} = {}) {
export default async function({login, data, computed, imports, q, graphql, queries, account}, {enabled = false} = {}) {
//Plugin execution
try {
//Check if plugin is enabled and requirements are met
@@ -7,10 +7,11 @@
return null
//Load inputs
imports.metadata.plugins.followup.inputs({data, account, q})
let {sections} = imports.metadata.plugins.followup.inputs({data, account, q})
//Define getters
const followup = {
sections,
issues:{
get count() {
return this.open + this.closed
@@ -24,17 +25,42 @@
},
pr:{
get count() {
return this.open + this.merged
return this.open + this.closed + this.merged
},
get open() {
return computed.repositories.pr_open
},
get closed() {
return computed.repositories.pr_closed
},
get merged() {
return computed.repositories.pr_merged
},
},
}
//Load user issues and pull requests
if (sections.includes("user")) {
const {user} = await graphql(queries.followup.user({login}))
followup.user = {
issues:{
get count() {
return this.open + this.closed
},
open:user.issues_open.totalCount,
closed:user.issues_closed.totalCount,
},
pr:{
get count() {
return this.open + this.closed + this.merged
},
open:user.pr_open.totalCount,
closed:user.pr_closed.totalCount,
merged:user.pr_merged.totalCount,
},
}
}
//Results
return followup
}

View File

@@ -1,5 +1,5 @@
name: "🎟️ Follow-up of issues and pull requests"
cost: 0 API request
cost: 0 API request (1 GraphQL request if "user" section is enabled)
categorie: github
index: 11
supports:
@@ -13,3 +13,13 @@ inputs:
description: Display follow-up of repositories issues and pull requests
type: boolean
default: no
# Sections to display
plugin_followup_sections:
description: Sections to display
type: array
format: comma-separated
default: repositories
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

View File

@@ -0,0 +1,19 @@
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
}
}
}

View File

@@ -3,3 +3,10 @@
with:
token: MOCKED_TOKEN
plugin_followup: yes
- name: Follow-up plugin (complete)
uses: lowlighter/metrics@latest
with:
token: MOCKED_TOKEN
plugin_followup: yes
plugin_followup_sections: repositories, user

View File

@@ -1,71 +1,73 @@
<% if (plugins.followup) { %>
<div class="column">
<section>
<h2 class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M6 2a.75.75 0 01.696.471L10 10.731l1.304-3.26A.75.75 0 0112 7h3.25a.75.75 0 010 1.5h-2.742l-1.812 4.528a.75.75 0 01-1.392 0L6 4.77 4.696 8.03A.75.75 0 014 8.5H.75a.75.75 0 010-1.5h2.742l1.812-4.529A.75.75 0 016 2z"></path></svg>
Overall repositories issues and pull requests status
Overall issues and pull requests status
</h2>
<div class="row fill-width">
<section class="column">
<h3 class="no-margin-top">Issues</h3>
<% if (plugins.followup.error) { %>
<section>
<div class="field error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
<%= plugins.followup.error.message %>
</section>
<% if (plugins.followup.error) { %>
<section>
<div class="field error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
<%= plugins.followup.error.message %>
</div>
</section>
<% } else { %>
<% for (const name of plugins.followup.sections) { const section = {repositories:plugins.followup, user:plugins.followup?.user}[name] %>
<div class="column">
<h3>
<%= {repositories:`On ${user.login}'${[...user.login].pop() === "s" ? "" : "s"} repositories`, user:`Created by ${user.login}`}[name] %>
</h3>
<div class="row fill-width">
<section class="column">
<h3 class="no-margin-top">Issues</h3>
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
<mask id="issues-bar">
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
</mask>
<rect mask="url(#issues-bar)" x="0" y="0" width="<%= section.issues.count ? 0 : 220 %>" height="8" fill="#d1d5da"/>
<rect mask="url(#issues-bar)" x="0" y="0" width="<%= (section.issues.open/section.issues.count)*220 || 0 %>" height="8" fill="#28a745"/>
<rect mask="url(#issues-bar)" x="<%= (section.issues.open/section.issues.count)*220 || 0 %>" y="0" width="<%= (1-section.issues.open/section.issues.count)*220 || 0 %>" height="8" fill="#d73a49"/>
</svg>
<div class="followup legend field horizontal fill-width">
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg>
<span class="no-wrap"><%= section.issues.open %> <small>open</small></span>
</div>
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d73a49" fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 0110.65-5.003.75.75 0 00.959-1.153 8 8 0 102.592 8.33.75.75 0 10-1.444-.407A6.5 6.5 0 011.5 8zM8 12a1 1 0 100-2 1 1 0 000 2zm0-8a.75.75 0 01.75.75v3.5a.75.75 0 11-1.5 0v-3.5A.75.75 0 018 4zm4.78 4.28l3-3a.75.75 0 00-1.06-1.06l-2.47 2.47-.97-.97a.749.749 0 10-1.06 1.06l1.5 1.5a.75.75 0 001.06 0z"></path></svg>
<span class="no-wrap"><%= section.issues.closed %> <small>closed</small></span>
</div>
</div>
</section>
<% } else { %>
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
<mask id="issues-bar">
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
</mask>
<rect mask="url(#issues-bar)" x="0" y="0" width="<%= plugins.followup.issues.count ? 0 : 220 %>" height="8" fill="#d1d5da"/>
<rect mask="url(#issues-bar)" x="0" y="0" width="<%= (plugins.followup.issues.closed/plugins.followup.issues.count)*220 || 0 %>" height="8" fill="#d73a49"/>
<rect mask="url(#issues-bar)" x="<%= (plugins.followup.issues.closed/plugins.followup.issues.count)*220 || 0 %>" y="0" width="<%= (1-plugins.followup.issues.closed/plugins.followup.issues.count)*220 || 0 %>" height="8" fill="#28a745"/>
</svg>
<div class="field horizontal fill-width">
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d73a49" fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 0110.65-5.003.75.75 0 00.959-1.153 8 8 0 102.592 8.33.75.75 0 10-1.444-.407A6.5 6.5 0 011.5 8zM8 12a1 1 0 100-2 1 1 0 000 2zm0-8a.75.75 0 01.75.75v3.5a.75.75 0 11-1.5 0v-3.5A.75.75 0 018 4zm4.78 4.28l3-3a.75.75 0 00-1.06-1.06l-2.47 2.47-.97-.97a.749.749 0 10-1.06 1.06l1.5 1.5a.75.75 0 001.06 0z"></path></svg>
<span class="no-wrap"><%= plugins.followup.issues.closed %> Closed</span>
</div>
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg>
<span class="no-wrap"><%= plugins.followup.issues.open %> Open</span>
</div>
</div>
<% } %>
</section>
<section class="column">
<h3 class="no-margin-top">Pull requests</h3>
<% if (plugins.followup.error) { %>
<section>
<div class="field error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
<%= plugins.followup.error.message %>
<section class="column">
<h3 class="no-margin-top">Pull requests</h3>
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
<mask id="pr-bar">
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
</mask>
<rect mask="url(#pr-bar)" x="0" y="0" width="<%= section.pr.count ? 0 : 220 %>" height="8" fill="#d1d5da"/>
<rect mask="url(#pr-bar)" x="0" y="0" width="<%= (section.pr.open/section.pr.count)*220 || 0 %>" height="8" fill="#28a745"/>
<rect mask="url(#pr-bar)" x="<%= (section.pr.open/section.pr.count)*220 || 0 %>" y="0" width="<%= (section.pr.closed/section.pr.count)*220 || 0 %>" height="8" fill="#d73a49"/>
<rect mask="url(#pr-bar)" x="<%= ((section.pr.open+section.pr.closed)/section.pr.count)*220 || 0 %>" y="0" width="<%= (1-(section.pr.open+section.pr.closed)/section.pr.count)*220 || 0 %>" height="8" fill="#6f42c1"/>
</svg>
<div class="followup legend field horizontal fill-width">
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg>
<span class="no-wrap"><%= section.pr.open %> <small>open</small></span>
</div>
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#d73a49" fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg>
<span class="no-wrap"><%= section.pr.closed %> <small>closed</small></span>
</div>
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#6f42c1" fill-rule="evenodd" d="M5 3.254V3.25v.005a.75.75 0 110-.005v.004zm.45 1.9a2.25 2.25 0 10-1.95.218v5.256a2.25 2.25 0 101.5 0V7.123A5.735 5.735 0 009.25 9h1.378a2.251 2.251 0 100-1.5H9.25a4.25 4.25 0 01-3.8-2.346zM12.75 9a.75.75 0 100-1.5.75.75 0 000 1.5zm-8.5 4.5a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
<span class="no-wrap"><%= section.pr.merged %> <small>merged</small></span>
</div>
</div>
</section>
<% } else { %>
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="220" height="8">
<mask id="pr-bar">
<rect x="0" y="0" width="220" height="8" fill="white" rx="5"/>
</mask>
<rect mask="url(#pr-bar)" x="0" y="0" width="<%= plugins.followup.pr.count ? 0 : 220 %>" height="8" fill="#d1d5da"/>
<rect mask="url(#pr-bar)" x="0" y="0" width="<%= (plugins.followup.pr.merged/plugins.followup.pr.count)*220 || 0 %>" height="8" fill="#6f42c1"/>
<rect mask="url(#pr-bar)" x="<%= (plugins.followup.pr.merged/plugins.followup.pr.count)*220 || 0 %>" y="0" width="<%= (1-plugins.followup.pr.merged/plugins.followup.pr.count)*220 || 0 %>" height="8" fill="#28a745"/>
</svg>
<div class="field horizontal fill-width">
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#6f42c1" fill-rule="evenodd" d="M5 3.254V3.25v.005a.75.75 0 110-.005v.004zm.45 1.9a2.25 2.25 0 10-1.95.218v5.256a2.25 2.25 0 101.5 0V7.123A5.735 5.735 0 009.25 9h1.378a2.251 2.251 0 100-1.5H9.25a4.25 4.25 0 01-3.8-2.346zM12.75 9a.75.75 0 100-1.5.75.75 0 000 1.5zm-8.5 4.5a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
<span class="no-wrap"><%= plugins.followup.pr.merged %> Merged</span>
</div>
<div class="field center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#28a745" fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg>
<span class="no-wrap"><%= plugins.followup.pr.open %> Open</span>
</div>
</div>
<% } %>
</section>
</div>
</div>
</div>
</div>
<% } %>
<% } %>
<% } %>

View File

@@ -157,6 +157,22 @@
flex: 1 1 0;
}
/* Follow-up */
.followup.legend {
font-size: 12px;
}
.followup.legend svg {
margin: 0 3px;
width: 14px;
height: 14px;
}
.followup.legend svg:first-child {
margin-left: 0;
}
.followup.legend svg:last-child {
margin-right: 0;
}
/* Labels */
.label {
background-color: #58A6FF30;