From 9200dd75fb252556a77c477de639207030b46730 Mon Sep 17 00:00:00 2001 From: Oscar Date: Thu, 17 Oct 2024 10:38:09 +0200 Subject: [PATCH 1/5] Add support for publishing new custom votings --- README.md | 14 ++-- models/custom-vote.js | 63 ++++++++++++++++ models/{vote.js => standard-vote.js} | 10 ++- models/versioned-notulen.js | 2 - support/besluit-exporter.js | 8 +- support/extract-utils.js | 10 ++- support/prefixes.js | 3 +- support/templates/partials/treatment.hbs | 93 +++++++++++++----------- 8 files changed, 141 insertions(+), 62 deletions(-) create mode 100644 models/custom-vote.js rename models/{vote.js => standard-vote.js} (95%) diff --git a/README.md b/README.md index 8b9aa33..da9247f 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,15 @@ Once a specific document is generated for signing or publication the content is { "data": { "type": "extract-preview", - }, - "relationships": { - "treatment": { - "data": { - "id": "{{treatmentUuid}}", - "type": "behandeling-van-agendapunt" + "relationships": { + "treatment": { + "data": { + "id": "{{treatmentUuid}}", + "type": "behandeling-van-agendapunt" + } } } - } + } } ``` ## creating a signed document diff --git a/models/custom-vote.js b/models/custom-vote.js new file mode 100644 index 0000000..fda2aae --- /dev/null +++ b/models/custom-vote.js @@ -0,0 +1,63 @@ +import { prefixMap } from '../support/prefixes'; +// @ts-ignore +import { sparqlEscapeUri, query } from 'mu'; +import { whoVotesBasedOnClassifcationMap } from '../support/classification-utils'; +import { sortMandatees } from '../support/query-utils'; + +export default class CustomVote { + static async findAll({ treatmentUri }) { + try { + const result = await query(` + ${prefixMap.get('besluit').toSparqlString()} + ${prefixMap.get('schema').toSparqlString()} + ${prefixMap.get('ext').toSparqlString()} + ${prefixMap.get('pav').toSparqlString()} + ${prefixMap.get('gn').toSparqlString()} + SELECT DISTINCT * WHERE { + ${sparqlEscapeUri(treatmentUri)} a besluit:BehandelingVanAgendapunt; + besluit:heeftStemming ?uri. + ?uri a gn:AangepasteStemming; + schema:position ?position; + ext:votingDocument ?documentContainer. + ?documentContainer pav:hasCurrentVersion ?editorDocument . + ?editorDocument ext:editorDocumentContent ?content; + ext:editorDocumentContext ?context. + }ORDER BY ASC(?position) + `); + return result.results.bindings.map((binding) => + CustomVote.fromBinding(binding) + ); + } catch (e) { + console.log(e); + throw `failed to fetch custom votes for treatment ${treatmentUri}`; + } + } + + static fromBinding({ + uri, + position, + content, + context + }) { + return new CustomVote({ + uri: uri.value, + position: position.value, + content: content.value, + context: context.value, + }); + } + + constructor({ + uri, + position, + content, + context + }) { + this.type = 'customVote' + this.isCustomVote = true; + this.uri = uri; + this.position = position; + this.content = content; + this.context = context; + } +} diff --git a/models/vote.js b/models/standard-vote.js similarity index 95% rename from models/vote.js rename to models/standard-vote.js index 445fd0c..3dede05 100644 --- a/models/vote.js +++ b/models/standard-vote.js @@ -4,7 +4,7 @@ import { sparqlEscapeUri, query } from 'mu'; import { whoVotesBasedOnClassifcationMap } from '../support/classification-utils'; import { sortMandatees } from '../support/query-utils'; -export default class Vote { +export default class StandardVote { static async findAll({ treatmentUri }) { try { const result = await query(` @@ -31,11 +31,11 @@ export default class Vote { } ORDER BY ASC(?position) `); return result.results.bindings.map((binding) => - Vote.fromBinding(binding) + StandardVote.fromBinding(binding) ); } catch (e) { console.log(e); - throw `failed to fetch votes for treatment ${treatmentUri}`; + throw `failed to fetch standard votes for treatment ${treatmentUri}`; } } @@ -50,7 +50,7 @@ export default class Vote { position, adminBodyClassification, }) { - return new Vote({ + return new StandardVote({ uri: uri.value, subject: subject.value, result: result.value, @@ -79,6 +79,8 @@ export default class Vote { position, adminBodyClassification, }) { + this.type = 'standardVote' + this.isStandardVote = true; this.uri = uri; this.subject = subject; this.result = result; diff --git a/models/versioned-notulen.js b/models/versioned-notulen.js index ec504ef..155a732 100644 --- a/models/versioned-notulen.js +++ b/models/versioned-notulen.js @@ -30,8 +30,6 @@ export default class VersionedNotulen { const bindings = r.results.bindings; if (bindings.length > 0) { const binding = bindings[0]; - - console.log(binding); const fileUri = bindings.fileUri?.value; let html; if (fileUri) { diff --git a/support/besluit-exporter.js b/support/besluit-exporter.js index 3d17a77..84aa71e 100644 --- a/support/besluit-exporter.js +++ b/support/besluit-exporter.js @@ -9,7 +9,8 @@ import { prefixes } from './prefixes'; import Meeting from '../models/meeting'; import Treatment from '../models/treatment'; import Decision from '../models/decision'; -import Vote from '../models/vote'; +import StandardVote from '../models/standard-vote'; +import CustomVote from '../models/custom-vote'; export async function buildBesluitenLijstForMeetingId(meetingUuid) { const meeting = await Meeting.find(meetingUuid); @@ -40,7 +41,10 @@ async function addDecisionsToTreatment(treatment) { } async function addVotesToTreatment(treatment) { - const votes = await Vote.findAll({ treatmentUri: treatment.uri }); + const standardVotes = await StandardVote.findAll({ treatmentUri: treatment.uri }); + const customVotes = await CustomVote.findAll({ treatmentUri: treatment.uri }); + const votes = [...standardVotes, ...customVotes]; + votes.sort((a, b) => a.position - b.position); if (votes.length > 0) { // this makes it easier to check if there are votes in the template treatment.votes = votes; diff --git a/support/extract-utils.js b/support/extract-utils.js index 4a86612..3038080 100644 --- a/support/extract-utils.js +++ b/support/extract-utils.js @@ -1,7 +1,8 @@ import Treatment from '../models/treatment'; import AgendaPoint from '../models/agendapoint'; import Meeting from '../models/meeting'; -import Vote from '../models/vote'; +import StandardVote from '../models/standard-vote'; +import CustomVote from '../models/custom-vote'; import Decision from '../models/decision'; // @ts-ignore import { query, sparqlEscapeUri, update } from 'mu'; @@ -108,10 +109,13 @@ export async function buildExtractDataForTreatment( participantCache = buildParticipantCache(participationList); } } - const votes = await Vote.findAll({ treatmentUri: treatment.uri }); + const standardVotes = await StandardVote.findAll({ treatmentUri: treatment.uri }); + const customVotes = await CustomVote.findAll({ treatmentUri: treatment.uri }); + const votes = [...standardVotes, ...customVotes]; + votes.sort((a, b) => a.position -b.position); if (participationList && participationList.present.length > 0) { // only try fetching voters if people were present - await Promise.all(votes.map((vote) => vote.fetchVoters(participantCache))); + await Promise.all(votes.map((vote) => vote.type === 'customVote' ? vote : vote.fetchVoters(participantCache))); } let content; if (isPublic) { diff --git a/support/prefixes.js b/support/prefixes.js index 8d1f4e4..ec6617c 100644 --- a/support/prefixes.js +++ b/support/prefixes.js @@ -68,7 +68,8 @@ const prefixMap = new Map([ ["notulen", new Prefix("notulen", "http://lblod.data.gift/vocabularies/notulen/")], ["nfo", new Prefix("nfo", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#")], ["nie", new Prefix("nie", "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#")], - ["dbpedia", new Prefix("dbpedia", "http://dbpedia.org/ontology/")] + ["dbpedia", new Prefix("dbpedia", "http://dbpedia.org/ontology/")], + ["gn", new Prefix("gn", "http://data.lblod.info/vocabularies/gelinktnotuleren/")] ]); export { prefixes, prefixMap }; diff --git a/support/templates/partials/treatment.hbs b/support/templates/partials/treatment.hbs index 2b9a6ba..daec217 100644 --- a/support/templates/partials/treatment.hbs +++ b/support/templates/partials/treatment.hbs @@ -45,50 +45,57 @@

Stemmingen bij agendapunt

{{#each this.votes}} -
-

- {{#if this.isSecret}} - {{this.whoVotesPhrase}} geheim - {{else}} - {{this.whoVotesPhrase}} openbaar - {{/if}} -

-

{{this.subject}}

- {{> mandateeList mandatees=this.attendees property="besluit:heeftAanwezige" title="Aanwezigen tijdens de stemming" includeRole=false}} - {{> mandateeList mandatees=this.voters property="besluit:heeftStemmer" title="Effectieve stemmers" includeRole=false}} - {{#if this.isUnanimous}} - - - - {{#unless this.geheim}} - {{> mandateeList mandatees=this.positiveVoters property="besluit:heeftVoorstander" title="Voorstanders" includeRole=false hideText=true}} - {{> mandateeList mandatees=this.negativeVoters property="besluit:heeftTegenstander" title="Tegenstanders" includeRole=false hideText=true}} - {{> mandateeList mandatees=this.abstentionVoters property="besluit:heeftOnthouders" title="Onthoudingen, blanco of ongeldig" includeRole=false hideText=true}} - {{/unless}} -

{{this.result}}, met eenparigheid van stemmen

- {{else}} -

- Totaal aantal voorstanders: - {{this.positiveVotes}} -

-

- Totaal aantal tegenstanders: - {{this.negativeVotes}} -

-

- Totaal aantal onthoudingen, blanco of ongeldig: - {{this.abstentionVotes}} -

- {{#unless this.geheim}} - {{> mandateeList mandatees=this.positiveVoters property="besluit:heeftVoorstander" title="Voorstanders" includeRole=false}} - {{> mandateeList mandatees=this.negativeVoters property="besluit:heeftTegenstander" title="Tegenstanders" includeRole=false}} - {{> mandateeList mandatees=this.abstentionVoters property="besluit:heeftOnthouders" title="Onthoudingen, blanco of ongeldig" includeRole=false}} - {{/unless}} -

Met als gevolg,

-

{{this.result}}

+ {{#if this.isStandardVote}} +
+

+ {{#if this.isSecret}} + {{this.whoVotesPhrase}} geheim + {{else}} + {{this.whoVotesPhrase}} openbaar + {{/if}} +

+

{{this.subject}}

+ {{> mandateeList mandatees=this.attendees property="besluit:heeftAanwezige" title="Aanwezigen tijdens de stemming" includeRole=false}} + {{> mandateeList mandatees=this.voters property="besluit:heeftStemmer" title="Effectieve stemmers" includeRole=false}} + {{#if this.isUnanimous}} + + + + {{#unless this.geheim}} + {{> mandateeList mandatees=this.positiveVoters property="besluit:heeftVoorstander" title="Voorstanders" includeRole=false hideText=true}} + {{> mandateeList mandatees=this.negativeVoters property="besluit:heeftTegenstander" title="Tegenstanders" includeRole=false hideText=true}} + {{> mandateeList mandatees=this.abstentionVoters property="besluit:heeftOnthouders" title="Onthoudingen, blanco of ongeldig" includeRole=false hideText=true}} + {{/unless}} +

{{this.result}}, met eenparigheid van stemmen

+ {{else}} +

+ Totaal aantal voorstanders: + {{this.positiveVotes}} +

+

+ Totaal aantal tegenstanders: + {{this.negativeVotes}} +

+

+ Totaal aantal onthoudingen, blanco of ongeldig: + {{this.abstentionVotes}} +

+ {{#unless this.geheim}} + {{> mandateeList mandatees=this.positiveVoters property="besluit:heeftVoorstander" title="Voorstanders" includeRole=false}} + {{> mandateeList mandatees=this.negativeVoters property="besluit:heeftTegenstander" title="Tegenstanders" includeRole=false}} + {{> mandateeList mandatees=this.abstentionVoters property="besluit:heeftOnthouders" title="Onthoudingen, blanco of ongeldig" includeRole=false}} + {{/unless}} +

Met als gevolg,

+

{{this.result}}

+ {{/if}} + +
+ {{/if}} + {{#if this.isCustomVote}} +
+ {{{this.content}}} +
{{/if}} - -
{{/each}}
{{/if}} From fc14ff439f36cc5212481334b837fd3951bbb16f Mon Sep 17 00:00:00 2001 From: Oscar Date: Thu, 17 Oct 2024 10:48:08 +0200 Subject: [PATCH 2/5] linting --- models/custom-vote.js | 18 +++--------------- models/standard-vote.js | 2 +- support/besluit-exporter.js | 4 +++- support/extract-utils.js | 12 +++++++++--- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/models/custom-vote.js b/models/custom-vote.js index fda2aae..495ace4 100644 --- a/models/custom-vote.js +++ b/models/custom-vote.js @@ -1,8 +1,6 @@ import { prefixMap } from '../support/prefixes'; // @ts-ignore import { sparqlEscapeUri, query } from 'mu'; -import { whoVotesBasedOnClassifcationMap } from '../support/classification-utils'; -import { sortMandatees } from '../support/query-utils'; export default class CustomVote { static async findAll({ treatmentUri }) { @@ -33,12 +31,7 @@ export default class CustomVote { } } - static fromBinding({ - uri, - position, - content, - context - }) { + static fromBinding({ uri, position, content, context }) { return new CustomVote({ uri: uri.value, position: position.value, @@ -47,13 +40,8 @@ export default class CustomVote { }); } - constructor({ - uri, - position, - content, - context - }) { - this.type = 'customVote' + constructor({ uri, position, content, context }) { + this.type = 'customVote'; this.isCustomVote = true; this.uri = uri; this.position = position; diff --git a/models/standard-vote.js b/models/standard-vote.js index 3dede05..a614a76 100644 --- a/models/standard-vote.js +++ b/models/standard-vote.js @@ -79,7 +79,7 @@ export default class StandardVote { position, adminBodyClassification, }) { - this.type = 'standardVote' + this.type = 'standardVote'; this.isStandardVote = true; this.uri = uri; this.subject = subject; diff --git a/support/besluit-exporter.js b/support/besluit-exporter.js index 84aa71e..d979f59 100644 --- a/support/besluit-exporter.js +++ b/support/besluit-exporter.js @@ -41,7 +41,9 @@ async function addDecisionsToTreatment(treatment) { } async function addVotesToTreatment(treatment) { - const standardVotes = await StandardVote.findAll({ treatmentUri: treatment.uri }); + const standardVotes = await StandardVote.findAll({ + treatmentUri: treatment.uri + }); const customVotes = await CustomVote.findAll({ treatmentUri: treatment.uri }); const votes = [...standardVotes, ...customVotes]; votes.sort((a, b) => a.position - b.position); diff --git a/support/extract-utils.js b/support/extract-utils.js index 3038080..b840857 100644 --- a/support/extract-utils.js +++ b/support/extract-utils.js @@ -109,13 +109,19 @@ export async function buildExtractDataForTreatment( participantCache = buildParticipantCache(participationList); } } - const standardVotes = await StandardVote.findAll({ treatmentUri: treatment.uri }); + const standardVotes = await StandardVote.findAll({ + treatmentUri: treatment.uri + }); const customVotes = await CustomVote.findAll({ treatmentUri: treatment.uri }); const votes = [...standardVotes, ...customVotes]; - votes.sort((a, b) => a.position -b.position); + votes.sort((a, b) => a.position - b.position); if (participationList && participationList.present.length > 0) { // only try fetching voters if people were present - await Promise.all(votes.map((vote) => vote.type === 'customVote' ? vote : vote.fetchVoters(participantCache))); + await Promise.all( + votes.map((vote) => + vote.type === 'customVote' ? vote : vote.fetchVoters(participantCache) + ) + ); } let content; if (isPublic) { From 6570e696b3c0c40600e4112dda88b72674e9edfb Mon Sep 17 00:00:00 2001 From: Oscar Date: Thu, 17 Oct 2024 10:50:08 +0200 Subject: [PATCH 3/5] linting 2 --- support/besluit-exporter.js | 4 ++-- support/extract-utils.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/support/besluit-exporter.js b/support/besluit-exporter.js index d979f59..3a5b3b8 100644 --- a/support/besluit-exporter.js +++ b/support/besluit-exporter.js @@ -41,8 +41,8 @@ async function addDecisionsToTreatment(treatment) { } async function addVotesToTreatment(treatment) { - const standardVotes = await StandardVote.findAll({ - treatmentUri: treatment.uri + const standardVotes = await StandardVote.findAll({ + treatmentUri: treatment.uri, }); const customVotes = await CustomVote.findAll({ treatmentUri: treatment.uri }); const votes = [...standardVotes, ...customVotes]; diff --git a/support/extract-utils.js b/support/extract-utils.js index b840857..858fff7 100644 --- a/support/extract-utils.js +++ b/support/extract-utils.js @@ -109,8 +109,8 @@ export async function buildExtractDataForTreatment( participantCache = buildParticipantCache(participationList); } } - const standardVotes = await StandardVote.findAll({ - treatmentUri: treatment.uri + const standardVotes = await StandardVote.findAll({ + treatmentUri: treatment.uri, }); const customVotes = await CustomVote.findAll({ treatmentUri: treatment.uri }); const votes = [...standardVotes, ...customVotes]; @@ -118,7 +118,7 @@ export async function buildExtractDataForTreatment( if (participationList && participationList.present.length > 0) { // only try fetching voters if people were present await Promise.all( - votes.map((vote) => + votes.map((vote) => vote.type === 'customVote' ? vote : vote.fetchVoters(participantCache) ) ); From 2822d49b92cabb8b0d133d73c6f31749a161fa49 Mon Sep 17 00:00:00 2001 From: Oscar Date: Wed, 23 Oct 2024 10:52:49 +0200 Subject: [PATCH 4/5] use custom helper instead of isCustomVote... --- models/custom-vote.js | 1 - models/standard-vote.js | 1 - support/setup-handlebars.js | 4 ++++ support/templates/partials/treatment.hbs | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/models/custom-vote.js b/models/custom-vote.js index 495ace4..d921ded 100644 --- a/models/custom-vote.js +++ b/models/custom-vote.js @@ -42,7 +42,6 @@ export default class CustomVote { constructor({ uri, position, content, context }) { this.type = 'customVote'; - this.isCustomVote = true; this.uri = uri; this.position = position; this.content = content; diff --git a/models/standard-vote.js b/models/standard-vote.js index a614a76..0b6cc71 100644 --- a/models/standard-vote.js +++ b/models/standard-vote.js @@ -80,7 +80,6 @@ export default class StandardVote { adminBodyClassification, }) { this.type = 'standardVote'; - this.isStandardVote = true; this.uri = uri; this.subject = subject; this.result = result; diff --git a/support/setup-handlebars.js b/support/setup-handlebars.js index 8c11570..04066ac 100644 --- a/support/setup-handlebars.js +++ b/support/setup-handlebars.js @@ -48,4 +48,8 @@ function registerHelpers() { Handlebars.registerHelper('inc', (value) => { return parseInt(value) + 1; }); + + Handlebars.registerHelper('eq', (variable, value) => { + return variable === value; + }); } diff --git a/support/templates/partials/treatment.hbs b/support/templates/partials/treatment.hbs index daec217..985d51e 100644 --- a/support/templates/partials/treatment.hbs +++ b/support/templates/partials/treatment.hbs @@ -45,7 +45,7 @@

Stemmingen bij agendapunt

{{#each this.votes}} - {{#if this.isStandardVote}} + {{#if (eq this.type "standardVote")}}

{{#if this.isSecret}} @@ -91,7 +91,7 @@

{{/if}} - {{#if this.isCustomVote}} + {{#if (eq this.type "customVote")}}
{{{this.content}}}
From b941d8d3b5aa16d06c1e3f976ef4a984277da5b4 Mon Sep 17 00:00:00 2001 From: Oscar Date: Tue, 29 Oct 2024 11:21:19 +0100 Subject: [PATCH 5/5] new model --- models/custom-vote.js | 2 +- support/templates/partials/treatment.hbs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/custom-vote.js b/models/custom-vote.js index d921ded..83debdd 100644 --- a/models/custom-vote.js +++ b/models/custom-vote.js @@ -13,7 +13,7 @@ export default class CustomVote { ${prefixMap.get('gn').toSparqlString()} SELECT DISTINCT * WHERE { ${sparqlEscapeUri(treatmentUri)} a besluit:BehandelingVanAgendapunt; - besluit:heeftStemming ?uri. + gn:heeftAangepasteStemming ?uri. ?uri a gn:AangepasteStemming; schema:position ?position; ext:votingDocument ?documentContainer. diff --git a/support/templates/partials/treatment.hbs b/support/templates/partials/treatment.hbs index 985d51e..d290bf0 100644 --- a/support/templates/partials/treatment.hbs +++ b/support/templates/partials/treatment.hbs @@ -46,7 +46,7 @@
{{#each this.votes}} {{#if (eq this.type "standardVote")}} -
+

{{#if this.isSecret}} {{this.whoVotesPhrase}} geheim @@ -92,7 +92,7 @@

{{/if}} {{#if (eq this.type "customVote")}} -
+
{{{this.content}}}
{{/if}}