From 88f9e1a41f09606f3a6872f60594858a4741ea6b Mon Sep 17 00:00:00 2001
From: Simon Lecoq <22963968+lowlighter@users.noreply.github.com>
Date: Fri, 30 Jul 2021 23:05:12 +0200
Subject: [PATCH] Add new Discussions plugin (#430) [skip ci]
---
.../github/graphql/discussions.categories.mjs | 27 +++++++++++
.../github/graphql/discussions.statistics.mjs | 11 +++++
source/app/web/statics/app.placeholder.js | 14 ++++++
source/plugins/discussions/README.md | 21 ++++++++
source/plugins/discussions/index.mjs | 46 ++++++++++++++++++
source/plugins/discussions/metadata.yml | 12 +++++
.../discussions/queries/categories.graphql | 15 ++++++
.../discussions/queries/statistics.graphql | 13 +++++
source/plugins/discussions/tests.yml | 5 ++
source/templates/classic/partials/_.json | 1 +
.../classic/partials/discussions.ejs | 48 +++++++++++++++++++
11 files changed, 213 insertions(+)
create mode 100644 source/app/mocks/api/github/graphql/discussions.categories.mjs
create mode 100644 source/app/mocks/api/github/graphql/discussions.statistics.mjs
create mode 100644 source/plugins/discussions/README.md
create mode 100644 source/plugins/discussions/index.mjs
create mode 100644 source/plugins/discussions/metadata.yml
create mode 100644 source/plugins/discussions/queries/categories.graphql
create mode 100644 source/plugins/discussions/queries/statistics.graphql
create mode 100644 source/plugins/discussions/tests.yml
create mode 100644 source/templates/classic/partials/discussions.ejs
diff --git a/source/app/mocks/api/github/graphql/discussions.categories.mjs b/source/app/mocks/api/github/graphql/discussions.categories.mjs
new file mode 100644
index 00000000..e5fa8fdc
--- /dev/null
+++ b/source/app/mocks/api/github/graphql/discussions.categories.mjs
@@ -0,0 +1,27 @@
+/**Mocked data */
+export default function({faker, query, login = faker.internet.userName()}) {
+ console.debug("metrics/compute/mocks > mocking graphql api result > contributors/commit")
+ return /after: "MOCKED_CURSOR"/m.test(query)
+ ? ({
+ user:{
+ repositoryDiscussions:{
+ edges:[],
+ nodes:[],
+ }
+ }
+ })
+ : ({
+ user:{
+ repositoryDiscussions:{
+ edges:new Array(100).fill(null).map(_ => ({cursor:"MOCKED_CURSOR"})),
+ nodes:new Array(100).fill(null).map(_ => ({
+ category:{
+ emoji:faker.random.arrayElement([":chart_with_upwards_trend:", ":chart_with_downwards_trend:", ":bar_char:"]),
+ name:faker.lorem.slug()
+ }
+ }))
+ }
+ }
+ })
+}
+
diff --git a/source/app/mocks/api/github/graphql/discussions.statistics.mjs b/source/app/mocks/api/github/graphql/discussions.statistics.mjs
new file mode 100644
index 00000000..b899f7ea
--- /dev/null
+++ b/source/app/mocks/api/github/graphql/discussions.statistics.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 > contributors/commit")
+ return ({
+ user:{
+ started:{totalCount:faker.datatype.number(1000)},
+ comments:{totalCount:faker.datatype.number(1000)},
+ answers:{totalCount:faker.datatype.number(1000)}
+ }
+ })
+}
\ No newline at end of file
diff --git a/source/app/web/statics/app.placeholder.js b/source/app/web/statics/app.placeholder.js
index 63d4cc51..2c327712 100644
--- a/source/app/web/statics/app.placeholder.js
+++ b/source/app/web/statics/app.placeholder.js
@@ -586,6 +586,20 @@
},
})
: null),
+ //Discussions
+ ...(set.plugins.enabled.discussions
+ ? ({
+ discussions: {
+ categories: {
+ stats: { '🙏 Q&A': faker.datatype.number(100), '📣 Announcements': faker.datatype.number(100), '💡 Ideas': faker.datatype.number(100), '💬 General': faker.datatype.number(100) },
+ favorite: '📣 Announcements'
+ },
+ started: faker.datatype.number(1000),
+ comments: faker.datatype.number(1000),
+ answers: faker.datatype.number(1000),
+ },
+ })
+ : null),
//Posts
...(set.plugins.enabled.posts
? ({
diff --git a/source/plugins/discussions/README.md b/source/plugins/discussions/README.md
new file mode 100644
index 00000000..23bae3c4
--- /dev/null
+++ b/source/plugins/discussions/README.md
@@ -0,0 +1,21 @@
+### 💬 Discussions
+
+The *discussions* plugin displays your GitHub discussions metrics.
+
+
+
+
+
+ |
+
+
+#### ℹ️ Examples workflows
+
+[➡️ Available options for this plugin](metadata.yml)
+
+```yaml
+- uses: lowlighter/metrics@latest
+ with:
+ # ... other options
+ plugin_discussions: yes
+```
\ No newline at end of file
diff --git a/source/plugins/discussions/index.mjs b/source/plugins/discussions/index.mjs
new file mode 100644
index 00000000..8327fb68
--- /dev/null
+++ b/source/plugins/discussions/index.mjs
@@ -0,0 +1,46 @@
+//Setup
+ export default async function({login, q, imports, graphql, queries, data, account}, {enabled = false} = {}) {
+ //Plugin execution
+ try {
+ //Check if plugin is enabled and requirements are met
+ if ((!enabled)||(!q.discussions))
+ return null
+
+ //Load inputs
+ imports.metadata.plugins.discussions.inputs({data, account, q})
+ const discussions = {categories:{}}
+
+ //Fetch general statistics
+ const stats = Object.fromEntries(Object.entries((await graphql(queries.discussions.statistics({login}))).user).map(([key, value]) => [key, value.totalCount]))
+ Object.assign(discussions, stats)
+
+ //Load started discussions
+ {
+ const fetched = []
+ const categories = {}
+ let cursor = null
+ let pushed = 0
+ do {
+ console.debug(`metrics/compute/${login}/discussions > retrieving discussions after ${cursor}`)
+ const {user:{repositoryDiscussions:{edges = [], nodes = []} = {}}} = await graphql(queries.discussions.categories({login, after:cursor ? `after: "${cursor}"` : ""}))
+ cursor = edges?.[edges?.length - 1]?.cursor
+ fetched.push(...nodes)
+ pushed = nodes.length
+ console.debug(`metrics/compute/${login}/discussions > retrieved ${pushed} discussions after ${cursor}`)
+ } while ((pushed) && (cursor))
+
+ //Compute favorite category
+ for (const category of [...fetched.map(({category:{emoji, name}}) => `${imports.emoji.get(emoji)} ${name}`)])
+ categories[category] = (categories[category] ?? 0) + 1
+ discussions.categories.stats = categories
+ discussions.categories.favorite = Object.entries(categories).sort((a, b) => b[1] - a[1]).map(([name]) => name).shift() ?? null
+ }
+
+ //Results
+ return discussions
+ }
+ //Handle errors
+ catch (error) {
+ throw {error:{message:"An error occured", instance:error}}
+ }
+ }
diff --git a/source/plugins/discussions/metadata.yml b/source/plugins/discussions/metadata.yml
new file mode 100644
index 00000000..84058a96
--- /dev/null
+++ b/source/plugins/discussions/metadata.yml
@@ -0,0 +1,12 @@
+name: "💬 Discussions"
+cost: 1 GraphQL request + 1 GraphQL request per 100 discussions started
+category: github
+supports:
+ - user
+inputs:
+
+ # Enable or disable plugin
+ plugin_discussions:
+ description: GitHub discussions metrics
+ type: boolean
+ default: no
\ No newline at end of file
diff --git a/source/plugins/discussions/queries/categories.graphql b/source/plugins/discussions/queries/categories.graphql
new file mode 100644
index 00000000..cc6ba0f6
--- /dev/null
+++ b/source/plugins/discussions/queries/categories.graphql
@@ -0,0 +1,15 @@
+query DiscussionsCategories {
+ user(login: "$login") {
+ repositoryDiscussions($after first: 100, orderBy: {field: CREATED_AT, direction: DESC}) {
+ edges {
+ cursor
+ }
+ nodes {
+ category {
+ emoji
+ name
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/plugins/discussions/queries/statistics.graphql b/source/plugins/discussions/queries/statistics.graphql
new file mode 100644
index 00000000..281bf769
--- /dev/null
+++ b/source/plugins/discussions/queries/statistics.graphql
@@ -0,0 +1,13 @@
+query DiscussionsStatistics {
+ user(login: "$login") {
+ started: repositoryDiscussions {
+ totalCount
+ }
+ comments: repositoryDiscussionComments {
+ totalCount
+ }
+ answers: repositoryDiscussionComments(onlyAnswers: true) {
+ totalCount
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/plugins/discussions/tests.yml b/source/plugins/discussions/tests.yml
new file mode 100644
index 00000000..d1a46e9a
--- /dev/null
+++ b/source/plugins/discussions/tests.yml
@@ -0,0 +1,5 @@
+- name: Discussions plugin (default)
+ uses: lowlighter/metrics@latest
+ with:
+ token: MOCKED_TOKEN
+ plugin_discussions: yes
\ No newline at end of file
diff --git a/source/templates/classic/partials/_.json b/source/templates/classic/partials/_.json
index cc4d8fc5..0834249c 100644
--- a/source/templates/classic/partials/_.json
+++ b/source/templates/classic/partials/_.json
@@ -25,6 +25,7 @@
"anilist",
"wakatime",
"skyline",
+ "discussions",
"support",
"stackoverflow",
"stock",
diff --git a/source/templates/classic/partials/discussions.ejs b/source/templates/classic/partials/discussions.ejs
new file mode 100644
index 00000000..e8587b2f
--- /dev/null
+++ b/source/templates/classic/partials/discussions.ejs
@@ -0,0 +1,48 @@
+<% if (plugins.discussions) { %>
+
+
+
+ <% if (!plugins.discussions.error) { %>
+ <%= plugins.discussions.started %> Discussion<%= s(plugins.discussions.started) %> started
+ <% } else { %>
+ Discussions
+ <% } %>
+
+ <% if (plugins.discussions.error) { %>
+
+
+
+
+ <%= plugins.discussions.error.message %>
+
+
+
+ <% } else { %>
+
+
+
+
+ <%= plugins.discussions.comments %> Comment<%= s(plugins.discussions.comments) %>
+
+
+
+
+
+ <%= plugins.discussions.answers %> Answer<%= s(plugins.discussions.answers) %>
+
+
+
+ <% if (Object.keys(plugins.discussions.categories.stats).length) { %>
+
+
+
+ <% for (const [category, posts] of Object.entries(plugins.discussions.categories.stats)) { %>
+
<%= category %>
<%= posts %>
+ <% } %>
+
+
+
+ <% } %>
+ <% } %>
+
+<% } %>
\ No newline at end of file