From 0a77a52ae0d935f1eaccd241f25445393dff6be0 Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Fri, 8 Mar 2024 11:24:25 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Create=20vote=20result=20pages=20an?= =?UTF-8?q?d=20svg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 4 +- .github/activities.ts | 279 ++++++++++-------- .github/graphql/query.comment.graphql | 51 ++++ .../{ => graphql}/query.discussions.graphql | 0 .github/{ => graphql}/query.pinned.graphql | 0 .../{ => graphql}/query.pullrequests.graphql | 0 .github/votes.ts | 191 ++++++++++++ .github/workflows/gh-pages.yml | 16 + .github/workflows/push-votes.yml | 40 +-- _config.ts | 16 +- deno.json | 12 +- deno.lock | 82 +++++ site/_includes/foundation.yml | 8 - site/_includes/layouts/vote.vto | 94 ++++++ site/components.md | 12 + site/votes/commonhaus/automation-test/25.json | 122 ++++++++ site/votes/index.page.js | 186 ++++++++++++ 17 files changed, 958 insertions(+), 155 deletions(-) create mode 100644 .github/graphql/query.comment.graphql rename .github/{ => graphql}/query.discussions.graphql (100%) rename .github/{ => graphql}/query.pinned.graphql (100%) rename .github/{ => graphql}/query.pullrequests.graphql (100%) create mode 100644 .github/votes.ts create mode 100644 site/_includes/layouts/vote.vto create mode 100644 site/votes/commonhaus/automation-test/25.json create mode 100644 site/votes/index.page.js diff --git a/.editorconfig b/.editorconfig index 0297c59d..2f834712 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,8 +11,8 @@ indent_style = space indent_size = 2 trim_trailing_whitespace = false -[*.java] +[*.{java,ts,js}] indent_size = 4 -[*.{yml,yaml,html,js,rb,css,xml,scss,vto}] +[*.{yml,yaml,html,rb,css,xml,scss,vto}] indent_size = 2 diff --git a/.github/activities.ts b/.github/activities.ts index 22041eb0..3b0b3bff 100644 --- a/.github/activities.ts +++ b/.github/activities.ts @@ -2,86 +2,124 @@ import { ensureDirSync } from "https://deno.land/std@0.114.0/fs/mod.ts"; import { join } from "https://deno.land/std@0.114.0/path/mod.ts"; import { safeLoad, safeDump } from "https://deno.land/x/js_yaml_port@3.14.0/js-yaml.js"; +interface Problem { + path: string[]; + explanation: string; +} +interface Error { + message: string; + extensions?: { + "value": string; + "problems": Problem[]; + }, + locations?: { + line: number; + column: number; + }[]; +} interface Label { - name: string; + name: string; } interface Author { - login: string; - url: string; - avatarUrl: string; + login: string; + url: string; + avatarUrl: string; } interface ItemWithAuthor { - id: string; - title: string; - number: number; - author: Author; - labels: { - nodes: Label[]; - }; - url: string; - body: string; - createdAt: string; - updatedAt: string; - closed: boolean; - closedAt: string; - locked: boolean; - activeLockReason: string; - category?: { - name: string; - }; - isAnswered?: boolean; - answerChosenAt?: string; + id: string; + title: string; + number: number; + author: Author; + labels: { + nodes: Label[]; + }; + url: string; + body: string; + createdAt: string; + updatedAt: string; + closed: boolean; + closedAt: string; + locked: boolean; + activeLockReason: string; + category?: { + name: string; + }; + isAnswered?: boolean; + answerChosenAt?: string; } interface DiscussionsData { - data: { - repository: { - discussions: { - nodes: ItemWithAuthor[]; - }; + errors?: Error[]; + data: { + repository: { + discussions: { + nodes: ItemWithAuthor[]; + }; + }; }; - }; } interface PullRequestData { - data: { - repository: { - pullRequests: { - nodes: ItemWithAuthor[]; - }; + errors?: Error[]; + data: { + repository: { + pullRequests: { + nodes: ItemWithAuthor[]; + }; + }; }; - }; } interface PinnedItem { - discussion: { - id: string; - }; + discussion: { + id: string; + }; } interface PinnedItemData { - data: { - repository: { - pinnedDiscussions: { - nodes: PinnedItem[]; - }; + errors?: Error[]; + data: { + repository: { + pinnedDiscussions: { + nodes: PinnedItem[]; + }; + }; }; - }; } const prefixMap: Record = { - announcements: '[đŸ“Ŗ ]', - reviews: '[đŸ—ŗī¸ ]', + announcements: '[đŸ“Ŗ ]', + reviews: '[đŸ—ŗī¸ ]', }; -const pins: PinnedItemData = JSON.parse(Deno.readTextFileSync('./site/activity/activity-pinned.json')); -const pinnedIds = pins.data.repository.pinnedDiscussions.nodes - .map((node) => node.discussion.id); +// Get last commit date for a file +function runGraphQL(filePath: string): string { + const command = new Deno.Command('gh', { + args: [ + 'api', 'graphql', + '-F', "owner=commonhaus", + '-F', "name=foundation-draft", + '-F', `query=@${filePath}`, + ] + }); + + const { code, stdout, stderr } = command.outputSync(); + const output = new TextDecoder().decode(stdout).trim(); + console.log(code, filePath, new TextDecoder().decode(stderr)); + console.assert(code === 0); + return output; +} + +const pins: PinnedItemData = JSON.parse(runGraphQL('.github/graphql/query.pinned.graphql')); +if (pins.errors || !pins.data) { + console.error(pins); + Deno.exit(1); +} +const pinnedIds = pins.data.repository.pinnedDiscussions.nodes.map((node) => node.discussion.id); function generateContent(item: ItemWithAuthor, id: string, type: string): string { - const title = item.title.replace(/"/g, '\\"') - const prefix = item.category - ? prefixMap[item.category.name.toLowerCase()] - : ''; - let status = ''; - let data = `--- + const title = item.title.replace(/"/g, '\\"') + const prefix = item.category + ? prefixMap[item.category.name.toLowerCase()] + : ''; + let data = `--- title: "${title}" rss-title: "${prefix}${title}" github: "${item.url}" @@ -93,83 +131,94 @@ url: "/activity/${id}.html" type: ${type} tags: - post`; - if (item.category) { - data += `\n- ${item.category.name.toLowerCase()}`; - } - if (item.closed) { - data += `\nclosedAt: ${item.closedAt}` - } - if (item.locked) { - data += `\nlockReason: "${item.activeLockReason}"` - } - if (item.isAnswered) { - data += `\nansweredAt: ${item.answerChosenAt}`; - } - if (pinnedIds.includes(item.id)) { - data += `\npinned: true`; - } - data += `\n---\n${item.body}\n`; - // console.log(data); - return data; + if (item.category) { + data += `\n- ${item.category.name.toLowerCase()}`; + } + if (item.closed) { + data += `\nclosedAt: ${item.closedAt}` + } + if (item.locked) { + data += `\nlockReason: "${item.activeLockReason}"` + } + if (item.isAnswered) { + data += `\nansweredAt: ${item.answerChosenAt}`; + } + if (pinnedIds.includes(item.id)) { + data += `\npinned: true`; + } + data += `\n---\n${item.body}\n`; + // console.log(data); + return data; } function writeItemsToFiles(items: ItemWithAuthor[], outputDir: string, type: string): void { - ensureDirSync(outputDir); - for (const item of items) { - const id = item.number.toString(); - // const slug = id + slugifyTitle(discussion.title); - const content = generateContent(item, id, type); - const filePath = join(outputDir, `${id}.md`); - Deno.writeTextFileSync(filePath, content); - } + ensureDirSync(outputDir); + for (const item of items) { + const id = item.number.toString(); + const content = generateContent(item, id, type); + const filePath = join(outputDir, `${id}.md`); + Deno.writeTextFileSync(filePath, content); + } } function updateAuthors(items: ItemWithAuthor[]): void { - const authorsPath = './site/_data/authors.yml'; - const authorsYaml = Deno.readTextFileSync(authorsPath); - const authors = safeLoad(authorsYaml) || {}; - for (const item of items) { - const author = item.author; - if (!authors[author.login]) { - authors[author.login] = { - login: author.login, - url: author.url, - avatar: author.avatarUrl, - }; + const authorsPath = './site/_data/authors.yml'; + const authorsYaml = Deno.readTextFileSync(authorsPath); + const authors = safeLoad(authorsYaml) || {}; + for (const item of items) { + const author = item.author; + if (!authors[author.login]) { + authors[author.login] = { + login: author.login, + url: author.url, + avatar: author.avatarUrl, + }; + } } - } - Deno.writeTextFileSync(authorsPath, safeDump(authors)); + Deno.writeTextFileSync(authorsPath, safeDump(authors)); } function updateDiscussions(data: DiscussionsData) { - // Filter discussions by category (Announcements) or label (Notice), - const discussions = data.data.repository.discussions.nodes - .filter((discussion: ItemWithAuthor) => - (discussion.category && prefixMap[discussion.category.name.toLowerCase()]) - || discussion.labels.nodes.some((label: Label) => label.name.toLowerCase() === 'notice') - ); - writeItemsToFiles(discussions, `./site/activity`, "discussion"); - // Update authors.yml (add/update author of last 10 discussions) - updateAuthors(discussions); + // Filter discussions by category (Announcements) or label (Notice), + const discussions = data.data.repository.discussions.nodes + .filter((discussion: ItemWithAuthor) => + (discussion.category && prefixMap[discussion.category.name.toLowerCase()]) + || discussion.labels.nodes.some((label: Label) => label.name.toLowerCase() === 'notice') + ); + + writeItemsToFiles(discussions, `./site/activity`, "discussion"); + + // Update authors.yml (add/update author of last 10 discussions) + updateAuthors(discussions); } function updatePullRequests(data: PullRequestData) { - // Filter discussions by label (Notice), - const prs = data.data.repository.pullRequests.nodes - .filter((pr: ItemWithAuthor) => - pr.labels.nodes.some((label: Label) => label.name.toLowerCase() === 'notice') - ); - prs.forEach((pr: ItemWithAuthor) => pr.category = { name: 'reviews' }); - writeItemsToFiles(prs, `./site/activity`, "pullRequest"); - // Update authors.yml (add/update author of last 10 discussions) - updateAuthors(prs); + // Filter discussions by label (Notice), + const prs = data.data.repository.pullRequests.nodes + .filter((pr: ItemWithAuthor) => + pr.labels.nodes.some((label: Label) => label.name.toLowerCase() === 'notice') + ); + + prs.forEach((pr: ItemWithAuthor) => pr.category = { name: 'reviews' }); + writeItemsToFiles(prs, `./site/activity`, "pullRequest"); + + // Update authors.yml (add/update author of last 10 discussions) + updateAuthors(prs); } // Usage: // First, use a deno task to download the last 10 discussions from GitHub > discussions.json // Parse the discussions.json file -const discuss: DiscussionsData = JSON.parse(Deno.readTextFileSync('./site/activity/activity-discussions.json')); +const discuss: DiscussionsData = JSON.parse(runGraphQL('.github/graphql/query.discussions.graphql')); +if (discuss.errors || !discuss.data) { + console.error(discuss); + Deno.exit(1); +} updateDiscussions(discuss); -const prs: PullRequestData = JSON.parse(Deno.readTextFileSync('./site/activity/activity-prs.json')); +const prs: PullRequestData = JSON.parse(runGraphQL('.github/graphql/query.pullrequests.graphql')); +if (prs.errors || !prs.data) { + console.error(prs); + Deno.exit(1); +} updatePullRequests(prs); diff --git a/.github/graphql/query.comment.graphql b/.github/graphql/query.comment.graphql new file mode 100644 index 00000000..4c491f10 --- /dev/null +++ b/.github/graphql/query.comment.graphql @@ -0,0 +1,51 @@ +query($commentId: ID!) { + node(id: $commentId) { + ... on IssueComment { + issue { + id + closed + closedAt + number + labels(first: 10) { + nodes { + name + } + } + repository { + nameWithOwner + } + title + url + } + body + createdAt + id + updatedAt + url + } + ... on DiscussionComment { + id + discussion { + id + closed + closedAt + number + labels(first: 10) { + nodes { + name + } + } + repository { + nameWithOwner + } + title + url + } + body + createdAt + id + updatedAt + url + } + } +} \ No newline at end of file diff --git a/.github/query.discussions.graphql b/.github/graphql/query.discussions.graphql similarity index 100% rename from .github/query.discussions.graphql rename to .github/graphql/query.discussions.graphql diff --git a/.github/query.pinned.graphql b/.github/graphql/query.pinned.graphql similarity index 100% rename from .github/query.pinned.graphql rename to .github/graphql/query.pinned.graphql diff --git a/.github/query.pullrequests.graphql b/.github/graphql/query.pullrequests.graphql similarity index 100% rename from .github/query.pullrequests.graphql rename to .github/graphql/query.pullrequests.graphql diff --git a/.github/votes.ts b/.github/votes.ts new file mode 100644 index 00000000..a7b6f958 --- /dev/null +++ b/.github/votes.ts @@ -0,0 +1,191 @@ +import { ensureDirSync } from "https://deno.land/std@0.114.0/fs/mod.ts"; + +interface Problem { + path: string[]; + explanation: string; +} +interface Error { + message: string; + extensions?: { + "value": string; + "problems": Problem[]; + }, + locations?: { + line: number; + column: number; + }[]; +} +interface Label { + name: string; +} +interface Item { + id: string; + title: string; + number: number; + closed: boolean; + closedAt: string; + labels: { + nodes: Label[]; + }; + repository: { + nameWithOwner: string; + } + url: string; +} +interface Result { + errors?: Error[]; + data?: { + node: { + discussion?: Item, + issue?: Item, + body: string; + createdAt: string; + id: string; + updatedAt: string; + url: string; + }; + }; +} +interface Voter { + login: string; + url: string; + avatarUrl: string; +} +interface VoteCategory { + reactions: string[]; + team: Voter[]; + teamTotal: number; + total: number; +} +interface DuplicateVote { + user: Voter; + createdAt: string; + reaction: string; +} +interface VoteData { + voteType: string; + hasQuorum: boolean; + group: string; + groupSize: number; + groupVotes: number; + countedVotes: number; + droppedVotes: number; + votingThreshold: string; + categories?: Record; + duplicates: DuplicateVote[] + missingGroupActors: Voter[]; + + // Fields are added (or modified) by this script for rendering + closed?: boolean; + closedAt?: string; + commentId?: string; + date?: string; + github?: string; + ignored?: VoteCategory; + itemId?: string; + itemTitle?: string; + number?: number; + repoName?: string; + sortedCategories?: [string, VoteCategory][]; + tags?: string[]; + type?: string; + updated?: string; + url?: string; +} + +// Get last commit date for a file +function runGraphQL(commentId: string, filePath: string): string { + const command = new Deno.Command('gh', { + args: [ + 'api', 'graphql', + '-F', "owner=commonhaus", + '-F', "name=foundation-draft", + '-F', `commentId=${commentId}`, + '-F', `query=@${filePath}`, + ] + }); + + const { code, stdout, stderr } = command.outputSync(); + const output = new TextDecoder().decode(stdout).trim(); + console.log(code, filePath, new TextDecoder().decode(stderr)); + console.log(output); + console.assert(code === 0); + return output; +} + +const commentId = Deno.args[0]; +console.log(`Check vote data in comment ${commentId}`); + +const result: Result = JSON.parse(runGraphQL(commentId, '.github/graphql/query.comment.graphql')); +console.log(result); + +// If we have errors, we're done. +if (result.errors || !result.data) { + console.error(result); + Deno.exit(1); +} +// If we can't find the parent item, we're done +const comment = result.data.node; +const item = comment.discussion || comment.issue; +if (!item) { + console.error(comment); + Deno.exit(1); +} + +const match = comment.body.match(//); +const voteData: VoteData = JSON.parse(match ? match[1].trim() : ''); + +voteData.commentId = comment.id; +voteData.github = item.url; +voteData.itemId = item.id; +voteData.itemTitle = item.title; +voteData.number = item.number; +voteData.repoName = item.repository.nameWithOwner; +voteData.type = 'vote'; +voteData.date = comment.createdAt; +voteData.updated = comment.updatedAt ? comment.updatedAt : comment.createdAt; +voteData.url = `/votes/${item.repository.nameWithOwner}/${item.number}.html`; + +voteData.tags = item.labels.nodes.map(label => label.name); +voteData.tags.push('vote'); +voteData.closed = item.closed; +if (item.closed) { + voteData.tags.push('closed'); + voteData.closedAt = item.closedAt; +} + +if (voteData.categories) { + const ignored: VoteCategory | undefined = voteData.categories['ignored']; + if (ignored) { + voteData.ignored = ignored; + delete voteData.categories['ignored']; + } + + for (const category of Object.values(voteData.categories)) { + // +1, -1, laugh, confused, heart, hooray, rocket, eyes + // thumbs_up, plus_one, thumbs_down, minus_one + category.reactions = category.reactions.map((r: string) => + r.replace('+1', '👍') + .replace('thumbs_up', '👍') + .replace('plus_one', '👍') + .replace('-1', '👎') + .replace('thumbs_down', '👎') + .replace('minus_one', '👎') + .replace('laugh', '😄') + .replace('confused', '😕') + .replace('heart', '❤ī¸') + .replace('hooray', '🎉') + .replace('rocket', '🚀') + .replace('eyes', '👀') + ); + } + + voteData.sortedCategories = Object.entries(voteData.categories).sort(); +} + +// The relative path for the vote file +const relativeVotePath = `./site/votes/${item.repository.nameWithOwner}/`; +const fileName = `${item.number}.json`; +console.log(`Writing vote data to ${relativeVotePath}${fileName}`); +ensureDirSync(relativeVotePath); +Deno.writeTextFileSync(`${relativeVotePath}${fileName}`, JSON.stringify(voteData, null, 2)); \ No newline at end of file diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 3a0e11b3..f448b7e3 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -62,3 +62,19 @@ jobs: if: github.ref == 'refs/heads/main' && github.repository == ${{ env.REPO }} id: deployment uses: actions/deploy-pages@v4 + + check: + runs-on: ubuntu-latest + needs: publish + if: always() + steps: + - if: ${{ needs.publish.result != 'failure' }} + run: | + echo "Deployment successful" + + - if: ${{ needs.publish.result == 'failure' }} + run: | + echo "Deployment failed" + exit 1 + + diff --git a/.github/workflows/push-votes.yml b/.github/workflows/push-votes.yml index cb5e64be..94d5dc85 100644 --- a/.github/workflows/push-votes.yml +++ b/.github/workflows/push-votes.yml @@ -3,14 +3,10 @@ name: update-vote-results on: workflow_dispatch: inputs: - id: + comment_id: description: "The bot comment id" required: true default: "-1" - type: - description: "The comment type (discussion or issue)" - required: true - default: "discussion" env: GH_BOT_EMAIL: "41898282+github-actions[bot]@users.noreply.github.com" @@ -46,22 +42,30 @@ jobs: git config user.name ${{ env.GH_BOT_NAME }} git config user.email ${{ env.GH_BOT_EMAIL }} + echo ${{ toJson(github.event) }} + - name: Git Vote Update env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_EVENT: ${{ toJson(github.event) }} + COMMENT_ID: ${{ github.event.inputs.comment_id }} run: | echo "Updating vote results..." - echo "${GITHUB_EVENT}" + deno task votes "${COMMENT_ID}" + git add -A -- ./site/votes + if git diff --quiet HEAD; then + echo "-- No changes -- " + else + git commit -am "đŸ—ŗī¸ Auto-update GH Votes" && git push + fi - # publish-gh-pages: - # needs: build - # uses: ./.github/workflows/gh-pages.yml - # concurrency: - # group: "pages" - # cancel-in-progress: true - # permissions: - # contents: read - # pages: write - # id-token: write - # secrets: inherit \ No newline at end of file + publish-gh-pages: + needs: build + uses: ./.github/workflows/gh-pages.yml + concurrency: + group: "pages" + cancel-in-progress: true + permissions: + contents: read + pages: write + id-token: write + secrets: inherit \ No newline at end of file diff --git a/_config.ts b/_config.ts index 27d2d7ea..d7570196 100644 --- a/_config.ts +++ b/_config.ts @@ -129,13 +129,11 @@ site.data("url", (page: Page) => { return page.data.url; }, "/foundation"); -// deno-lint-ignore no-explicit-any -const FOUNDATION_DATA: Record = safeLoad(Deno.readTextFileSync("./site/_includes/foundation.yml")); +const FOUNDATION_DATA: Record = safeLoad(Deno.readTextFileSync("./site/_includes/foundation.yml")); // Ignore pages based on the foundation.yml file site.addEventListener("beforeBuild", () => { - // deno-lint-ignore no-explicit-any - const structure: Record | undefined = FOUNDATION_DATA; + const structure: Record | undefined = FOUNDATION_DATA; if (structure) { ignorePages(structure, "foundation/"); } @@ -255,5 +253,15 @@ site.filter("authorAvatar", (page: Page) => { return `
${login}
\n`; } }); +site.filter("listVoters", (voters: unknown) => { + if (voters && Array.isArray(voters)) { + return voters + .map((voter: { login: string; url: string; }) => + `${voter.login}`) + .join(", "); + } else { + console.log(voters, "is not an array"); + } +}); export default site; diff --git a/deno.json b/deno.json index 07b8aef0..2809dd82 100644 --- a/deno.json +++ b/deno.json @@ -1,19 +1,15 @@ { "tasks": { - "lume": "echo \"import 'lume/cli.ts'\" | deno run --unstable -A -", + "lume": "echo \"import 'lume/cli.ts'\" | deno run -A -", "attach": "echo \"import 'lume/cli.ts'\" | deno run --inspect-wait --unstable -A -", "build": "deno task lume", "serve": "deno task lume -s", "debug": "deno task attach -s", - "q-discuss": "gh api graphql -F owner='commonhaus' -F name='foundation-draft' -F query=@./.github/query.discussions.graphql > ./site/activity/activity-discussions.json", - "q-pins": "gh api graphql -F owner='commonhaus' -F name='foundation-draft' -F query=@./.github/query.pinned.graphql > ./site/activity/activity-pinned.json", - "q-pr": "gh api graphql -F owner='commonhaus' -F name='foundation-draft' -F query=@./.github/query.pullrequests.graphql > ./site/activity/activity-prs.json", - "assemble": "deno run --allow-read=./site/activity,./site/_data/authors.yml --allow-write=./site/activity,./site/_data/authors.yml ./.github/activities.ts", - "activity": "deno task q-pins && deno task q-discuss && deno task q-pr && deno task assemble", - - "lastmod": "deno run --allow-run=\"git\" --allow-read=./site --allow-write=./site/_includes/foundation.yml ./.github/lastmod.ts", + "activity": "deno run --allow-run=gh --allow-read=./.github/graphql,./site/activity,./site/_data/authors.yml --allow-write=./site/activity,./site/_data/authors.yml ./.github/activities.ts", + "lastmod": "deno run --allow-run=git,gh --allow-read=./site --allow-write=./site/_includes/foundation.yml ./.github/lastmod.ts", + "votes": "deno run --allow-run=gh --allow-read=./.github/graphql,./site/votes --allow-write=./site/votes ./.github/votes.ts", "checklinks": "deno run --allow-read --allow-net https://deno.land/x/link_checker/crawl.ts https://www.commonhaus.org", "checklocal": "deno run --allow-read --allow-net https://deno.land/x/link_checker/crawl.ts http://localhost:3000/" diff --git a/deno.lock b/deno.lock index d6fd697b..0bf63057 100644 --- a/deno.lock +++ b/deno.lock @@ -712,6 +712,87 @@ "https://deno.land/std@0.208.0/yaml/schema/mod.ts": "4472e827bab5025e92bc2eb2eeefa70ecbefc64b2799b765c69af84822efef32", "https://deno.land/std@0.208.0/yaml/stringify.ts": "fffc09c65c68d3d63f8159e8cbaa3f489bc20a8e55b4fbb61a8c2e9f914d1d02", "https://deno.land/std@0.208.0/yaml/type.ts": "65553da3da3c029b6589c6e4903f0afbea6768be8fca61580711457151f2b30f", + "https://deno.land/std@0.210.0/assert/assert.ts": "e265ad50a9341f3b40e51dd4cb41ab253d976943ba78a977106950e52e0302ab", + "https://deno.land/std@0.210.0/assert/assertion_error.ts": "26ed1863d905005f00785c89750c001c3522c5417e4f58f95044b8143cfc1593", + "https://deno.land/std@0.210.0/html/entities.ts": "f165f9dfdc639acc393af8718e1e0d0c82739bd4e1ebaaee80da83e2f5b9ea99", + "https://deno.land/std@0.210.0/html/mod.ts": "3f8c71781e32037ab63bd417759d15d31fb9606c6615c85dcabcc515986494ba", + "https://deno.land/std@0.210.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946", + "https://deno.land/std@0.210.0/path/_common/basename.ts": "0d978ff818f339cd3b1d09dc914881f4d15617432ae519c1b8fdc09ff8d3789a", + "https://deno.land/std@0.210.0/path/_common/common.ts": "9e4233b2eeb50f8b2ae10ecc2108f58583aea6fd3e8907827020282dc2b76143", + "https://deno.land/std@0.210.0/path/_common/constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.210.0/path/_common/dirname.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.210.0/path/_common/format.ts": "11aa62e316dfbf22c126917f5e03ea5fe2ee707386555a8f513d27ad5756cf96", + "https://deno.land/std@0.210.0/path/_common/from_file_url.ts": "ef1bf3197d2efbf0297a2bdbf3a61d804b18f2bcce45548ae112313ec5be3c22", + "https://deno.land/std@0.210.0/path/_common/glob_to_reg_exp.ts": "50386887d6041f15741d0013a703ee63ef673983d465d3a0c9c190e95f8da4fe", + "https://deno.land/std@0.210.0/path/_common/normalize.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.210.0/path/_common/normalize_string.ts": "88c472f28ae49525f9fe82de8c8816d93442d46a30d6bb5063b07ff8a89ff589", + "https://deno.land/std@0.210.0/path/_common/relative.ts": "1af19d787a2a84b8c534cc487424fe101f614982ae4851382c978ab2216186b4", + "https://deno.land/std@0.210.0/path/_common/strip_trailing_separators.ts": "7ffc7c287e97bdeeee31b155828686967f222cd73f9e5780bfe7dfb1b58c6c65", + "https://deno.land/std@0.210.0/path/_common/to_file_url.ts": "a8cdd1633bc9175b7eebd3613266d7c0b6ae0fb0cff24120b6092ac31662f9ae", + "https://deno.land/std@0.210.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.210.0/path/_os.ts": "30b0c2875f360c9296dbe6b7f2d528f0f9c741cecad2e97f803f5219e91b40a2", + "https://deno.land/std@0.210.0/path/basename.ts": "04bb5ef3e86bba8a35603b8f3b69537112cdd19ce64b77f2522006da2977a5f3", + "https://deno.land/std@0.210.0/path/common.ts": "f4d061c7d0b95a65c2a1a52439edec393e906b40f1caf4604c389fae7caa80f5", + "https://deno.land/std@0.210.0/path/dirname.ts": "88a0a71c21debafc4da7a4cd44fd32e899462df458fbca152390887d41c40361", + "https://deno.land/std@0.210.0/path/extname.ts": "8c6d6112bce335b4d3d5a07cb0451816d0c2094c147049874fca2db5f707044b", + "https://deno.land/std@0.210.0/path/format.ts": "3457530cc85d1b4bab175f9ae73998b34fd456c830d01883169af0681b8894fb", + "https://deno.land/std@0.210.0/path/from_file_url.ts": "e7fa233ea1dff9641e8d566153a24d95010110185a6f418dd2e32320926043f8", + "https://deno.land/std@0.210.0/path/glob_to_regexp.ts": "74d7448c471e293d03f05ccb968df4365fed6aaa508506b6325a8efdc01d8271", + "https://deno.land/std@0.210.0/path/is_absolute.ts": "67232b41b860571c5b7537f4954c88d86ae2ba45e883ee37d3dec27b74909d13", + "https://deno.land/std@0.210.0/path/is_glob.ts": "567dce5c6656bdedfc6b3ee6c0833e1e4db2b8dff6e62148e94a917f289c06ad", + "https://deno.land/std@0.210.0/path/join.ts": "3ee91038e3eaa966897eddda43d5207d7cae5c2de8a658bdbd722e8f8f29206a", + "https://deno.land/std@0.210.0/path/join_globs.ts": "9b84d5103b63d3dbed4b2cf8b12477b2ad415c7d343f1488505162dc0e5f4db8", + "https://deno.land/std@0.210.0/path/mod.ts": "eff1d7b0617293bd90254d379a7266887dc6fbf5a00e0f450eeb854959379294", + "https://deno.land/std@0.210.0/path/normalize.ts": "aa95be9a92c7bd4f9dc0ba51e942a1973e2b93d266cd74f5ca751c136d520b66", + "https://deno.land/std@0.210.0/path/normalize_glob.ts": "674baa82e1c00b6cb153bbca36e06f8e0337cb8062db6d905ab5de16076ca46b", + "https://deno.land/std@0.210.0/path/parse.ts": "d87ff0deef3fb495bc0d862278ff96da5a06acf0625ca27769fc52ac0d3d6ece", + "https://deno.land/std@0.210.0/path/posix/_util.ts": "ecf49560fedd7dd376c6156cc5565cad97c1abe9824f4417adebc7acc36c93e5", + "https://deno.land/std@0.210.0/path/posix/basename.ts": "a630aeb8fd8e27356b1823b9dedd505e30085015407caa3396332752f6b8406a", + "https://deno.land/std@0.210.0/path/posix/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", + "https://deno.land/std@0.210.0/path/posix/dirname.ts": "f48c9c42cc670803b505478b7ef162c7cfa9d8e751b59d278b2ec59470531472", + "https://deno.land/std@0.210.0/path/posix/extname.ts": "ee7f6571a9c0a37f9218fbf510c440d1685a7c13082c348d701396cc795e0be0", + "https://deno.land/std@0.210.0/path/posix/format.ts": "b94876f77e61bfe1f147d5ccb46a920636cd3cef8be43df330f0052b03875968", + "https://deno.land/std@0.210.0/path/posix/from_file_url.ts": "b97287a83e6407ac27bdf3ab621db3fccbf1c27df0a1b1f20e1e1b5acf38a379", + "https://deno.land/std@0.210.0/path/posix/glob_to_regexp.ts": "6ed00c71fbfe0ccc35977c35444f94e82200b721905a60bd1278b1b768d68b1a", + "https://deno.land/std@0.210.0/path/posix/is_absolute.ts": "159900a3422d11069d48395568217eb7fc105ceda2683d03d9b7c0f0769e01b8", + "https://deno.land/std@0.210.0/path/posix/is_glob.ts": "ec4fbc604b9db8487f7b56ab0e759b24a971ab6a45f7b0b698bc39b8b9f9680f", + "https://deno.land/std@0.210.0/path/posix/join.ts": "0c0d84bdc344876930126640011ec1b888e6facf74153ffad9ef26813aa2a076", + "https://deno.land/std@0.210.0/path/posix/join_globs.ts": "f4838d54b1f60a34a40625a3293f6e583135348be1b2974341ac04743cb26121", + "https://deno.land/std@0.210.0/path/posix/mod.ts": "f1b08a7f64294b7de87fc37190d63b6ce5b02889af9290c9703afe01951360ae", + "https://deno.land/std@0.210.0/path/posix/normalize.ts": "11de90a94ab7148cc46e5a288f7d732aade1d616bc8c862f5560fa18ff987b4b", + "https://deno.land/std@0.210.0/path/posix/normalize_glob.ts": "10a1840c628ebbab679254d5fa1c20e59106102354fb648a1765aed72eb9f3f9", + "https://deno.land/std@0.210.0/path/posix/parse.ts": "199208f373dd93a792e9c585352bfc73a6293411bed6da6d3bc4f4ef90b04c8e", + "https://deno.land/std@0.210.0/path/posix/relative.ts": "e2f230608b0f083e6deaa06e063943e5accb3320c28aef8d87528fbb7fe6504c", + "https://deno.land/std@0.210.0/path/posix/resolve.ts": "51579d83159d5c719518c9ae50812a63959bbcb7561d79acbdb2c3682236e285", + "https://deno.land/std@0.210.0/path/posix/separator.ts": "0b6573b5f3269a3164d8edc9cefc33a02dd51003731c561008c8bb60220ebac1", + "https://deno.land/std@0.210.0/path/posix/to_file_url.ts": "ac5499aa0c6e2c266019cba7d1f7e5a92b8e04983cd72be97f81adad185619a6", + "https://deno.land/std@0.210.0/path/posix/to_namespaced_path.ts": "c9228a0e74fd37e76622cd7b142b8416663a9b87db643302fa0926b5a5c83bdc", + "https://deno.land/std@0.210.0/path/relative.ts": "23d45ede8b7ac464a8299663a43488aad6b561414e7cbbe4790775590db6349c", + "https://deno.land/std@0.210.0/path/resolve.ts": "5b184efc87155a0af9fa305ff68a109e28de9aee81fc3e77cd01380f19daf867", + "https://deno.land/std@0.210.0/path/separator.ts": "1a21ffd408bfaa317bffff604e5a799f78a7a5571590bde6b9cdce7685953d74", + "https://deno.land/std@0.210.0/path/to_file_url.ts": "edaafa089e0bce386e1b2d47afe7c72e379ff93b28a5829a5885e4b6c626d864", + "https://deno.land/std@0.210.0/path/to_namespaced_path.ts": "cf8734848aac3c7527d1689d2adf82132b1618eff3cc523a775068847416b22a", + "https://deno.land/std@0.210.0/path/windows/_util.ts": "f32b9444554c8863b9b4814025c700492a2b57ff2369d015360970a1b1099d54", + "https://deno.land/std@0.210.0/path/windows/basename.ts": "8a9dbf7353d50afbc5b221af36c02a72c2d1b2b5b9f7c65bf6a5a2a0baf88ad3", + "https://deno.land/std@0.210.0/path/windows/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", + "https://deno.land/std@0.210.0/path/windows/dirname.ts": "5c2aa541384bf0bd9aca821275d2a8690e8238fa846198ef5c7515ce31a01a94", + "https://deno.land/std@0.210.0/path/windows/extname.ts": "07f4fa1b40d06a827446b3e3bcc8d619c5546b079b8ed0c77040bbef716c7614", + "https://deno.land/std@0.210.0/path/windows/format.ts": "343019130d78f172a5c49fdc7e64686a7faf41553268961e7b6c92a6d6548edf", + "https://deno.land/std@0.210.0/path/windows/from_file_url.ts": "d53335c12b0725893d768be3ac6bf0112cc5b639d2deb0171b35988493b46199", + "https://deno.land/std@0.210.0/path/windows/glob_to_regexp.ts": "290755e18ec6c1a4f4d711c3390537358e8e3179581e66261a0cf348b1a13395", + "https://deno.land/std@0.210.0/path/windows/is_absolute.ts": "245b56b5f355ede8664bd7f080c910a97e2169972d23075554ae14d73722c53c", + "https://deno.land/std@0.210.0/path/windows/is_glob.ts": "ec4fbc604b9db8487f7b56ab0e759b24a971ab6a45f7b0b698bc39b8b9f9680f", + "https://deno.land/std@0.210.0/path/windows/join.ts": "e6600bf88edeeef4e2276e155b8de1d5dec0435fd526ba2dc4d37986b2882f16", + "https://deno.land/std@0.210.0/path/windows/join_globs.ts": "f4838d54b1f60a34a40625a3293f6e583135348be1b2974341ac04743cb26121", + "https://deno.land/std@0.210.0/path/windows/mod.ts": "d7040f461465c2c21c1c68fc988ef0bdddd499912138cde3abf6ad60c7fb3814", + "https://deno.land/std@0.210.0/path/windows/normalize.ts": "9deebbf40c81ef540b7b945d4ccd7a6a2c5a5992f791e6d3377043031e164e69", + "https://deno.land/std@0.210.0/path/windows/normalize_glob.ts": "344ff5ed45430495b9a3d695567291e50e00b1b3b04ea56712a2acf07ab5c128", + "https://deno.land/std@0.210.0/path/windows/parse.ts": "120faf778fe1f22056f33ded069b68e12447668fcfa19540c0129561428d3ae5", + "https://deno.land/std@0.210.0/path/windows/relative.ts": "026855cd2c36c8f28f1df3c6fbd8f2449a2aa21f48797a74700c5d872b86d649", + "https://deno.land/std@0.210.0/path/windows/resolve.ts": "5ff441ab18a2346abadf778121128ee71bda4d0898513d4639a6ca04edca366b", + "https://deno.land/std@0.210.0/path/windows/separator.ts": "ae21f27015f10510ed1ac4a0ba9c4c9c967cbdd9d9e776a3e4967553c397bd5d", + "https://deno.land/std@0.210.0/path/windows/to_file_url.ts": "8e9ea9e1ff364aa06fa72999204229952d0a279dbb876b7b838b2b2fea55cce3", + "https://deno.land/std@0.210.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d", "https://deno.land/x/cliffy@v0.25.7/_utils/distance.ts": "02af166952c7c358ac83beae397aa2fbca4ad630aecfcd38d92edb1ea429f004", "https://deno.land/x/cliffy@v0.25.7/ansi/ansi.ts": "7f43d07d31dd7c24b721bb434c39cbb5132029fa4be3dd8938873065f65e5810", "https://deno.land/x/cliffy@v0.25.7/ansi/ansi_escapes.ts": "885f61f343223f27b8ec69cc138a54bea30542924eacd0f290cd84edcf691387", @@ -920,6 +1001,7 @@ "https://deno.land/x/lume_markdown_plugins@v0.7.0/toc/anchors.ts": "8a4a1c6b2c63156622695ceba57fa7100a6e5f109c9a383a1dcaf755233c8184", "https://deno.land/x/lume_markdown_plugins@v0.7.0/toc/mod.ts": "8c7aa6e1dcfabda4264503495a3875388108cd9a5a94b54853b45a8e8cba9f78", "https://deno.land/x/lume_markdown_plugins@v0.7.0/utils.ts": "6e6c3c394709eff39080562732c2dafe404f225253aaded937133ea694c4b735", + "https://deno.land/x/vento@v0.10.0/deps.ts": "009b5a40b5a54e669468a27d18167f443516cdbe430d660c8dca2a86ad065530", "https://deno.land/x/vento@v0.9.1/deps.ts": "1fb4acd6b3b6261c308280b09e8feadac7b0fac1e01bd0e0f1e75527a2181a52", "https://deno.land/x/vento@v0.9.1/mod.ts": "ab690911075158ee3762bd7aefe209cdcb6ead7b492f14b0693d7025244db8e6", "https://deno.land/x/vento@v0.9.1/plugins/echo.ts": "f7c064fb6d34b29852f46f6e01583ed87656dcbbc5cae51c8f29198d6951d0cf", diff --git a/site/_includes/foundation.yml b/site/_includes/foundation.yml index 8cb119a2..13edc61c 100644 --- a/site/_includes/foundation.yml +++ b/site/_includes/foundation.yml @@ -1,13 +1,5 @@ -BOOTSTRAPPING: - ignore: true - date: 2024-02-02T15:07:59.000Z - layout: layouts/bylaws.vto agreements: bootstrapping: - BOOTSTRAPPING: - ignore: true - date: 2024-02-22T18:23:49.000Z - layout: layouts/bylaws.vto bootstrapping: url: /bylaws/bootstrapping.html description: > diff --git a/site/_includes/layouts/vote.vto b/site/_includes/layouts/vote.vto new file mode 100644 index 00000000..2e881bad --- /dev/null +++ b/site/_includes/layouts/vote.vto @@ -0,0 +1,94 @@ +--- +layout: layouts/base.vto +--- +
+
+
+

Voting results for +{{ page.data.voteItem }} {{ page.data.itemTitle }}

+
+
+ +
    +
  • Method: {{ page.data.voteType }}
  • +
  • Group: {{ page.data.group }} ({{ page.data.groupSize }} members)
  • +
  • Response threshold: {{ page.data.votingThreshold }}1
  • +
+ + + + + + + + + + + + + + + + + + + {{- for k of page.data.sortedCategories }} + + + + + + + {{- /for }} + + + + + + + + + +
Vote Results
Response Group2Total VotesTeam VotesVoting members
{{ k[0] }} ({{ k[1].reactions.join(", ") }}){{ k[1].total }}{{ k[1].teamTotal }}{{ k[1].team |> listVoters }}
Total{{ page.data.countedVotes }}{{ page.data.groupVotes }}
+{{ if page.data.ignored }} +
+

The following reactions were not counted: {{ page.data.ignored.reactions.join(", ") }}

+
+{{ /if -}} +{{ if page.data.duplicates }} +
+

The following votes were not counted (multiple votes, using most recent):

+
+ {{- for d of page.data.duplicates }} + {{ d.user.login }}:{{ d.reaction }} + {{- /for }} +
+
+{{- /if }} + +
+
+
    +
  1. The response threshold is the minimum number of votes required to consider the vote valid without a meeting. +If the total number of votes is less than the response threshold, a meeting will be held, and normal quorum rules will apply. +↩ī¸Ž

    +
  2. +
  3. For votes using Martha's rules, this will be "approve", "ok", and "revise". +For manual, reaction-based votes, reactions will be appear individually. For manual votes based on comment responses, there will be one "comment" group. +↩ī¸Ž

    +
  4. +
+
+
+ +
+ {{- if page.data.closed }} +
closed on
+ {{- else if page.data.updated }} +
updated
+ {{- /if }} +
posted
+
+
+
+ diff --git a/site/components.md b/site/components.md index 8f6abbc8..f08fdd91 100644 --- a/site/components.md +++ b/site/components.md @@ -95,3 +95,15 @@ Lorem **ipsum** dolor *sit* amet, ***consectetur*** adipiscing elit, sed do eius > [!CAUTION] > Negative potential consequences of an action. + +--- + +## Vote status badges + +![](/votes/vote-closed.svg) + +![](/votes/vote-progress.svg) + +![](/votes/vote-quorum.svg) + +![](/votes/vote-unknown.svg) \ No newline at end of file diff --git a/site/votes/commonhaus/automation-test/25.json b/site/votes/commonhaus/automation-test/25.json new file mode 100644 index 00000000..41a8ac85 --- /dev/null +++ b/site/votes/commonhaus/automation-test/25.json @@ -0,0 +1,122 @@ +{ + "voteType": "marthas", + "hasQuorum": true, + "group": "commonhaus/test-quorum-default", + "groupSize": 2, + "groupVotes": 2, + "countedVotes": 2, + "droppedVotes": 2, + "votingThreshold": "all", + "categories": { + "revise": { + "reactions": [ + "👎" + ], + "team": [ + { + "login": "commonhaus-bot", + "url": "https://github.com/commonhaus-bot", + "avatarUrl": "https://avatars.githubusercontent.com/u/156364140?v=4" + } + ], + "teamTotal": 1, + "total": 1 + }, + "approve": { + "reactions": [ + "👍" + ], + "team": [ + { + "login": "ebullient", + "url": "https://github.com/ebullient", + "avatarUrl": "https://avatars.githubusercontent.com/u/808713?u=8c50c6169b230fc62777f6910f6669b753ae95a8&v=4" + } + ], + "teamTotal": 1, + "total": 1 + } + }, + "duplicates": [ + { + "user": { + "id": "U_kgDOCVHtbA", + "webhook_id": null, + "login": "commonhaus-bot", + "url": "https://github.com/commonhaus-bot", + "avatarUrl": "https://avatars.githubusercontent.com/u/156364140?v=4", + "webhookData": false + }, + "createdAt": "2024-02-27T03:24:46.000+00:00", + "reaction": "👍" + } + ], + "missingGroupActors": [], + "commentId": "DC_kwDOLDuJqs4Agzlr", + "github": "https://github.com/commonhaus/automation-test/discussions/25", + "itemId": "D_kwDOLDuJqs4AXteZ", + "itemTitle": "Amd more things", + "number": 25, + "repoName": "commonhaus/automation-test", + "type": "vote", + "date": "2024-02-27T02:53:29Z", + "updated": "2024-03-08T13:43:12Z", + "url": "/votes/commonhaus/automation-test/25.html", + "tags": [ + "notice", + "vote/open", + "vote/quorum", + "vote" + ], + "closed": false, + "ignored": { + "reactions": [ + "heart" + ], + "team": [ + { + "login": "ebullient", + "url": "https://github.com/ebullient", + "avatarUrl": "https://avatars.githubusercontent.com/u/808713?u=8c50c6169b230fc62777f6910f6669b753ae95a8&v=4" + } + ], + "teamTotal": 1, + "total": 1 + }, + "sortedCategories": [ + [ + "approve", + { + "reactions": [ + "👍" + ], + "team": [ + { + "login": "ebullient", + "url": "https://github.com/ebullient", + "avatarUrl": "https://avatars.githubusercontent.com/u/808713?u=8c50c6169b230fc62777f6910f6669b753ae95a8&v=4" + } + ], + "teamTotal": 1, + "total": 1 + } + ], + [ + "revise", + { + "reactions": [ + "👎" + ], + "team": [ + { + "login": "commonhaus-bot", + "url": "https://github.com/commonhaus-bot", + "avatarUrl": "https://avatars.githubusercontent.com/u/156364140?v=4" + } + ], + "teamTotal": 1, + "total": 1 + } + ] + ] +} \ No newline at end of file diff --git a/site/votes/index.page.js b/site/votes/index.page.js new file mode 100644 index 00000000..1416a3a5 --- /dev/null +++ b/site/votes/index.page.js @@ -0,0 +1,186 @@ +import { path } from "https://deno.land/x/vento@v0.10.0/deps.ts"; + +function createSVG({ width, height, color, status, progress }) { + const voteWidth = 40; + const voteTextX = voteWidth / 2; + const statusWidth = width - voteWidth; + const statusTextX = voteWidth + 4; + const progressBarWidth = statusWidth * progress; // Width of the progress bar + return ` + + + + + + + + + + + + + + + + + + + vote + vote + ${status} + ${status} + + + `; +} + +const unknownSvg = createSVG({ + width: 120, + height: 20, + color: "#F4F4F4", + status: 'unknown', + progress: 1 +}); + +const closedSvg = createSVG({ + width: 120, + height: 20, + color: "#CCD1FF", + status: 'quorum', + progress: 1 +}); + +const quorumSvg = createSVG({ + width: 120, + height: 20, + color: "#CCFFE0", + status: 'quorum', + progress: 1 +}); + +function createIndex(pages, dir, uri) { + const files = Deno.readDirSync(dir); + for (const file of files) { + if (file.isDirectory) { + createIndex(pages, path.join(dir, file.name), path.join(uri, file.name)); + } else if (file.name.endsWith('.json')) { + const data = JSON.parse(Deno.readTextFileSync(path.join(dir, file.name))); + data.content = JSON.stringify(data, null, 2); + data.date = new Date(data.date); + data.title = `Vote results for ${data.repoName}#${data.number}`; + data.voteItem = `${data.repoName}#${data.number}`; + data.updated = new Date(data.updated); + data.url = `${uri}/${file.name.replace(/\.json$/, '.html')}`; + data.cssclasses = ['vote-result', data.voteType]; + pages.push(data); + } + } +} + +export default function* ({ page }) { + const genPages = []; + const dir = path.dirname(path.fromFileUrl(import.meta.url)); + + // recurse to find/generate pages for individual vote results + createIndex(genPages, dir, "/votes"); + + const index = []; + for (const gp of genPages) { + index.push(`
  • ${gp.voteItem}${gp.itemTitle}
  • `); + } + + const general = { + templateEngine: ['vto', 'md'], + layout: 'layouts/vote.vto', + description: "vote result", + metas: { + robots: false, + description: "vote result" + } + }; + + for (const gp of genPages) { + const newPage = { ...general, ...gp }; + yield newPage; + + // required votes based on supermajority, majority, or all + // round up: whole human + const requiredVotes = gp.votingThreshold == 'supermajority' + ? Math.ceil(gp.groupSize * 2 / 3) + : gp.votingThreshold == 'majority' + ? Math.ceil(gp.groupSize / 2) + : gp.groupSize; + + // gp.hasQuorum = false; + // gp.groupVotes = 1; + + let svgContent = unknownSvg; + if (gp.hasQuorum) { + svgContent = quorumSvg; + } else { + svgContent = createSVG({ + width: 120, + height: 20, + color: "#FFFACD", + status: 'in progress', + progress: gp.groupVotes / requiredVotes + }); + } + + const svg = { + url: newPage.url.replace(/\.html$/, '.svg'), + content: svgContent + } + yield svg; + } + + yield { + url: '/votes/vote-unknown.svg', + content: unknownSvg + } + + yield { + url: '/votes/vote-quorum.svg', + content: quorumSvg + } + + yield { + url: '/votes/vote-closed.svg', + content: closedSvg + } + + yield { + url: '/votes/vote-progress.svg', + content: createSVG({ + width: 120, + height: 20, + color: "#FFFACD", + status: 'in progress', + progress: .6 + }) + } + + yield { + layout: 'layouts/index.vto', + title: "Index of Vote results", + description: "Vote results", + url: "/votes/index.html", + cssclasses: ['vote-index'], + content: `
      ${index.join('')}
    ` + } +} \ No newline at end of file