feat(plugins/notables): advanced filters (#789)
This commit is contained in:
@@ -309,15 +309,25 @@ export function ghfilter(text, object) {
|
|||||||
console.debug(`metrics/svg/ghquery > checking ${text} against ${JSON.stringify(object)}`)
|
console.debug(`metrics/svg/ghquery > checking ${text} against ${JSON.stringify(object)}`)
|
||||||
const result = text.split(/(?<!NOT) /).map(x => x.trim()).filter(x => x).map(criteria => {
|
const result = text.split(/(?<!NOT) /).map(x => x.trim()).filter(x => x).map(criteria => {
|
||||||
const [key, filters] = criteria.split(":")
|
const [key, filters] = criteria.split(":")
|
||||||
const value = object[/^NOT /.test(key) ? key.substring(3).trim() : key.trim()]
|
const value = object[/^NOT /.test(key) ? key.substring(3).trim() : /^-/.test(key) ? key.substring(1).trim() : key.trim()]
|
||||||
console.debug(`metrics/svg/ghquery > checking ${criteria} against ${value}`)
|
console.debug(`metrics/svg/ghquery > checking ${criteria} against ${value}`)
|
||||||
|
if (value === undefined) {
|
||||||
|
console.debug(`metrics/svg/ghquery > value for ${criteria} is undefined, considering it truthy`)
|
||||||
|
return true
|
||||||
|
}
|
||||||
return filters?.split(",").map(x => x.trim()).filter(x => x).map(filter => {
|
return filters?.split(",").map(x => x.trim()).filter(x => x).map(filter => {
|
||||||
if (!Number.isFinite(Number(value))) {
|
if (!Number.isFinite(Number(value))) {
|
||||||
if (/^NOT /.test(filter))
|
if (/^NOT /.test(filter))
|
||||||
return value !== filter.substring(3).trim()
|
return value !== filter.substring(3).trim()
|
||||||
|
if (/^-/.test(key))
|
||||||
|
return value !== filter
|
||||||
return value === filter.trim()
|
return value === filter.trim()
|
||||||
}
|
}
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
case /^true$/.test(filter):
|
||||||
|
return value === true
|
||||||
|
case /^false$/.test(filter):
|
||||||
|
return value === false
|
||||||
case /^>\d+$/.test(filter):
|
case /^>\d+$/.test(filter):
|
||||||
return value > Number(filter.substring(1))
|
return value > Number(filter.substring(1))
|
||||||
case /^>=\d+$/.test(filter):
|
case /^>=\d+$/.test(filter):
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ export default async function({login, q, imports, rest, graphql, data, account,
|
|||||||
return null
|
return null
|
||||||
|
|
||||||
//Load inputs
|
//Load inputs
|
||||||
let {filter, repositories, from, indepth} = imports.metadata.plugins.notable.inputs({data, account, q})
|
let {filter, skipped, repositories, from, indepth} = imports.metadata.plugins.notable.inputs({data, account, q})
|
||||||
|
skipped.push(...data.shared["repositories.skipped"])
|
||||||
|
|
||||||
//Iterate through contributed repositories
|
//Iterate through contributed repositories
|
||||||
const commits = []
|
const commits = []
|
||||||
@@ -19,6 +20,7 @@ export default async function({login, q, imports, rest, graphql, data, account,
|
|||||||
const {user:{repositoriesContributedTo:{edges}}} = await graphql(queries.notable.contributions({login, after:cursor ? `after: "${cursor}"` : "", repositories:data.shared["repositories.batch"] || 100}))
|
const {user:{repositoriesContributedTo:{edges}}} = await graphql(queries.notable.contributions({login, after:cursor ? `after: "${cursor}"` : "", repositories:data.shared["repositories.batch"] || 100}))
|
||||||
cursor = edges?.[edges?.length - 1]?.cursor
|
cursor = edges?.[edges?.length - 1]?.cursor
|
||||||
edges
|
edges
|
||||||
|
.filter(({node}) => !((skipped.includes(node.nameWithOwner.toLocaleLowerCase()))||(skipped.includes(node.nameWithOwner.split("/")[1].toLocaleLowerCase()))))
|
||||||
.filter(({node}) => ({all:true, organization:node.isInOrganization, user:!node.isInOrganization}[from]))
|
.filter(({node}) => ({all:true, organization:node.isInOrganization, user:!node.isInOrganization}[from]))
|
||||||
.filter(({node}) => imports.ghfilter(filter, {name:node.nameWithOwner, user:node.owner.login, stars:node.stargazers.totalCount, watchers:node.watchers.totalCount, forks:node.forks.totalCount}))
|
.filter(({node}) => imports.ghfilter(filter, {name:node.nameWithOwner, user:node.owner.login, stars:node.stargazers.totalCount, watchers:node.watchers.totalCount, forks:node.forks.totalCount}))
|
||||||
.map(({node}) => commits.push({handle:node.nameWithOwner, stars:node.stargazers.totalCount, organization:node.isInOrganization, avatarUrl:node.owner.avatarUrl}))
|
.map(({node}) => commits.push({handle:node.nameWithOwner, stars:node.stargazers.totalCount, organization:node.isInOrganization, avatarUrl:node.owner.avatarUrl}))
|
||||||
@@ -79,7 +81,7 @@ export default async function({login, q, imports, rest, graphql, data, account,
|
|||||||
if (aggregated.has(key)) {
|
if (aggregated.has(key)) {
|
||||||
const aggregate = aggregated.get(key)
|
const aggregate = aggregated.get(key)
|
||||||
aggregate.aggregated++
|
aggregate.aggregated++
|
||||||
if (extras) {
|
if (indepth) {
|
||||||
const {history = 0, user:{commits = 0, percentage = 0, maintainer = false} = {}} = _extras
|
const {history = 0, user:{commits = 0, percentage = 0, maintainer = false} = {}} = _extras
|
||||||
aggregate.history = aggregate.history ?? 0
|
aggregate.history = aggregate.history ?? 0
|
||||||
aggregate.history += history
|
aggregate.history += history
|
||||||
@@ -94,9 +96,11 @@ export default async function({login, q, imports, rest, graphql, data, account,
|
|||||||
|
|
||||||
}
|
}
|
||||||
contributions = [...aggregated.values()]
|
contributions = [...aggregated.values()]
|
||||||
if (extras) {
|
if (indepth) {
|
||||||
//Normalize contribution percentage
|
//Normalize contribution percentage
|
||||||
contributions.map(aggregate => aggregate.user ? aggregate.user.percentage /= aggregate.aggregated : null)
|
contributions.map(aggregate => aggregate.user ? aggregate.user.percentage /= aggregate.aggregated : null)
|
||||||
|
//Additional filtering (no user commits means that API wasn't able to answer back, considering it as matching by default)
|
||||||
|
contributions = contributions.filter(({handle, user}) => !user?.commits ? true : imports.ghfilter(filter, {handle, commits:contributions.history, "commits.user":user.commits, "commits.user%":user.percentage*100, maintainer:user.maintainer}))
|
||||||
//Sort contribution by maintainer first and then by contribution percentage
|
//Sort contribution by maintainer first and then by contribution percentage
|
||||||
contributions = contributions.sort((a, b) => ((b.user?.percentage + b.user?.maintainer) || 0) - ((a.user?.percentage + a.user?.maintainer) || 0))
|
contributions = contributions.sort((a, b) => ((b.user?.percentage + b.user?.maintainer) || 0) - ((a.user?.percentage + a.user?.maintainer) || 0))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,20 @@ inputs:
|
|||||||
|
|
||||||
Based on [GitHub search syntax](https://docs.github.com/en/search-github/getting-started-with-searching-on-github/understanding-the-search-syntax).
|
Based on [GitHub search syntax](https://docs.github.com/en/search-github/getting-started-with-searching-on-github/understanding-the-search-syntax).
|
||||||
Supported fields are `stars`, `forks` and `watchers`
|
Supported fields are `stars`, `forks` and `watchers`
|
||||||
|
|
||||||
|
If `plugin_notable_indepth` is enabled, `commits`, `commits.user`, `commits.user%` and `maintainer` fields are also supported.
|
||||||
|
Some repositories may not be able to reported advanced stats and in the case the default behaviour will be to bypass filtering
|
||||||
type: string
|
type: string
|
||||||
default: ""
|
default: ""
|
||||||
example: stars:>500 forks:>100
|
example: stars:>500 forks:>100 maintainer:true commits.user%:>5
|
||||||
|
|
||||||
|
plugin_notable_skipped:
|
||||||
|
description: Skipped repositories
|
||||||
|
type: array
|
||||||
|
format: comma-separated
|
||||||
|
default: ""
|
||||||
|
example: my-repo-1, my-repo-2, owner/repo-3, ...
|
||||||
|
inherits: repositories_skipped
|
||||||
|
|
||||||
plugin_notable_from:
|
plugin_notable_from:
|
||||||
description: |
|
description: |
|
||||||
|
|||||||
Reference in New Issue
Block a user