Version 2.10 (#30)
This commit is contained in:
@@ -55,7 +55,8 @@
|
||||
"languages.ignored":"",
|
||||
"languages.skipped":"",
|
||||
"pagespeed.detailed":false,
|
||||
"habits.from":100,
|
||||
"pagespeed.screenshot":false,
|
||||
"habits.from":200,
|
||||
"habits.days":14,
|
||||
"habits.facts":true,
|
||||
"habits.charts":false,
|
||||
@@ -65,6 +66,8 @@
|
||||
"posts.source":"dev.to",
|
||||
"isocalendar.duration":"half-year",
|
||||
"projects.limit":4,
|
||||
"projects.repositories":"",
|
||||
"topics.mode":"starred",
|
||||
"topics.sort":"stars",
|
||||
"topics.limit":12,
|
||||
"tweets.limit":2,
|
||||
@@ -78,6 +81,7 @@
|
||||
descriptions:{
|
||||
classic:"Classic template",
|
||||
terminal:"Terminal template",
|
||||
repository:"(hidden)",
|
||||
},
|
||||
},
|
||||
generated:{
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="step">
|
||||
<h2>2. Select a template</h2>
|
||||
<div class="templates">
|
||||
<label v-for="template in templates.list" :key="template">
|
||||
<label v-for="template in templates.list" :key="template" v-show="templates.descriptions[template] !== '(hidden)'">
|
||||
<input type="radio" v-model="templates.selected" :value="template" @change="load" :disabled="generated.pending">
|
||||
{{ templates.descriptions[template] || template }}
|
||||
</label>
|
||||
@@ -78,6 +78,10 @@
|
||||
Detailed PageSpeed report
|
||||
<input type="checkbox" v-model="plugins.options['pagespeed.detailed']" @change="load">
|
||||
</label>
|
||||
<label>
|
||||
Include a website screenshot
|
||||
<input type="checkbox" v-model="plugins.options['pagespeed.screenshot']" @change="load">
|
||||
</label>
|
||||
</div>
|
||||
<div class="options-group" v-if="plugins.enabled.languages">
|
||||
<h4>{{ plugins.descriptions.languages }}</h4>
|
||||
@@ -134,6 +138,13 @@
|
||||
</div>
|
||||
<div class="options-group" v-if="plugins.enabled.topics">
|
||||
<h4>{{ plugins.descriptions.topics }}</h4>
|
||||
<label>
|
||||
Topics display mode
|
||||
<select v-model="plugins.options['topics.mode']" @change="load">
|
||||
<option value="starred">Starred topics</option>
|
||||
<option value="mastered">Known and mastered technologies</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Topics sorting
|
||||
<select v-model="plugins.options['topics.sort']">
|
||||
@@ -154,6 +165,10 @@
|
||||
Number of projects to display
|
||||
<input type="number" v-model="plugins.options['projects.limit']" min="1" max="100" @change="load">
|
||||
</label>
|
||||
<label>
|
||||
Repositories projects to display (comma separated)
|
||||
<input type="text" v-model="plugins.options['projects.repositories']" @change="load">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -27,8 +27,9 @@
|
||||
const s = (value, end = "") => value > 1 ? {y:"ies", "":"s"}[end] : end
|
||||
if ((!(template in Templates))||(!(template in conf.templates))||((conf.settings.templates.enabled.length)&&(!conf.settings.templates.enabled.includes(template))))
|
||||
throw new Error("unsupported template")
|
||||
const {query, image, style, fonts} = conf.templates[template]
|
||||
const data = {base:{}, config:{}}
|
||||
const {image, style, fonts} = conf.templates[template]
|
||||
const queries = conf.queries
|
||||
const data = {base:{}, config:{}, errors:[], plugins:{}, computed:{}}
|
||||
|
||||
//Base parts
|
||||
{
|
||||
@@ -44,29 +45,39 @@
|
||||
else {
|
||||
//Query data from GitHub API
|
||||
console.debug(`metrics/compute/${login} > graphql query`)
|
||||
Object.assign(data, await graphql(query
|
||||
.replace(/[$]login/, `"${login}"`)
|
||||
.replace(/[$]repositories/, `${repositories}`)
|
||||
.replace(/[$]calendar.to/, `"${(new Date()).toISOString()}"`)
|
||||
.replace(/[$]calendar.from/, `"${(new Date(Date.now()-14*24*60*60*1000)).toISOString()}"`)
|
||||
))
|
||||
|
||||
Object.assign(data, await graphql(queries.common({login, "calendar.from":new Date(Date.now()-14*24*60*60*1000).toISOString(), "calendar.to":(new Date()).toISOString()})))
|
||||
//Query repositories from GitHub API
|
||||
{
|
||||
//Iterate through repositories
|
||||
let cursor = null
|
||||
let pushed = 0
|
||||
do {
|
||||
console.debug(`metrics/compute/${login} > retrieving repositories after ${cursor}`)
|
||||
const {user:{repositories:{edges, nodes}}} = await graphql(queries.repositories({login, after:cursor ? `after: "${cursor}"` : "", repositories:Math.min(repositories, 100)}))
|
||||
cursor = edges?.[edges?.length-1]?.cursor
|
||||
data.user.repositories.nodes.push(...nodes)
|
||||
pushed = nodes.length
|
||||
} while ((pushed)&&(cursor)&&(data.user.repositories.nodes.length < repositories))
|
||||
//Limit repositories
|
||||
console.debug(`metrics/compute/${login} > keeping only ${repositories} repositories`)
|
||||
data.user.repositories.nodes.splice(repositories)
|
||||
console.debug(`metrics/compute/${login} > loaded ${data.user.repositories.nodes.length} repositories`)
|
||||
}
|
||||
//Compute metrics
|
||||
console.debug(`metrics/compute/${login} > compute`)
|
||||
const computer = Templates[template].default || Templates[template]
|
||||
await computer({login, q, dflags}, {conf, data, rest, graphql, plugins}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, run, fs, os, paths, util, format, bytes, shuffle, htmlescape, urlexpand}})
|
||||
await computer({login, q, dflags}, {conf, data, rest, graphql, plugins, queries}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, run, fs, os, paths, util, format, bytes, shuffle, htmlescape, urlexpand}})
|
||||
const promised = await Promise.all(pending)
|
||||
|
||||
//Check plugins errors
|
||||
{
|
||||
const errors = promised.filter(({result = null}) => result?.error)
|
||||
if (die) {
|
||||
if (errors.length)
|
||||
throw new Error(`${errors.length} error${s(errors.length)} found...`)
|
||||
}
|
||||
else {
|
||||
console.warn(`${errors.length} error${s(errors.length)} found, ignoring...`)
|
||||
console.warn(util.inspect(errors, {depth:Infinity, maxStringLength:256}))
|
||||
const errors = [...promised.filter(({result = null}) => result?.error), ...data.errors]
|
||||
if (errors.length) {
|
||||
console.warn(`metrics/compute/${login} > ${errors.length} errors !`)
|
||||
if (die)
|
||||
throw new Error(`An error occured during rendering, dying`)
|
||||
else
|
||||
console.warn(util.inspect(errors, {depth:Infinity, maxStringLength:256}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,7 +170,7 @@
|
||||
/** Placeholder generator */
|
||||
function placeholder({data, conf, q}) {
|
||||
//Proxifier
|
||||
const proxify = (target) => typeof target === "object" ? new Proxy(target, {
|
||||
const proxify = (target) => (typeof target === "object")&&(target) ? new Proxy(target, {
|
||||
get(target, property) {
|
||||
//Primitive conversion
|
||||
if (property === Symbol.toPrimitive)
|
||||
@@ -196,11 +207,11 @@
|
||||
[key, proxify({
|
||||
posts:{source:"########", list:new Array("posts.limit" in q ? Math.max(Number(q["posts.limit"])||0, 0) : 2).fill({title:"###### ###### ####### ######", date:"####"})},
|
||||
music:{provider:"########", tracks:new Array("music.limit" in q ? Math.max(Number(q["music.limit"])||0, 0) : 4).fill({name:"##########", artist:"######", artwork:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg=="})},
|
||||
pagespeed:{detailed:!!q["pagespeed.detailed"], scores:["Performance", "Accessibility", "Best Practices", "SEO"].map(title => ({title, score:NaN}))},
|
||||
pagespeed:{detailed:!!q["pagespeed.detailed"], screenshot:!!q["pagespeed.screenshot"] ? "data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==" : null, scores:["Performance", "Accessibility", "Best Practices", "SEO"].map(title => ({title, score:NaN}))},
|
||||
followup:{issues:{count:0}, pr:{count:0}},
|
||||
habits:{facts:!!(q["habits.facts"] ?? 1), charts:!!q["habits.charts"], indents:{style:`########`}, commits:{day:"####"}, linguist:{ordered:[]}},
|
||||
languages:{favorites:new Array(7).fill(null).map((_, x) => ({x, name:"######", color:"#ebedf0", value:1/(x+1)}))},
|
||||
topics:{list:[...new Array("topics.limit" in q ? Math.max(Number(q["topics.limit"])||0, 0) : 12).fill(null).map(() => ({name:"######", description:"", icon:null})), {name:`And ## more...`, description:"", icon:null}]},
|
||||
topics:{mode:"topics.mode" in q ? q["topics.mode"] : "starred", list:[...new Array("topics.limit" in q ? Math.max(Number(q["topics.limit"])||0, 0) : 12).fill(null).map(() => ({name:"######", description:"", icon:null})), {name:`And ## more...`, description:"", icon:null}]},
|
||||
projects:{list:[...new Array("projects.limit" in q ? Math.max(Number(q["projects.limit"])||0, 0) : 4).fill(null).map(() => ({name:"########", updated:"########", progress:{enabled:true, todo:"##", doing:"##", done:"##", total:"##"}}))]},
|
||||
tweets:{profile:{username:"########", verified:false}, list:[...new Array("tweets.limit" in q ? Math.max(Number(q["tweets.limit"])||0, 0) : 2).fill(null).map(() => ({text:"###### ###### ####### ######".repeat(4), created_at:Date.now()}))]},
|
||||
}[key]??{})]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, graphql, q}, {enabled = false} = {}) {
|
||||
export default async function ({login, graphql, q, queries}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -7,40 +7,22 @@
|
||||
return null
|
||||
//Retrieve gists from graphql api
|
||||
console.debug(`metrics/compute/${login}/plugins > gists > querying api`)
|
||||
const {user:{gists}} = await graphql(`
|
||||
query Gists {
|
||||
user(login: "${login}") {
|
||||
gists(last: 100) {
|
||||
totalCount
|
||||
nodes {
|
||||
stargazerCount
|
||||
isFork
|
||||
forks {
|
||||
totalCount
|
||||
}
|
||||
comments {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
const {user:{gists}} = await graphql(queries.gists({login}))
|
||||
//Iterate through gists
|
||||
console.debug(`metrics/compute/${login}/plugins > gists > processing ${gists.nodes.length} gists`)
|
||||
let stargazers = 0, forks = 0, comments = 0
|
||||
let stargazers = 0, forks = 0, comments = 0, files = 0
|
||||
for (const gist of gists.nodes) {
|
||||
//Skip forks
|
||||
if (gist.isFork)
|
||||
continue
|
||||
//Compute stars, forks and comments
|
||||
//Compute stars, forks, comments and files count
|
||||
stargazers += gist.stargazerCount
|
||||
forks += gist.forks.totalCount
|
||||
comments += gist.comments.totalCount
|
||||
files += gist.files.length
|
||||
}
|
||||
//Results
|
||||
return {totalCount:gists.totalCount, stargazers, forks, comments}
|
||||
return {totalCount:gists.totalCount, stargazers, forks, files, comments}
|
||||
}
|
||||
//Handle errors
|
||||
catch (error) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, graphql, q}, {enabled = false} = {}) {
|
||||
export default async function ({login, graphql, q, queries}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -24,24 +24,7 @@
|
||||
const calendar = {}
|
||||
for (const [name, from, to] of [["padding", padding, start], ["weeks", start, now]]) {
|
||||
console.debug(`metrics/compute/${login}/plugins > isocalendar > loading ${name} from "${from.toISOString()}" to "${to.toISOString()}"`)
|
||||
const {user:{calendar:{contributionCalendar:{weeks}}}} = await graphql(`
|
||||
query Calendar {
|
||||
user(login: "${login}") {
|
||||
calendar:contributionsCollection(from: "${from.toISOString()}", to: "${to.toISOString()}") {
|
||||
contributionCalendar {
|
||||
weeks {
|
||||
contributionDays {
|
||||
contributionCount
|
||||
color
|
||||
date
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
const {user:{calendar:{contributionCalendar:{weeks}}}} = await graphql(queries.calendar({login, from:from.toISOString(), to:to.toISOString()}))
|
||||
calendar[name] = weeks
|
||||
}
|
||||
//Apply padding
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
//Limit tracklist
|
||||
if (limit > 0) {
|
||||
console.debug(`metrics/compute/${login}/plugins > music > keeping only ${limit} tracks`)
|
||||
tracks = tracks.slice(0, limit)
|
||||
tracks.splice(limit)
|
||||
}
|
||||
//Convert artworks to base64
|
||||
console.debug(`metrics/compute/${login}/plugins > music > loading artworks`)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
if ((!enabled)||(!q.pagespeed)||(!data.user.websiteUrl))
|
||||
return null
|
||||
//Parameters override
|
||||
let {"pagespeed.detailed":detailed = false} = q
|
||||
let {"pagespeed.detailed":detailed = false, "pagespeed.screenshot":screenshot = false} = q
|
||||
//Duration in days
|
||||
detailed = !!detailed
|
||||
//Format url if needed
|
||||
@@ -18,12 +18,18 @@
|
||||
console.debug(`metrics/compute/${login}/plugins > pagespeed > querying api for ${url}`)
|
||||
const scores = new Map()
|
||||
await Promise.all(["performance", "accessibility", "best-practices", "seo"].map(async category => {
|
||||
console.debug(`metrics/compute/${login}/plugins > pagespeed > performing audit ${category}`)
|
||||
const request = await imports.axios.get(`https://www.googleapis.com/pagespeedonline/v5/runPagespeed?category=${category}&url=${url}&key=${token}`)
|
||||
console.debug(request.data)
|
||||
const {score, title} = request.data.lighthouseResult.categories[category]
|
||||
scores.set(category, {score, title})
|
||||
console.debug(`metrics/compute/${login}/plugins > pagespeed > performed audit ${category} (status code ${request.status})`)
|
||||
//Perform audit
|
||||
console.debug(`metrics/compute/${login}/plugins > pagespeed > performing audit ${category}`)
|
||||
const request = await imports.axios.get(`https://www.googleapis.com/pagespeedonline/v5/runPagespeed?category=${category}&url=${url}&key=${token}`)
|
||||
console.debug(request.data)
|
||||
const {score, title} = request.data.lighthouseResult.categories[category]
|
||||
scores.set(category, {score, title})
|
||||
console.debug(`metrics/compute/${login}/plugins > pagespeed > performed audit ${category} (status code ${request.status})`)
|
||||
//Store screenshot
|
||||
if ((screenshot)&&(category === "performance")) {
|
||||
result.screenshot = request.data.lighthouseResult.audits["final-screenshot"].details.data
|
||||
console.debug(`metrics/compute/${login}/plugins > pagespeed > performed audit ${category} (status code ${request.status})`)
|
||||
}
|
||||
}))
|
||||
result.scores = [scores.get("performance"), scores.get("accessibility"), scores.get("best-practices"), scores.get("seo")]
|
||||
//Detailed metrics
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
//Limit tracklist
|
||||
if (limit > 0) {
|
||||
console.debug(`metrics/compute/${login}/plugins > posts > keeping only ${limit} posts`)
|
||||
posts = posts.slice(0, limit)
|
||||
posts.splice(limit)
|
||||
}
|
||||
//Results
|
||||
return {source, list:posts}
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
//Setup
|
||||
export default async function ({login, graphql, q}, {enabled = false} = {}) {
|
||||
export default async function ({login, graphql, q, queries}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
if ((!enabled)||(!q.projects))
|
||||
return null
|
||||
//Parameters override
|
||||
let {"projects.limit":limit = 4} = q
|
||||
let {"projects.limit":limit = 4, "projects.repositories":repositories = ""} = q
|
||||
//Repositories projects
|
||||
repositories = repositories?.split(",").map(repository => repository.trim()).filter(repository => /[-\w]+[/][-\w]+[/]projects[/]\d+/.test(repository)) ?? []
|
||||
//Limit
|
||||
limit = Math.max(1, Math.min(100, Number(limit)))
|
||||
//Retrieve contribution calendar from graphql api
|
||||
limit = Math.max(repositories.length, Math.min(100, Number(limit)))
|
||||
//Retrieve user owned projects from graphql api
|
||||
console.debug(`metrics/compute/${login}/plugins > projects > querying api`)
|
||||
const {user:{projects}} = await graphql(`
|
||||
query Projects {
|
||||
user(login: "${login}") {
|
||||
projects(last: ${limit}, states: OPEN, orderBy: {field: UPDATED_AT, direction: DESC}) {
|
||||
totalCount
|
||||
nodes {
|
||||
name
|
||||
updatedAt
|
||||
progress {
|
||||
doneCount
|
||||
inProgressCount
|
||||
todoCount
|
||||
enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
const {user:{projects}} = await graphql(queries.projects({login, limit}))
|
||||
//Retrieve repositories projects from graphql api
|
||||
for (const identifier of repositories) {
|
||||
//Querying repository project
|
||||
console.debug(`metrics/compute/${login}/plugins > projects > querying api for ${identifier}`)
|
||||
const {user, repository, id} = identifier.match(/(?<user>[-\w]+)[/](?<repository>[-\w]+)[/]projects[/](?<id>\d+)/)?.groups
|
||||
const {user:{repository:{project}}} = await graphql(queries["projects.repository"]({user, repository, id}))
|
||||
//Adding it to projects list
|
||||
console.debug(`metrics/compute/${login}/plugins > projects > registering ${identifier}`)
|
||||
project.name = `${project.name} (${user}/${repository})`
|
||||
projects.nodes.unshift(project)
|
||||
projects.totalCount++
|
||||
}
|
||||
|
||||
//Iterate through projects and format them
|
||||
console.debug(`metrics/compute/${login}/plugins > posts > processing ${projects.nodes.length} projects`)
|
||||
console.debug(`metrics/compute/${login}/plugins > projects > processing ${projects.nodes.length} projects`)
|
||||
const list = []
|
||||
for (const project of projects.nodes) {
|
||||
//Format date
|
||||
@@ -49,6 +45,9 @@
|
||||
//Append
|
||||
list.push({name:project.name, updated, progress:{enabled, todo, doing, done, total:todo+doing+done}})
|
||||
}
|
||||
//Limit
|
||||
console.debug(`metrics/compute/${login}/plugins > projects > keeping only ${limit} projects`)
|
||||
list.splice(limit)
|
||||
//Results
|
||||
return {list, totalCount:projects.totalCount}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
if ((!enabled)||(!q.topics))
|
||||
return null
|
||||
//Parameters override
|
||||
let {"topics.sort":sort = "stars", "topics.limit":limit = 15} = q
|
||||
let {"topics.sort":sort = "stars", "topics.mode":mode = "starred", "topics.limit":limit = (mode === "mastered" ? 0 : 15)} = q
|
||||
//Shuffle
|
||||
const shuffle = (sort === "random")
|
||||
//Sort method
|
||||
sort = {starred:"created", activity:"updated", stars:"stars", random:"created"}[sort] ?? "starred"
|
||||
//Limit
|
||||
limit = Math.max(1, Math.min(20, Number(limit)))
|
||||
limit = Math.max(0, Math.min(20, Number(limit)))
|
||||
//Mode
|
||||
mode = ["starred", "mastered"].includes(mode) ? mode : "starred"
|
||||
//Start puppeteer and navigate to topics
|
||||
console.debug(`metrics/compute/${login}/plugins > topics > searching starred topics`)
|
||||
let topics = []
|
||||
@@ -49,11 +51,10 @@
|
||||
console.debug(`metrics/compute/${login}/plugins > topics > shuffling topics`)
|
||||
topics = imports.shuffle(topics)
|
||||
}
|
||||
//Limit topics
|
||||
if (limit > 0) {
|
||||
//Limit topics (starred mode)
|
||||
if ((mode === "starred")&&(limit > 0)) {
|
||||
console.debug(`metrics/compute/${login}/plugins > topics > keeping only ${limit} topics`)
|
||||
const removed = topics.slice(limit)
|
||||
topics = topics.slice(0, limit)
|
||||
const removed = topics.splice(limit)
|
||||
topics.push({name:`And ${removed.length} more...`, description:removed.map(({name}) => name).join(", "), icon:null})
|
||||
}
|
||||
//Convert icons to base64
|
||||
@@ -66,8 +67,18 @@
|
||||
//Escape HTML description
|
||||
topic.description = imports.htmlescape(topic.description)
|
||||
}
|
||||
//Filter topics with icon (mastered mode)
|
||||
if (mode === "mastered") {
|
||||
console.debug(`metrics/compute/${login}/plugins > topics > filtering topics with icon`)
|
||||
topics = topics.filter(({icon}) => icon)
|
||||
}
|
||||
//Limit topics (mastered mode)
|
||||
if ((mode === "mastered")&&(limit > 0)) {
|
||||
console.debug(`metrics/compute/${login}/plugins > topics > keeping only ${limit} topics`)
|
||||
topics.splice(limit)
|
||||
}
|
||||
//Results
|
||||
return {list:topics}
|
||||
return {mode, list:topics}
|
||||
}
|
||||
//Handle errors
|
||||
catch (error) {
|
||||
|
||||
15
src/queries/calendar.graphql
Normal file
15
src/queries/calendar.graphql
Normal file
@@ -0,0 +1,15 @@
|
||||
query Calendar {
|
||||
user(login: "$login") {
|
||||
calendar:contributionsCollection(from: "$from", to: "$to") {
|
||||
contributionCalendar {
|
||||
weeks {
|
||||
contributionDays {
|
||||
contributionCount
|
||||
color
|
||||
date
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
query Metrics {
|
||||
user(login: $login) {
|
||||
user(login: "$login") {
|
||||
databaseId
|
||||
name
|
||||
login
|
||||
@@ -11,45 +11,11 @@ query Metrics {
|
||||
gists {
|
||||
totalCount
|
||||
}
|
||||
repositories(last: $repositories, isFork: false, ownerAffiliations: OWNER) {
|
||||
repositories(last: 0, isFork: false, ownerAffiliations: OWNER) {
|
||||
totalCount
|
||||
totalDiskUsage
|
||||
nodes {
|
||||
name
|
||||
watchers {
|
||||
totalCount
|
||||
}
|
||||
stargazers {
|
||||
totalCount
|
||||
}
|
||||
languages(first: 4) {
|
||||
edges {
|
||||
size
|
||||
node {
|
||||
color
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
issues_open: issues(states: OPEN) {
|
||||
totalCount
|
||||
}
|
||||
issues_closed: issues(states: CLOSED) {
|
||||
totalCount
|
||||
}
|
||||
pr_open: pullRequests(states: OPEN) {
|
||||
totalCount
|
||||
}
|
||||
pr_merged: pullRequests(states: MERGED) {
|
||||
totalCount
|
||||
}
|
||||
releases {
|
||||
totalCount
|
||||
}
|
||||
forkCount
|
||||
licenseInfo {
|
||||
spdxId
|
||||
}
|
||||
}
|
||||
}
|
||||
packages {
|
||||
@@ -75,7 +41,7 @@ query Metrics {
|
||||
totalPullRequestContributions
|
||||
totalPullRequestReviewContributions
|
||||
}
|
||||
calendar:contributionsCollection(from: $calendar.from, to: $calendar.to) {
|
||||
calendar:contributionsCollection(from: "$calendar.from", to: "$calendar.to") {
|
||||
contributionCalendar {
|
||||
weeks {
|
||||
contributionDays {
|
||||
20
src/queries/gists.graphql
Normal file
20
src/queries/gists.graphql
Normal file
@@ -0,0 +1,20 @@
|
||||
query Gists {
|
||||
user(login: "$login") {
|
||||
gists(last: 100) {
|
||||
totalCount
|
||||
nodes {
|
||||
stargazerCount
|
||||
isFork
|
||||
forks {
|
||||
totalCount
|
||||
}
|
||||
files {
|
||||
name
|
||||
}
|
||||
comments {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/queries/projects.graphql
Normal file
17
src/queries/projects.graphql
Normal file
@@ -0,0 +1,17 @@
|
||||
query Projects {
|
||||
user(login: "$login") {
|
||||
projects(last: $limit, states: OPEN, orderBy: {field: UPDATED_AT, direction: DESC}) {
|
||||
totalCount
|
||||
nodes {
|
||||
name
|
||||
updatedAt
|
||||
progress {
|
||||
doneCount
|
||||
inProgressCount
|
||||
todoCount
|
||||
enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/queries/projects.repository.graphql
Normal file
16
src/queries/projects.repository.graphql
Normal file
@@ -0,0 +1,16 @@
|
||||
query Projects {
|
||||
user(login: "$user") {
|
||||
repository(name: "$repository") {
|
||||
project(number: $id) {
|
||||
name
|
||||
updatedAt
|
||||
progress {
|
||||
doneCount
|
||||
inProgressCount
|
||||
todoCount
|
||||
enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/queries/repositories.graphql
Normal file
46
src/queries/repositories.graphql
Normal file
@@ -0,0 +1,46 @@
|
||||
query Metrics {
|
||||
user(login: "$login") {
|
||||
repositories($after first: $repositories, isFork: false, ownerAffiliations: OWNER, orderBy: {field: UPDATED_AT, direction: DESC}) {
|
||||
edges {
|
||||
cursor
|
||||
}
|
||||
nodes {
|
||||
name
|
||||
watchers {
|
||||
totalCount
|
||||
}
|
||||
stargazers {
|
||||
totalCount
|
||||
}
|
||||
languages(first: 8) {
|
||||
edges {
|
||||
size
|
||||
node {
|
||||
color
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
issues_open: issues(states: OPEN) {
|
||||
totalCount
|
||||
}
|
||||
issues_closed: issues(states: CLOSED) {
|
||||
totalCount
|
||||
}
|
||||
pr_open: pullRequests(states: OPEN) {
|
||||
totalCount
|
||||
}
|
||||
pr_merged: pullRequests(states: MERGED) {
|
||||
totalCount
|
||||
}
|
||||
releases {
|
||||
totalCount
|
||||
}
|
||||
forkCount
|
||||
licenseInfo {
|
||||
spdxId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/queries/repository.graphql
Normal file
43
src/queries/repository.graphql
Normal file
@@ -0,0 +1,43 @@
|
||||
query Metrics {
|
||||
user(login: "$login") {
|
||||
repository(name: "$repo") {
|
||||
name
|
||||
createdAt
|
||||
diskUsage
|
||||
watchers {
|
||||
totalCount
|
||||
}
|
||||
stargazers {
|
||||
totalCount
|
||||
}
|
||||
languages(first: 8) {
|
||||
edges {
|
||||
size
|
||||
node {
|
||||
color
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
issues_open: issues(states: OPEN) {
|
||||
totalCount
|
||||
}
|
||||
issues_closed: issues(states: CLOSED) {
|
||||
totalCount
|
||||
}
|
||||
pr_open: pullRequests(states: OPEN) {
|
||||
totalCount
|
||||
}
|
||||
pr_merged: pullRequests(states: MERGED) {
|
||||
totalCount
|
||||
}
|
||||
releases {
|
||||
totalCount
|
||||
}
|
||||
forkCount
|
||||
licenseInfo {
|
||||
spdxId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,10 @@
|
||||
const logger = log ? console.debug : () => null
|
||||
logger(`metrics/setup > setup`)
|
||||
const templates = "src/templates"
|
||||
const queries = "src/queries"
|
||||
const conf = {
|
||||
templates:{},
|
||||
queries:{},
|
||||
settings:{},
|
||||
statics:path.resolve("src/html"),
|
||||
node_modules:path.resolve("node_modules"),
|
||||
@@ -52,22 +54,21 @@
|
||||
continue
|
||||
logger(`metrics/setup > load template [${name}]`)
|
||||
const files = [
|
||||
`${templates}/${name}/query.graphql`,
|
||||
`${templates}/${name}/image.svg`,
|
||||
`${templates}/${name}/style.css`,
|
||||
`${templates}/${name}/fonts.css`,
|
||||
]
|
||||
const [query, image, style, fonts] = await Promise.all(files.map(async file => `${await fs.promises.readFile(path.resolve(file))}`))
|
||||
conf.templates[name] = {query, image, style, fonts}
|
||||
].map(file => fs.existsSync(path.resolve(file)) ? file : file.replace(`${templates}/${name}/`, `${templates}/classic/`)).map(file => path.resolve(file))
|
||||
const [image, style, fonts] = await Promise.all(files.map(async file => `${await fs.promises.readFile(file)}`))
|
||||
conf.templates[name] = {image, style, fonts}
|
||||
logger(`metrics/setup > load template [${name}] > success`)
|
||||
//Debug
|
||||
if (conf.settings.debug) {
|
||||
Object.defineProperty(conf.templates, name, {
|
||||
get() {
|
||||
logger(`metrics/setup > reload template [${name}]`)
|
||||
const [query, image, style, fonts] = files.map(file => `${fs.readFileSync(path.resolve(file))}`)
|
||||
const [image, style, fonts] = files.map(file => `${fs.readFileSync(file)}`)
|
||||
logger(`metrics/setup > reload template [${name}] > success`)
|
||||
return {query, image, style, fonts}
|
||||
return {image, style, fonts}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -78,6 +79,39 @@
|
||||
conf.templates = JSON.parse(Buffer.from(`<#assets>`, "base64").toString("utf8"))
|
||||
}
|
||||
|
||||
//Load queries
|
||||
if (fs.existsSync(path.resolve(queries))) {
|
||||
for (const query of await fs.promises.readdir(queries)) {
|
||||
//Cache queries
|
||||
const name = query.replace(/[.]graphql$/, "")
|
||||
logger(`metrics/setup > load query [${name}]`)
|
||||
conf.queries[`_${name}`] = `${await fs.promises.readFile(path.resolve(`${queries}/${query}`))}`
|
||||
logger(`metrics/setup > load query [${name}] > success`)
|
||||
//Debug
|
||||
if (conf.settings.debug) {
|
||||
Object.defineProperty(conf.queries, `_${name}`, {
|
||||
get() {
|
||||
logger(`metrics/setup > reload query [${name}]`)
|
||||
const raw = `${fs.readFileSync(path.resolve(`${queries}/${query}`))}`
|
||||
logger(`metrics/setup > reload query [${name}] > success`)
|
||||
return raw
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(`metrics/setup > load queries from build`)
|
||||
conf.queries = JSON.parse(Buffer.from(`<#queries>`, "base64").toString("utf8"))
|
||||
}
|
||||
//Create queries formatters
|
||||
Object.keys(conf.queries).map(name => conf.queries[name.substring(1)] = (vars = {}) => {
|
||||
let query = conf.queries[name]
|
||||
for (const [key, value] of Object.entries(vars))
|
||||
query = query.replace(new RegExp(`[$]${key}`, "g"), value)
|
||||
return query
|
||||
})
|
||||
|
||||
//Conf
|
||||
logger(`metrics/setup > setup > success`)
|
||||
return conf
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
+ (!!base.repositories)*108
|
||||
+ ((!!base.repositories)*((!!plugins.traffic)||(!!plugins.lines)))*16
|
||||
+ (!!plugins.followup)*68
|
||||
+ (!!plugins.pagespeed)*126 + (plugins.pagespeed?.detailed ?? 0)*6*20
|
||||
+ (!!plugins.pagespeed)*126 + (plugins.pagespeed?.detailed ?? 0)*6*20 + (!!plugins.pagespeed?.screenshot)*330
|
||||
+ (!!plugins.habits)*28 + (!!plugins.habits?.facts)*58 + (!!plugins.habits?.charts)*226
|
||||
+ (!!plugins.languages)*96
|
||||
+ (!!plugins.music)*64 + (plugins.music?.tracks?.length ? 14+Math.max(0, plugins.music.tracks.length-1)*36 : 0)
|
||||
@@ -380,11 +380,19 @@
|
||||
<% } else { %>
|
||||
<section>
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg>
|
||||
<%= plugins.gists.stargazers %> Stargazer<%= s(plugins.gists.stargazers) %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4 1.75C4 .784 4.784 0 5.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v8.586A1.75 1.75 0 0114.25 15h-9a.75.75 0 010-1.5h9a.25.25 0 00.25-.25V6h-2.75A1.75 1.75 0 0110 4.25V1.5H5.75a.25.25 0 00-.25.25v2.5a.75.75 0 01-1.5 0v-2.5zm7.5-.188V4.25c0 .138.112.25.25.25h2.688a.252.252 0 00-.011-.013l-2.914-2.914a.272.272 0 00-.013-.011zM5.72 6.72a.75.75 0 000 1.06l1.47 1.47-1.47 1.47a.75.75 0 101.06 1.06l2-2a.75.75 0 000-1.06l-2-2a.75.75 0 00-1.06 0zM3.28 7.78a.75.75 0 00-1.06-1.06l-2 2a.75.75 0 000 1.06l2 2a.75.75 0 001.06-1.06L1.81 9.25l1.47-1.47z"></path></svg>
|
||||
<%= plugins.gists.files %> File<%= s(plugins.gists.files) %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.75 2.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h2a.75.75 0 01.75.75v2.19l2.72-2.72a.75.75 0 01.53-.22h4.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25H2.75zM1 2.75C1 1.784 1.784 1 2.75 1h10.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0113.25 12H9.06l-2.573 2.573A1.457 1.457 0 014 13.543V12H2.75A1.75 1.75 0 011 10.25v-7.5z"></path></svg>
|
||||
<%= plugins.gists.comments %> Comment<%= s(plugins.gists.comments) %>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg>
|
||||
<%= plugins.gists.stargazers %> Stargazer<%= s(plugins.gists.stargazers) %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
|
||||
<%= plugins.gists.forks %> Fork<%= s(plugins.gists.forks) %>
|
||||
@@ -497,6 +505,13 @@
|
||||
</section>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if (plugins.pagespeed.screenshot) { %>
|
||||
<div class="row">
|
||||
<section>
|
||||
<img class="screenshot" src="<%= plugins.pagespeed.screenshot %>" width="452" height="315"/>
|
||||
</section>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
@@ -585,7 +600,7 @@
|
||||
<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="M14.184 1.143a1.75 1.75 0 00-2.502-.57L.912 7.916a1.75 1.75 0 00-.53 2.32l.447.775a1.75 1.75 0 002.275.702l11.745-5.656a1.75 1.75 0 00.757-2.451l-1.422-2.464zm-1.657.669a.25.25 0 01.358.081l1.422 2.464a.25.25 0 01-.108.35l-2.016.97-1.505-2.605 1.85-1.26zM9.436 3.92l1.391 2.41-5.42 2.61-.942-1.63 4.97-3.39zM3.222 8.157l-1.466 1a.25.25 0 00-.075.33l.447.775a.25.25 0 00.325.1l1.598-.769-.83-1.436zm6.253 2.306a.75.75 0 00-.944-.252l-1.809.87a.75.75 0 00-.293.253L4.38 14.326a.75.75 0 101.238.848l1.881-2.75v2.826a.75.75 0 001.5 0v-2.826l1.881 2.75a.75.75 0 001.238-.848l-2.644-3.863z"></path></svg>
|
||||
Starred topics
|
||||
<%= {starred:"Starred topics", mastered:"Mastered technologies and topics"}[plugins.topics.mode] %>
|
||||
</h2>
|
||||
<div class="row">
|
||||
<% if (plugins.topics.error) { %>
|
||||
@@ -596,13 +611,21 @@
|
||||
</div>
|
||||
</section>
|
||||
<% } else { %>
|
||||
<section>
|
||||
<div class="topics fill-width">
|
||||
<% for (const {name, description} of plugins.topics.list) { %>
|
||||
<div class="label" title="<%= description %>"><%= name.toLocaleLowerCase() %></div>
|
||||
<% } %>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="topics fill-width">
|
||||
<% if (plugins.topics.mode === "starred") { %>
|
||||
<% for (const {name, description} of plugins.topics.list) { %>
|
||||
<div class="label" title="<%= description %>"><%= name.toLocaleLowerCase() %></div>
|
||||
<% } %>
|
||||
<% } else if (plugins.topics.mode === "mastered") { %>
|
||||
<% for (const {name, icon} of plugins.topics.list) { %>
|
||||
<% if (icon) { %>
|
||||
<img src="data:image/png;base64,<%= icon %>" width="24" height="24" alt="<%= name %>" title="<%= name %>"/>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
</section>
|
||||
<% } %>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 75 KiB |
@@ -226,6 +226,13 @@
|
||||
fill: #e53935;
|
||||
}
|
||||
|
||||
.screenshot {
|
||||
width: 452px;
|
||||
height: 315px;
|
||||
margin: 8px 14px 4px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* Music plugin */
|
||||
.tracklist {
|
||||
display: flex;
|
||||
@@ -284,6 +291,11 @@
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.topics img {
|
||||
border-radius: 5px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
/* Tweets */
|
||||
.tweet {
|
||||
font-size: 13px;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import common from "./../common.mjs"
|
||||
|
||||
/** Template processor */
|
||||
export default async function ({login, q}, {conf, data, rest, graphql, plugins}, {s, pending, imports}) {
|
||||
export default async function ({login, q}, {conf, data, rest, graphql, plugins, queries}, {s, pending, imports}) {
|
||||
//Common
|
||||
await common(...arguments)
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/** Template common processor */
|
||||
export default async function ({login, q, dflags}, {conf, data, rest, graphql, plugins}, {s, pending, imports}) {
|
||||
export default async function ({login, q, dflags}, {conf, data, rest, graphql, plugins, queries}, {s, pending, imports}) {
|
||||
|
||||
//Init
|
||||
const computed = data.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, releases:0}}
|
||||
const avatar = imports.imgb64(data.user.avatarUrl)
|
||||
data.plugins = {}
|
||||
console.debug(`metrics/compute/${login} > formatting common metrics`)
|
||||
|
||||
//Timezone config
|
||||
@@ -24,7 +23,7 @@
|
||||
pending.push((async () => {
|
||||
try {
|
||||
console.debug(`metrics/compute/${login}/plugins > ${name} > started`)
|
||||
data.plugins[name] = await imports.plugins[name]({login, q, imports, data, computed, rest, graphql}, plugins[name])
|
||||
data.plugins[name] = await imports.plugins[name]({login, q, imports, data, computed, rest, graphql, queries}, plugins[name])
|
||||
console.debug(`metrics/compute/${login}/plugins > ${name} > completed (${data.plugins[name] !== null ? "success" : "skipped"})`)
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
//Imports
|
||||
import classic from "./classic/template.mjs"
|
||||
import repository from "./repository/template.mjs"
|
||||
import terminal from "./terminal/template.mjs"
|
||||
|
||||
//Exports
|
||||
export default {
|
||||
classic,
|
||||
repository,
|
||||
terminal,
|
||||
}
|
||||
220
src/templates/repository/image.svg
Normal file
220
src/templates/repository/image.svg
Normal file
@@ -0,0 +1,220 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="480" height="<%= 0
|
||||
+ (!!base.header)*42
|
||||
+ (!!plugins.traffic)*18
|
||||
+ (!!plugins.followup)*68
|
||||
+ (!!base.metadata)*28
|
||||
+ (!!plugins.projects)*22 + (plugins.projects?.list?.length ?? 0)*60 + (!!plugins.projects?.error)*22
|
||||
%>">
|
||||
|
||||
<defs><style><%= fonts %></style></defs>
|
||||
|
||||
<style>
|
||||
<%= style %>
|
||||
</style>
|
||||
|
||||
<foreignObject x="0" y="0" width="100%" height="100%">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<% if (errors.length) { %>
|
||||
<section>
|
||||
<div class="row">
|
||||
<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>
|
||||
<%= errors.map(({error}) => error.message).join(", ") %>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<% } else { %>
|
||||
|
||||
<% if (base.header) { %>
|
||||
<section>
|
||||
<div class="row">
|
||||
<section>
|
||||
<div class="field <%= computed.cakeday ? 'cakeday' : '' %>">
|
||||
<% if (computed.cakeday) { %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.75 1.5a1.25 1.25 0 100 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707zM2 2.75c0 .45.108.875.3 1.25h-.55A1.75 1.75 0 000 5.75v2c0 .698.409 1.3 1 1.582v4.918c0 .966.784 1.75 1.75 1.75h10.5A1.75 1.75 0 0015 14.25V9.332c.591-.281 1-.884 1-1.582v-2A1.75 1.75 0 0014.25 4h-.55a2.75 2.75 0 00-2.45-4c-.984 0-1.874.42-2.57 1.23A5.086 5.086 0 008 2.274a5.086 5.086 0 00-.68-1.042C6.623.42 5.733 0 4.75 0A2.75 2.75 0 002 2.75zM8.941 4h2.309a1.25 1.25 0 100-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793zm-1.84 1.5H1.75a.25.25 0 00-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-.149zm1.649 0V8h5.5a.25.25 0 00.25-.25v-2a.25.25 0 00-.25-.25h-5.5zm0 4h4.75v4.75a.25.25 0 01-.25.25h-4.5v-5zm-1.5 0v5h-4.5a.25.25 0 01-.25-.25V9.5h4.75z"></path></svg>
|
||||
Created <%= computed.registration %>
|
||||
<% } else { %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zm.5 4.75a.75.75 0 00-1.5 0v3.5a.75.75 0 00.471.696l2.5 1a.75.75 0 00.557-1.392L8.5 7.742V4.75z"></path></svg>
|
||||
Created <%= computed.registration %>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" d="M2.5 3.5c0-.133.058-.318.282-.55.227-.237.592-.484 1.1-.708C4.899 1.795 6.354 1.5 8 1.5c1.647 0 3.102.295 4.117.742.51.224.874.47 1.101.707.224.233.282.418.282.551 0 .133-.058.318-.282.55-.227.237-.592.484-1.1.708C11.101 5.205 9.646 5.5 8 5.5c-1.647 0-3.102-.295-4.117-.742-.51-.224-.874-.47-1.101-.707-.224-.233-.282-.418-.282-.551zM1 3.5c0-.626.292-1.165.7-1.59.406-.422.956-.767 1.579-1.041C4.525.32 6.195 0 8 0c1.805 0 3.475.32 4.722.869.622.274 1.172.62 1.578 1.04.408.426.7.965.7 1.591v9c0 .626-.292 1.165-.7 1.59-.406.422-.956.767-1.579 1.041C11.476 15.68 9.806 16 8 16c-1.805 0-3.475-.32-4.721-.869-.623-.274-1.173-.62-1.579-1.04-.408-.426-.7-.965-.7-1.591v-9zM2.5 8V5.724c.241.15.503.286.779.407C4.525 6.68 6.195 7 8 7c1.805 0 3.475-.32 4.722-.869.275-.121.537-.257.778-.407V8c0 .133-.058.318-.282.55-.227.237-.592.484-1.1.708C11.101 9.705 9.646 10 8 10c-1.647 0-3.102-.295-4.117-.742-.51-.224-.874-.47-1.101-.707C2.558 8.318 2.5 8.133 2.5 8zm0 2.225V12.5c0 .133.058.318.282.55.227.237.592.484 1.1.708 1.016.447 2.471.742 4.118.742 1.647 0 3.102-.295 4.117-.742.51-.224.874-.47 1.101-.707.224-.233.282-.418.282-.551v-2.275c-.241.15-.503.285-.778.406-1.247.549-2.917.869-4.722.869-1.805 0-3.475-.32-4.721-.869a6.236 6.236 0 01-.779-.406z"></path></svg>
|
||||
<%= computed.diskUsage %> used
|
||||
</div>
|
||||
<% if (plugins.traffic) { %>
|
||||
<div class="field <%= plugins.traffic.error ? 'error' : '' %>">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M0 1.75A.75.75 0 01.75 1h4.253c1.227 0 2.317.59 3 1.501A3.744 3.744 0 0111.006 1h4.245a.75.75 0 01.75.75v10.5a.75.75 0 01-.75.75h-4.507a2.25 2.25 0 00-1.591.659l-.622.621a.75.75 0 01-1.06 0l-.622-.621A2.25 2.25 0 005.258 13H.75a.75.75 0 01-.75-.75V1.75zm8.755 3a2.25 2.25 0 012.25-2.25H14.5v9h-3.757c-.71 0-1.4.201-1.992.572l.004-7.322zm-1.504 7.324l.004-5.073-.002-2.253A2.25 2.25 0 005.003 2.5H1.5v9h3.757a3.75 3.75 0 011.994.574z"></path></svg>
|
||||
<% if (plugins.traffic.error) { %>
|
||||
<%= plugins.traffic.error.message %>
|
||||
<% } else { %>
|
||||
<%= plugins.traffic.views.count %> view<%= s(plugins.traffic.views.count) %> in last two weeks
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
</section>
|
||||
<section>
|
||||
<div class="field calendar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 <%= computed.calendar.length*15 %> 11" width="<%= computed.calendar.length*15 %>" height="16">
|
||||
<g>
|
||||
<% for (const [x, {color}] of Object.entries(computed.calendar)) { %>
|
||||
<rect class="day" x="<%= x*15 %>" y="0" width="11" height="11" fill="<%= color %>" rx="2" ry="2" />
|
||||
<% } %>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<% if (plugins.lines) { %>
|
||||
<div class="field <%= plugins.lines.error ? 'error' : '' %>">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h10.5a.25.25 0 00.25-.25V4.664a.25.25 0 00-.073-.177l-2.914-2.914a.25.25 0 00-.177-.073H2.75zM1 1.75C1 .784 1.784 0 2.75 0h7.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0113.25 16H2.75A1.75 1.75 0 011 14.25V1.75zm7 1.5a.75.75 0 01.75.75v1.5h1.5a.75.75 0 010 1.5h-1.5v1.5a.75.75 0 01-1.5 0V7h-1.5a.75.75 0 010-1.5h1.5V4A.75.75 0 018 3.25zm-3 8a.75.75 0 01.75-.75h4.5a.75.75 0 010 1.5h-4.5a.75.75 0 01-.75-.75z"></path></svg>
|
||||
<% if (plugins.lines.error) { %>
|
||||
<%= plugins.lines.error.message %>
|
||||
<% } else { %>
|
||||
<%= plugins.lines.added %> added, <%= plugins.lines.deleted %> removed
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<% } %>
|
||||
|
||||
<% if (plugins.followup) { %>
|
||||
<div class="row">
|
||||
|
||||
<section class="column">
|
||||
<h3>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 %>
|
||||
</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>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 %>
|
||||
</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>
|
||||
<% } %>
|
||||
|
||||
<% if (plugins.projects) { %>
|
||||
<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="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg>
|
||||
Active projects
|
||||
</h2>
|
||||
<div class="row">
|
||||
<% if (plugins.projects.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.projects.error.message %>
|
||||
</div>
|
||||
</section>
|
||||
<% } else { %>
|
||||
<section>
|
||||
<% for (const {name, updated, progress} of plugins.projects.list) { %>
|
||||
<div class="row fill-width">
|
||||
<section>
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0114.25 14H1.75A1.75 1.75 0 010 12.25v-8.5zm1.75-.25a.25.25 0 00-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-8.5a.25.25 0 00-.25-.25H1.75zM3.5 6.25a.75.75 0 01.75-.75h7a.75.75 0 010 1.5h-7a.75.75 0 01-.75-.75zm.75 2.25a.75.75 0 000 1.5h4a.75.75 0 000-1.5h-4z"></path></svg>
|
||||
<%= name %>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="row">
|
||||
<section>
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zm.5 4.75a.75.75 0 00-1.5 0v3.5a.75.75 0 00.471.696l2.5 1a.75.75 0 00.557-1.392L8.5 7.742V4.75z"></path></svg>
|
||||
Updated <%= updated %>
|
||||
</div>
|
||||
</section>
|
||||
<% if (progress.enabled) { %>
|
||||
<section>
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.5 1.75a.25.25 0 01.25-.25h8.5a.25.25 0 01.25.25v7.736a.75.75 0 101.5 0V1.75A1.75 1.75 0 0011.25 0h-8.5A1.75 1.75 0 001 1.75v11.5c0 .966.784 1.75 1.75 1.75h3.17a.75.75 0 000-1.5H2.75a.25.25 0 01-.25-.25V1.75zM4.75 4a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-4.5zM4 7.75A.75.75 0 014.75 7h2a.75.75 0 010 1.5h-2A.75.75 0 014 7.75zm11.774 3.537a.75.75 0 00-1.048-1.074L10.7 14.145 9.281 12.72a.75.75 0 00-1.062 1.058l1.943 1.95a.75.75 0 001.055.008l4.557-4.45z"></path></svg>
|
||||
<%= [progress.done ? `${progress.done} done` : "", progress.doing ? `${progress.doing} doing` : "", progress.todo ? `${progress.todo} todo` : ""].filter(str => str).join(" · ") %>
|
||||
</div>
|
||||
</section>
|
||||
<% } %>
|
||||
</div>
|
||||
<% if (progress.enabled) { %>
|
||||
<div class="field center horizontal-wrap ">
|
||||
<svg class="bar" xmlns="http://www.w3.org/2000/svg" width="460" height="8">
|
||||
<mask id="project-bar">
|
||||
<rect x="0" y="0" width="460" height="8" fill="white" rx="5"/>
|
||||
</mask>
|
||||
<rect mask="url(#project-bar)" x="0" y="0" width="<%= (progress.done/progress.total)*460 %>" height="8" fill="#28A745"/>
|
||||
<rect mask="url(#project-bar)" x="<%= (progress.done/progress.total)*460 %>" y="0" width="<%= (progress.doing/progress.total)*460 %>" height="8" fill="#6F42C1"/>
|
||||
<rect mask="url(#project-bar)" x="<%= ((progress.done+progress.doing)/progress.total)*460 %>" y="0" width="<%= (progress.todo/progress.total)*460 %>" height="8" fill="#d1d5da"/>
|
||||
</svg>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</section>
|
||||
<% } %>
|
||||
</div>
|
||||
</section>
|
||||
<% } %>
|
||||
|
||||
<% } %>
|
||||
|
||||
<% if (base.metadata) { %>
|
||||
<footer>
|
||||
<span>Last updated <%= new Date().toGMTString() %> with lowlighter/metrics@<%= meta.version %></span>
|
||||
</footer>
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
</foreignObject>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 18 KiB |
60
src/templates/repository/template.mjs
Normal file
60
src/templates/repository/template.mjs
Normal file
@@ -0,0 +1,60 @@
|
||||
//Imports
|
||||
import common from "./../common.mjs"
|
||||
|
||||
/** Template processor */
|
||||
export default async function ({login, q}, {conf, data, rest, graphql, plugins, queries}, {s, pending, imports}) {
|
||||
//Check arguments
|
||||
const {repo} = q
|
||||
if (!repo) {
|
||||
console.debug(`metrics/compute/${login}/${repo} > error, repo was undefined`)
|
||||
data.errors.push({error:{message:`You must pass a "repo" argument to use this template`}})
|
||||
return await common(...arguments)
|
||||
}
|
||||
|
||||
//Retrieving single repository
|
||||
console.debug(`metrics/compute/${login}/${repo} > retrieving single repository ${repo}`)
|
||||
const {user:{repository}} = await graphql(queries.repository({login, repo}))
|
||||
data.user.repositories.nodes = [repository]
|
||||
|
||||
//Get commit activity
|
||||
console.debug(`metrics/compute/${login}/${repo} > querying api for commits`)
|
||||
const commits = []
|
||||
for (let page = 0; page < 1; page++) {
|
||||
console.debug(`metrics/compute/${login}/${repo} > loading page ${page}`)
|
||||
const {data} = await rest.repos.listCommits({owner:login, repo, per_page:100, page})
|
||||
if (!data.length) {
|
||||
console.debug(`metrics/compute/${login}/${repo} > no more page to load`)
|
||||
break
|
||||
}
|
||||
commits.push(...data)
|
||||
}
|
||||
console.debug(`metrics/compute/${login}/${repo} > ${commits.length} commits loaded`)
|
||||
|
||||
//Override creation date and disk usage
|
||||
data.user.createdAt = repository.createdAt
|
||||
data.user.repositories.totalDiskUsage = repository.diskUsage
|
||||
|
||||
//Override contributions calendar
|
||||
const days = 14
|
||||
//Compute relative date for each contribution
|
||||
const now = new Date()
|
||||
now.setHours(0, 0, 0, 0)
|
||||
const contributions = commits.map(({commit}) => Math.abs(Math.ceil((now - new Date(commit.committer.date))/(24*60*60*1000)))).slice(0, days)
|
||||
//Count contributions per relative day
|
||||
const calendar = new Array(days).fill(0)
|
||||
for (const day of contributions)
|
||||
calendar[day]++
|
||||
const max = Math.max(...calendar)
|
||||
//Override contributions calendar
|
||||
data.user.calendar.contributionCalendar.weeks = calendar.map(commit => ({contributionDays:{color:commit ? `var(--color-calendar-graph-day-L${Math.ceil(commit/max/0.25)}-bg)` : "var(--color-calendar-graph-day-bg)"}}))
|
||||
|
||||
//Override plugins parameters
|
||||
q["projects.limit"] = 0
|
||||
|
||||
//Common
|
||||
await common(...arguments)
|
||||
await Promise.all(pending)
|
||||
|
||||
//Reformat projects name
|
||||
data.plugins.projects.list.map(project => project.name = project.name.replace(`(${login}/${repo})`, "").trim())
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
query Metrics {
|
||||
user(login: $login) {
|
||||
databaseId
|
||||
name
|
||||
login
|
||||
createdAt
|
||||
avatarUrl
|
||||
websiteUrl
|
||||
isHireable
|
||||
twitterUsername
|
||||
gists {
|
||||
totalCount
|
||||
}
|
||||
repositories(last: $repositories, isFork: false, ownerAffiliations: OWNER) {
|
||||
totalCount
|
||||
totalDiskUsage
|
||||
nodes {
|
||||
name
|
||||
watchers {
|
||||
totalCount
|
||||
}
|
||||
stargazers {
|
||||
totalCount
|
||||
}
|
||||
languages(first: 4) {
|
||||
edges {
|
||||
size
|
||||
node {
|
||||
color
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
issues_open: issues(states: OPEN) {
|
||||
totalCount
|
||||
}
|
||||
issues_closed: issues(states: CLOSED) {
|
||||
totalCount
|
||||
}
|
||||
pr_open: pullRequests(states: OPEN) {
|
||||
totalCount
|
||||
}
|
||||
pr_merged: pullRequests(states: MERGED) {
|
||||
totalCount
|
||||
}
|
||||
releases {
|
||||
totalCount
|
||||
}
|
||||
forkCount
|
||||
licenseInfo {
|
||||
spdxId
|
||||
}
|
||||
}
|
||||
}
|
||||
packages {
|
||||
totalCount
|
||||
}
|
||||
starredRepositories {
|
||||
totalCount
|
||||
}
|
||||
watching {
|
||||
totalCount
|
||||
}
|
||||
sponsorshipsAsSponsor {
|
||||
totalCount
|
||||
}
|
||||
sponsorshipsAsMaintainer {
|
||||
totalCount
|
||||
}
|
||||
contributionsCollection {
|
||||
totalRepositoriesWithContributedCommits
|
||||
totalCommitContributions
|
||||
restrictedContributionsCount
|
||||
totalIssueContributions
|
||||
totalPullRequestContributions
|
||||
totalPullRequestReviewContributions
|
||||
}
|
||||
calendar:contributionsCollection(from: $calendar.from, to: $calendar.to) {
|
||||
contributionCalendar {
|
||||
weeks {
|
||||
contributionDays {
|
||||
color
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repositoriesContributedTo {
|
||||
totalCount
|
||||
}
|
||||
followers {
|
||||
totalCount
|
||||
}
|
||||
following {
|
||||
totalCount
|
||||
}
|
||||
issueComments {
|
||||
totalCount
|
||||
}
|
||||
organizations {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
import common from "./../common.mjs"
|
||||
|
||||
/** Template processor */
|
||||
export default async function ({login, q}, {conf, data, rest, graphql, plugins}, {s, pending, imports}) {
|
||||
export default async function ({login, q}, {conf, data, rest, graphql, plugins, queries}, {s, pending, imports}) {
|
||||
//Common
|
||||
await common(...arguments)
|
||||
//Disable optimization to keep white-spaces
|
||||
|
||||
Reference in New Issue
Block a user